mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-10 17:53:06 +00:00
Optimize ecMul precompile (scalar multiplication on BN254) (#852)
* windowed mul * Working * Window of 4 bits * Fix * Comments * Unroll loop * Unroll loop * remove global * Minor * Minor * Implement `CALLVALUE, CALLDATALOAD, CALLDATASIZE, CALLDATACOPY` in interpreter * Minor * Doesn't work * Minor * Minor * wnaf msm * Working hardcoded values: 28657 opcodes * Working wnaf * Small wnaf optim * Precompute works * Working together * Bump to 129 bits * Working glv decomposition * Working MSM with GLV * Almost working * Working * ECC test folder * Working with real sig data * Fix tests + Clippy * Minor * Cleaning * Comments * Cleaning * Smaller glv test file * Print opcode count at the end of interpreter run * More constants * Add z3 proof that the GLV scalars are 129-bit or less * Minor change to z3 proof * Move files and renaming fns * Testing * Fix BN GLV * BN precompute table * Working precompute * Working bn tests * Working * Minor * Minor * Use MULFP254 * Minor * Merge conflicts * Remove unused asm file * ECC fns renaming (#874) * PR feedback
This commit is contained in:
parent
3332fbb483
commit
ac40bd5f5d
@ -27,6 +27,9 @@ pub(crate) fn combined_kernel() -> Kernel {
|
||||
include_str!("asm/curve/bn254/curve_add.asm"),
|
||||
include_str!("asm/curve/bn254/curve_mul.asm"),
|
||||
include_str!("asm/curve/bn254/moddiv.asm"),
|
||||
include_str!("asm/curve/bn254/glv.asm"),
|
||||
include_str!("asm/curve/bn254/msm.asm"),
|
||||
include_str!("asm/curve/bn254/precomputation.asm"),
|
||||
include_str!("asm/curve/common.asm"),
|
||||
include_str!("asm/curve/secp256k1/curve_add.asm"),
|
||||
include_str!("asm/curve/secp256k1/ecrecover.asm"),
|
||||
@ -35,6 +38,7 @@ pub(crate) fn combined_kernel() -> Kernel {
|
||||
include_str!("asm/curve/secp256k1/moddiv.asm"),
|
||||
include_str!("asm/curve/secp256k1/glv.asm"),
|
||||
include_str!("asm/curve/secp256k1/precomputation.asm"),
|
||||
include_str!("asm/curve/wnaf.asm"),
|
||||
include_str!("asm/exp.asm"),
|
||||
include_str!("asm/fields/fp6_macros.asm"),
|
||||
include_str!("asm/fields/fp6_mul.asm"),
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
// BN254 elliptic curve addition.
|
||||
// Uses the standard affine addition formula.
|
||||
global ec_add:
|
||||
global bn_add:
|
||||
// Uncomment for test inputs.
|
||||
// PUSH 0xdeadbeef
|
||||
// PUSH 2
|
||||
@ -16,27 +16,27 @@ global ec_add:
|
||||
// stack: y0, x0, y0, x1, y1, retdest
|
||||
DUP2
|
||||
// stack: x0, y0, x0, y0, x1, y1, retdest
|
||||
%ec_check
|
||||
%bn_check
|
||||
// stack: isValid(x0, y0), x0, y0, x1, y1, retdest
|
||||
DUP5
|
||||
// stack: x1, isValid(x0, y0), x0, y0, x1, y1, retdest
|
||||
DUP5
|
||||
// stack: x1, y1, isValid(x0, y0), x0, y0, x1, y1, retdest
|
||||
%ec_check
|
||||
%bn_check
|
||||
// stack: isValid(x1, y1), isValid(x0, y0), x0, y0, x1, y1, retdest
|
||||
AND
|
||||
// stack: isValid(x1, y1) & isValid(x0, y0), x0, y0, x1, y1, retdest
|
||||
%jumpi(ec_add_valid_points)
|
||||
%jumpi(bn_add_valid_points)
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Otherwise return
|
||||
%pop4
|
||||
// stack: retdest
|
||||
%ec_invalid_input
|
||||
%bn_invalid_input
|
||||
|
||||
// BN254 elliptic curve addition.
|
||||
// Assumption: (x0,y0) and (x1,y1) are valid points.
|
||||
global ec_add_valid_points:
|
||||
global bn_add_valid_points:
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Check if the first point is the identity.
|
||||
@ -46,17 +46,17 @@ global ec_add_valid_points:
|
||||
// stack: x0, y0, x0, y0, x1, y1, retdest
|
||||
%ec_isidentity
|
||||
// stack: (x0,y0)==(0,0), x0, y0, x1, y1, retdest
|
||||
%jumpi(ec_add_first_zero)
|
||||
%jumpi(bn_add_first_zero)
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Check if the first point is the identity.
|
||||
// Check if the second point is the identity.
|
||||
DUP4
|
||||
// stack: y1, x0, y0, x1, y1, retdest
|
||||
DUP4
|
||||
// stack: x1, y1, x0, y0, x1, y1, retdest
|
||||
%ec_isidentity
|
||||
// stack: (x1,y1)==(0,0), x0, y0, x1, y1, retdest
|
||||
%jumpi(ec_add_snd_zero)
|
||||
%jumpi(bn_add_snd_zero)
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Check if both points have the same x-coordinate.
|
||||
@ -66,7 +66,7 @@ global ec_add_valid_points:
|
||||
// stack: x0, x1, x0, y0, x1, y1, retdest
|
||||
EQ
|
||||
// stack: x0 == x1, x0, y0, x1, y1, retdest
|
||||
%jumpi(ec_add_equal_first_coord)
|
||||
%jumpi(bn_add_equal_first_coord)
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Otherwise, we can use the standard formula.
|
||||
@ -85,11 +85,11 @@ global ec_add_valid_points:
|
||||
// stack: x0 - x1, y0 - y1, x0, y0, x1, y1, retdest
|
||||
%moddiv
|
||||
// stack: lambda, x0, y0, x1, y1, retdest
|
||||
%jump(ec_add_valid_points_with_lambda)
|
||||
%jump(bn_add_valid_points_with_lambda)
|
||||
|
||||
// BN254 elliptic curve addition.
|
||||
// Assumption: (x0,y0) == (0,0)
|
||||
ec_add_first_zero:
|
||||
bn_add_first_zero:
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
// Just return (x1,y1)
|
||||
%stack (x0, y0, x1, y1, retdest) -> (retdest, x1, y1)
|
||||
@ -97,7 +97,7 @@ ec_add_first_zero:
|
||||
|
||||
// BN254 elliptic curve addition.
|
||||
// Assumption: (x1,y1) == (0,0)
|
||||
ec_add_snd_zero:
|
||||
bn_add_snd_zero:
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Just return (x0,y0)
|
||||
@ -106,7 +106,7 @@ ec_add_snd_zero:
|
||||
|
||||
// BN254 elliptic curve addition.
|
||||
// Assumption: lambda = (y0 - y1)/(x0 - x1)
|
||||
ec_add_valid_points_with_lambda:
|
||||
bn_add_valid_points_with_lambda:
|
||||
// stack: lambda, x0, y0, x1, y1, retdest
|
||||
|
||||
// Compute x2 = lambda^2 - x1 - x0
|
||||
@ -153,7 +153,7 @@ ec_add_valid_points_with_lambda:
|
||||
|
||||
// BN254 elliptic curve addition.
|
||||
// Assumption: (x0,y0) and (x1,y1) are valid points and x0 == x1
|
||||
ec_add_equal_first_coord:
|
||||
bn_add_equal_first_coord:
|
||||
// stack: x0, y0, x1, y1, retdest with x0 == x1
|
||||
|
||||
// Check if the points are equal
|
||||
@ -163,7 +163,7 @@ ec_add_equal_first_coord:
|
||||
// stack: y1, y0, x0, y0, x1, y1, retdest
|
||||
EQ
|
||||
// stack: y1 == y0, x0, y0, x1, y1, retdest
|
||||
%jumpi(ec_add_equal_points)
|
||||
%jumpi(bn_add_equal_points)
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Otherwise, one is the negation of the other so we can return (0,0).
|
||||
@ -181,7 +181,7 @@ ec_add_equal_first_coord:
|
||||
// BN254 elliptic curve addition.
|
||||
// Assumption: x0 == x1 and y0 == y1
|
||||
// Standard doubling formula.
|
||||
ec_add_equal_points:
|
||||
bn_add_equal_points:
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Compute lambda = 3/2 * x0^2 / y0
|
||||
@ -203,18 +203,19 @@ ec_add_equal_points:
|
||||
// stack: y0, 3/2 * x0^2, x0, y0, x1, y1, retdest
|
||||
%moddiv
|
||||
// stack: lambda, x0, y0, x1, y1, retdest
|
||||
%jump(ec_add_valid_points_with_lambda)
|
||||
%jump(bn_add_valid_points_with_lambda)
|
||||
|
||||
// BN254 elliptic curve doubling.
|
||||
// Assumption: (x0,y0) is a valid point.
|
||||
// Standard doubling formula.
|
||||
global ec_double:
|
||||
// stack: x0, y0, retdest
|
||||
DUP2
|
||||
// stack: y0, x0, y0, retdest
|
||||
DUP2
|
||||
// stack: x0, y0, x0, y0, retdest
|
||||
%jump(ec_add_equal_points)
|
||||
global bn_double:
|
||||
// stack: x, y, retdest
|
||||
DUP2 DUP2 %ec_isidentity
|
||||
// stack: (x,y)==(0,0), x, y, retdest
|
||||
%jumpi(ec_double_retself)
|
||||
DUP2 DUP2
|
||||
// stack: x, y, x, y, retdest
|
||||
%jump(bn_add_equal_points)
|
||||
|
||||
// Push the order of the BN254 base field.
|
||||
%macro bn_base
|
||||
@ -241,7 +242,7 @@ global ec_double:
|
||||
|
||||
// Check if (x,y) is a valid curve point.
|
||||
// Puts y^2 % N == (x^3 + 3) % N & (x < N) & (y < N) || (x,y)==(0,0) on top of the stack.
|
||||
%macro ec_check
|
||||
%macro bn_check
|
||||
// stack: x, y
|
||||
%bn_base
|
||||
// stack: N, x, y
|
||||
@ -291,17 +292,8 @@ global ec_double:
|
||||
// stack: y^2 % N == (x^3 + 3) % N & (x < N) & (y < N) || (x,y)==(0,0)
|
||||
%endmacro
|
||||
|
||||
// Check if (x,y)==(0,0)
|
||||
%macro ec_isidentity
|
||||
// stack: x, y
|
||||
OR
|
||||
// stack: x | y
|
||||
ISZERO
|
||||
// stack: (x,y) == (0,0)
|
||||
%endmacro
|
||||
|
||||
// Return (u256::MAX, u256::MAX) which is used to indicate the input was invalid.
|
||||
%macro ec_invalid_input
|
||||
%macro bn_invalid_input
|
||||
// stack: retdest
|
||||
PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
// stack: u256::MAX, retdest
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
// BN254 elliptic curve scalar multiplication.
|
||||
// Recursive implementation, same algorithm as in `exp.asm`.
|
||||
global ec_mul:
|
||||
// Uncomment for test inputs.
|
||||
// PUSH 0xdeadbeef
|
||||
// PUSH 0xd
|
||||
// PUSH 2
|
||||
// PUSH 1
|
||||
// Uses GLV, wNAF with w=5, and a MSM algorithm.
|
||||
global bn_mul:
|
||||
// stack: x, y, s, retdest
|
||||
DUP2
|
||||
// stack: y, x, y, s, retdest
|
||||
@ -19,77 +14,28 @@ global ec_mul:
|
||||
// stack: y, x, y, s, retdest
|
||||
DUP2
|
||||
// stack: x, y, x, y, s, retdest
|
||||
%ec_check
|
||||
%bn_check
|
||||
// stack: isValid(x, y), x, y, s, retdest
|
||||
%jumpi(ec_mul_valid_point)
|
||||
%jumpi(bn_mul_valid_point)
|
||||
// stack: x, y, s, retdest
|
||||
%pop3
|
||||
%ec_invalid_input
|
||||
%bn_invalid_input
|
||||
|
||||
// Same algorithm as in `exp.asm`
|
||||
ec_mul_valid_point:
|
||||
// stack: x, y, s, retdest
|
||||
DUP3
|
||||
// stack: s, x, y, s, retdest
|
||||
%jumpi(step_case)
|
||||
// stack: x, y, s, retdest
|
||||
%jump(ret_zero_ec_mul)
|
||||
|
||||
step_case:
|
||||
// stack: x, y, s, retdest
|
||||
PUSH recursion_return
|
||||
// stack: recursion_return, x, y, s, retdest
|
||||
PUSH 2
|
||||
// stack: 2, recursion_return, x, y, s, retdest
|
||||
DUP5
|
||||
// stack: s, 2, recursion_return, x, y, s, retdest
|
||||
DIV
|
||||
// stack: s / 2, recursion_return, x, y, s, retdest
|
||||
PUSH step_case_contd
|
||||
// stack: step_case_contd, s / 2, recursion_return, x, y, s, retdest
|
||||
DUP5
|
||||
// stack: y, step_case_contd, s / 2, recursion_return, x, y, s, retdest
|
||||
DUP5
|
||||
// stack: x, y, step_case_contd, s / 2, recursion_return, x, y, s, retdest
|
||||
%jump(ec_double)
|
||||
|
||||
// Assumption: 2(x,y) = (x',y')
|
||||
step_case_contd:
|
||||
// stack: x', y', s / 2, recursion_return, x, y, s, retdest
|
||||
%jump(ec_mul_valid_point)
|
||||
|
||||
recursion_return:
|
||||
// stack: x', y', x, y, s, retdest
|
||||
SWAP4
|
||||
// stack: s, y', x, y, x', retdest
|
||||
PUSH 1
|
||||
// stack: 1, s, y', x, y, x', retdest
|
||||
AND
|
||||
// stack: s & 1, y', x, y, x', retdest
|
||||
SWAP1
|
||||
// stack: y', s & 1, x, y, x', retdest
|
||||
SWAP2
|
||||
// stack: x, s & 1, y', y, x', retdest
|
||||
SWAP3
|
||||
// stack: y, s & 1, y', x, x', retdest
|
||||
SWAP4
|
||||
// stack: x', s & 1, y', x, y, retdest
|
||||
SWAP1
|
||||
// stack: s & 1, x', y', x, y, retdest
|
||||
%jumpi(odd_scalar)
|
||||
// stack: x', y', x, y, retdest
|
||||
SWAP3
|
||||
// stack: y, y', x, x', retdest
|
||||
POP
|
||||
// stack: y', x, x', retdest
|
||||
SWAP1
|
||||
// stack: x, y', x', retdest
|
||||
POP
|
||||
// stack: y', x', retdest
|
||||
SWAP2
|
||||
// stack: retdest, x', y'
|
||||
bn_mul_valid_point:
|
||||
%stack (x, y, s, retdest) -> (s, bn_mul_after_glv, x, y, bn_msm, bn_mul_end, retdest)
|
||||
%jump(bn_glv_decompose)
|
||||
bn_mul_after_glv:
|
||||
// stack: bneg, a, b, x, y, bn_msm, bn_mul_end, retdest
|
||||
// Store bneg at this (otherwise unused) location. Will be used later in the MSM.
|
||||
%mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q, @BN_BNEG_LOC)
|
||||
// stack: a, b, x, y, bn_msm, bn_mul_end, retdest
|
||||
PUSH bn_mul_after_a SWAP1 PUSH @SEGMENT_KERNEL_BN_WNAF_A PUSH @BN_SCALAR %jump(wnaf)
|
||||
bn_mul_after_a:
|
||||
// stack: b, x, y, bn_msm, bn_mul_end, retdest
|
||||
PUSH bn_mul_after_b SWAP1 PUSH @SEGMENT_KERNEL_BN_WNAF_B PUSH @BN_SCALAR %jump(wnaf)
|
||||
bn_mul_after_b:
|
||||
// stack: x, y, bn_msm, bn_mul_end, retdest
|
||||
%jump(bn_precompute_table)
|
||||
bn_mul_end:
|
||||
%stack (Ax, Ay, retdest) -> (retdest, Ax, Ay)
|
||||
JUMP
|
||||
|
||||
odd_scalar:
|
||||
// stack: x', y', x, y, retdest
|
||||
%jump(ec_add_valid_points)
|
||||
|
||||
97
evm/src/cpu/kernel/asm/curve/bn254/glv.asm
Normal file
97
evm/src/cpu/kernel/asm/curve/bn254/glv.asm
Normal file
@ -0,0 +1,97 @@
|
||||
// Inspired by https://github.com/AztecProtocol/weierstrudel/blob/master/huff_modules/endomorphism.huff
|
||||
// See also Sage code in evm/src/cpu/kernel/tests/ecc/bn_glv_test_data
|
||||
// Given scalar `k ∈ Bn254::ScalarField`, return `u, k1, k2` with `k1,k2 < 2^127` and such that
|
||||
// `k = k1 - s*k2` if `u==0` otherwise `k = k1 + s*k2`, where `s` is the scalar value representing the endomorphism.
|
||||
// In the comments below, N means @BN_SCALAR
|
||||
//
|
||||
// Z3 proof that the resulting `k1, k2` satisfy `k1>0`, `k1 < 2^127` and `|k2| < 2^127`.
|
||||
// ```python
|
||||
// from z3 import Solver, Int, Or, unsat
|
||||
// q = 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001
|
||||
// glv_s = 0xB3C4D79D41A917585BFC41088D8DAAA78B17EA66B99C90DD
|
||||
//
|
||||
// b2 = 0x89D3256894D213E3
|
||||
// b1 = -0x6F4D8248EEB859FC8211BBEB7D4F1128
|
||||
//
|
||||
// g1 = 0x24CCEF014A773D2CF7A7BD9D4391EB18D
|
||||
// g2 = 0x2D91D232EC7E0B3D7
|
||||
// k = Int("k")
|
||||
// c1 = Int("c1")
|
||||
// c2 = Int("c2")
|
||||
// s = Solver()
|
||||
//
|
||||
// c2p = -c2
|
||||
// s.add(k < q)
|
||||
// s.add(0 < k)
|
||||
// s.add(c1 * (2**256) <= g2 * k)
|
||||
// s.add((c1 + 1) * (2**256) > g2 * k)
|
||||
// s.add(c2p * (2**256) <= g1 * k)
|
||||
// s.add((c2p + 1) * (2**256) > g1 * k)
|
||||
//
|
||||
// q1 = c1 * b1
|
||||
// q2 = c2 * b2
|
||||
//
|
||||
// k2 = q2 - q1
|
||||
// k2L = (glv_s * k2) % q
|
||||
// k1 = k - k2L
|
||||
// k2 = -k2
|
||||
//
|
||||
// s.add(Or((k2 >= 2**127), (-k2 >= 2**127), (k1 >= 2**127), (k1 < 0)))
|
||||
//
|
||||
// assert s.check() == unsat
|
||||
// ```
|
||||
global bn_glv_decompose:
|
||||
// stack: k, retdest
|
||||
PUSH @BN_SCALAR DUP1 DUP1
|
||||
// Compute c2 which is the top 256 bits of k*g1. Use asm from https://medium.com/wicketh/mathemagic-full-multiply-27650fec525d.
|
||||
PUSH @U256_MAX
|
||||
// stack: -1, N, N, N, k, retdest
|
||||
PUSH @BN_GLV_MINUS_G1 DUP6
|
||||
// stack: k, g1, -1, N, N, N, k, retdest
|
||||
MULMOD
|
||||
// stack: (k * g1 % -1), N, N, N, k, retdest
|
||||
PUSH @BN_GLV_MINUS_G1 DUP6
|
||||
// stack: k, g1, (k * g1 % -1), N, N, N, k, retdest
|
||||
MUL
|
||||
// stack: bottom = (k * g1), (k * g1 % -1), N, N, N, k, retdest
|
||||
DUP1 DUP3
|
||||
// stack: (k * g1 % -1), bottom, bottom, (k * g1 % -1), N, N, N, k, retdest
|
||||
LT SWAP2 SUB SUB
|
||||
// stack: c2, N, N, N, k, retdest
|
||||
PUSH @BN_GLV_B2 MULMOD
|
||||
// stack: q2=c2*b2, N, N, k, retdest
|
||||
|
||||
// Use the same trick to compute c1 = top 256 bits of g2*k.
|
||||
PUSH @BN_SCALAR PUSH @U256_MAX
|
||||
PUSH @BN_GLV_G2 DUP7 MULMOD
|
||||
PUSH @BN_GLV_G2 DUP7 MUL
|
||||
DUP1 DUP3 LT
|
||||
SWAP2 SUB SUB
|
||||
// stack: c1, N, q2, N, N, k, retdest
|
||||
PUSH @BN_GLV_B1 MULMOD
|
||||
// stack: q1, q2, N, N, k, retdest
|
||||
|
||||
// We compute k2 = q1 + q2 - N, but we check for underflow and return N-q1-q2 instead if there is one,
|
||||
// along with a flag `underflow` set to 1 if there is an underflow, 0 otherwise.
|
||||
ADD %sub_check_underflow
|
||||
// stack: k2, underflow, N, k, retdest
|
||||
SWAP3 PUSH @BN_SCALAR DUP5 PUSH @BN_GLV_S
|
||||
// stack: s, k2, N, k, underflow, N, k2, retdest
|
||||
MULMOD
|
||||
// stack: s*k2, k, underflow, N, k2, retdest
|
||||
// Need to return `k + s*k2` if no underflow occur, otherwise return `k - s*k2` which is done in the `underflowed` fn.
|
||||
SWAP2 DUP1 %jumpi(underflowed)
|
||||
%stack (underflow, k, x, N, k2) -> (k, x, N, k2, underflow)
|
||||
ADDMOD
|
||||
%stack (k1, k2, underflow, retdest) -> (retdest, underflow, k1, k2)
|
||||
JUMP
|
||||
|
||||
underflowed:
|
||||
// stack: underflow, k, s*k2, N, k2
|
||||
// Compute (k-s*k2)%N. TODO: Use SUBMOD here when ready
|
||||
%stack (u, k, x, N, k2) -> (N, x, k, N, k2, u)
|
||||
SUB ADDMOD
|
||||
%stack (k1, k2, underflow, retdest) -> (retdest, underflow, k1, k2)
|
||||
JUMP
|
||||
|
||||
|
||||
73
evm/src/cpu/kernel/asm/curve/bn254/msm.asm
Normal file
73
evm/src/cpu/kernel/asm/curve/bn254/msm.asm
Normal file
@ -0,0 +1,73 @@
|
||||
// Computes the multiplication `a*G` using a standard MSM with the GLV decomposition of `a`.
|
||||
// see there for a detailed description.
|
||||
global bn_msm:
|
||||
// stack: retdest
|
||||
PUSH 0 PUSH 0 PUSH 0
|
||||
global bn_msm_loop:
|
||||
// stack: accx, accy, i, retdest
|
||||
DUP3 %bn_mload_wnaf_a
|
||||
// stack: w, accx, accy, i, retdest
|
||||
DUP1 %jumpi(bn_msm_loop_add_a_nonzero)
|
||||
POP
|
||||
msm_loop_add_b:
|
||||
//stack: accx, accy, i, retdest
|
||||
DUP3 %bn_mload_wnaf_b
|
||||
// stack: w, accx, accy, i, retdest
|
||||
DUP1 %jumpi(bn_msm_loop_add_b_nonzero)
|
||||
POP
|
||||
msm_loop_contd:
|
||||
%stack (accx, accy, i, retdest) -> (i, i, accx, accy, retdest)
|
||||
// TODO: the GLV scalars for the BN curve are 127-bit, so could use 127 here. But this would require modifying `wnaf.asm`. Not sure it's worth it...
|
||||
%eq_const(129) %jumpi(msm_end)
|
||||
%increment
|
||||
//stack: i+1, accx, accy, retdest
|
||||
%stack (i, accx, accy, retdest) -> (accx, accy, bn_msm_loop, i, retdest)
|
||||
%jump(bn_double)
|
||||
|
||||
msm_end:
|
||||
%stack (i, accx, accy, retdest) -> (retdest, accx, accy)
|
||||
JUMP
|
||||
|
||||
bn_msm_loop_add_a_nonzero:
|
||||
%stack (w, accx, accy, i, retdest) -> (w, accx, accy, msm_loop_add_b, i, retdest)
|
||||
%bn_mload_point_a
|
||||
// stack: px, py, accx, accy, msm_loop_add_b, i, retdest
|
||||
%jump(bn_add_valid_points)
|
||||
|
||||
bn_msm_loop_add_b_nonzero:
|
||||
%stack (w, accx, accy, i, retdest) -> (w, accx, accy, msm_loop_contd, i, retdest)
|
||||
%bn_mload_point_b
|
||||
// stack: px, py, accx, accy, msm_loop_contd, i, retdest
|
||||
%jump(bn_add_valid_points)
|
||||
|
||||
%macro bn_mload_wnaf_a
|
||||
// stack: i
|
||||
%mload_kernel(@SEGMENT_KERNEL_BN_WNAF_A)
|
||||
%endmacro
|
||||
|
||||
%macro bn_mload_wnaf_b
|
||||
// stack: i
|
||||
%mload_kernel(@SEGMENT_KERNEL_BN_WNAF_B)
|
||||
%endmacro
|
||||
|
||||
%macro bn_mload_point_a
|
||||
// stack: w
|
||||
DUP1
|
||||
%mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q)
|
||||
//stack: Gy, w
|
||||
SWAP1 %decrement %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q)
|
||||
//stack: Gx, Gy
|
||||
%endmacro
|
||||
|
||||
%macro bn_mload_point_b
|
||||
// stack: w
|
||||
DUP1
|
||||
%mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q)
|
||||
PUSH @BN_BNEG_LOC %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q)
|
||||
%stack (bneg, Gy, w) -> (@BN_BASE, Gy, bneg, bneg, Gy, w)
|
||||
SUB SWAP1 ISZERO MUL SWAP2 MUL ADD
|
||||
SWAP1 %decrement %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q)
|
||||
//stack: Gx, Gy
|
||||
PUSH @BN_GLV_BETA
|
||||
MULFP254
|
||||
%endmacro
|
||||
35
evm/src/cpu/kernel/asm/curve/bn254/precomputation.asm
Normal file
35
evm/src/cpu/kernel/asm/curve/bn254/precomputation.asm
Normal file
@ -0,0 +1,35 @@
|
||||
// Precompute a table of multiples of the BN254 point `Q = (Qx, Qy)`.
|
||||
// Let `(Qxi, Qyi) = i * Q`, then store in the `SEGMENT_KERNEL_BN_TABLE_Q` segment of memory the values
|
||||
// `i-1 => Qxi`, `i => Qyi if i < 16 else -Qy(32-i)` for `i in range(1, 32, 2)`.
|
||||
global bn_precompute_table:
|
||||
// stack: Qx, Qy, retdest
|
||||
PUSH precompute_table_contd DUP3 DUP3
|
||||
%jump(bn_double)
|
||||
precompute_table_contd:
|
||||
// stack: Qx2, Qy2, Qx, Qy, retdest
|
||||
PUSH 1
|
||||
bn_precompute_table_loop:
|
||||
// stack i, Qx2, Qy2, Qx, Qy, retdest
|
||||
PUSH 1 DUP2 SUB
|
||||
%stack (im, i, Qx2, Qy2, Qx, Qy, retdest) -> (i, Qy, im, Qx, i, Qx2, Qy2, Qx, Qy, retdest)
|
||||
%mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q)
|
||||
// stack: i, Qx2, Qy2, Qx, Qy, retdest
|
||||
DUP1 PUSH 32 SUB PUSH 1 DUP2 SUB
|
||||
// stack: 31-i, 32-i, i, Qx2, Qy2, Qx, Qy, retdest
|
||||
DUP7 PUSH @BN_BASE SUB
|
||||
// TODO: Could maybe avoid storing Qx a second time here, not sure if it would be more efficient.
|
||||
%stack (Qyy, iii, ii, i, Qx2, Qy2, Qx, Qy, retdest) -> (iii, Qx, ii, Qyy, i, Qx2, Qy2, Qx, Qy, retdest)
|
||||
%mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q)
|
||||
// stack: i, Qx2, Qy2, Qx, Qy, retdest
|
||||
PUSH 2 ADD
|
||||
// stack: i+2, Qx2, Qy2, Qx, Qy, retdest
|
||||
DUP1 PUSH 16 LT %jumpi(precompute_table_end)
|
||||
%stack (i, Qx2, Qy2, Qx, Qy, retdest) -> (Qx, Qy, Qx2, Qy2, precompute_table_loop_contd, i, Qx2, Qy2, retdest)
|
||||
%jump(bn_add_valid_points)
|
||||
precompute_table_loop_contd:
|
||||
%stack (Qx, Qy, i, Qx2, Qy2, retdest) -> (i, Qx2, Qy2, Qx, Qy, retdest)
|
||||
%jump(bn_precompute_table_loop)
|
||||
|
||||
precompute_table_end:
|
||||
// stack: i, Qx2, Qy2, Qx, Qy, retdest
|
||||
%pop5 JUMP
|
||||
@ -9,3 +9,17 @@ global ret_zero_ec_mul:
|
||||
SWAP2
|
||||
// stack: retdest, 0, 0
|
||||
JUMP
|
||||
|
||||
global ec_double_retself:
|
||||
%stack (x, y, retdest) -> (retdest, x, y)
|
||||
JUMP
|
||||
|
||||
// Check if (x,y)==(0,0)
|
||||
%macro ec_isidentity
|
||||
// stack: x, y
|
||||
OR
|
||||
// stack: x | y
|
||||
ISZERO
|
||||
// stack: (x,y) == (0,0)
|
||||
%endmacro
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
// Secp256k1 elliptic curve addition.
|
||||
// Assumption: (x0,y0) and (x1,y1) are valid points.
|
||||
global ec_add_valid_points_secp:
|
||||
global secp_add_valid_points:
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Check if the first point is the identity.
|
||||
@ -12,7 +12,7 @@ global ec_add_valid_points_secp:
|
||||
// stack: x0, y0, x0, y0, x1, y1, retdest
|
||||
%ec_isidentity
|
||||
// stack: (x0,y0)==(0,0), x0, y0, x1, y1, retdest
|
||||
%jumpi(ec_add_first_zero)
|
||||
%jumpi(secp_add_first_zero)
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Check if the second point is the identity.
|
||||
@ -22,7 +22,7 @@ global ec_add_valid_points_secp:
|
||||
// stack: x1, y1, x0, y0, x1, y1, retdest
|
||||
%ec_isidentity
|
||||
// stack: (x1,y1)==(0,0), x0, y0, x1, y1, retdest
|
||||
%jumpi(ec_add_snd_zero)
|
||||
%jumpi(secp_add_snd_zero)
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Check if both points have the same x-coordinate.
|
||||
@ -32,9 +32,9 @@ global ec_add_valid_points_secp:
|
||||
// stack: x0, x1, x0, y0, x1, y1, retdest
|
||||
EQ
|
||||
// stack: x0 == x1, x0, y0, x1, y1, retdest
|
||||
%jumpi(ec_add_equal_first_coord)
|
||||
%jumpi(secp_add_equal_first_coord)
|
||||
// Standard affine addition formula.
|
||||
global ec_add_valid_points_no_edge_case_secp:
|
||||
global secp_add_valid_points_no_edge_case:
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
// Compute lambda = (y0 - y1)/(x0 - x1)
|
||||
DUP4
|
||||
@ -51,11 +51,11 @@ global ec_add_valid_points_no_edge_case_secp:
|
||||
// stack: x0 - x1, y0 - y1, x0, y0, x1, y1, retdest
|
||||
%moddiv_secp_base
|
||||
// stack: lambda, x0, y0, x1, y1, retdest
|
||||
%jump(ec_add_valid_points_with_lambda)
|
||||
%jump(secp_add_valid_points_with_lambda)
|
||||
|
||||
// Secp256k1 elliptic curve addition.
|
||||
// Assumption: (x0,y0) == (0,0)
|
||||
ec_add_first_zero:
|
||||
secp_add_first_zero:
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Just return (x1,y1)
|
||||
@ -69,7 +69,7 @@ ec_add_first_zero:
|
||||
|
||||
// Secp256k1 elliptic curve addition.
|
||||
// Assumption: (x1,y1) == (0,0)
|
||||
ec_add_snd_zero:
|
||||
secp_add_snd_zero:
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Just return (x1,y1)
|
||||
@ -89,7 +89,7 @@ ec_add_snd_zero:
|
||||
|
||||
// Secp256k1 elliptic curve addition.
|
||||
// Assumption: lambda = (y0 - y1)/(x0 - x1)
|
||||
ec_add_valid_points_with_lambda:
|
||||
secp_add_valid_points_with_lambda:
|
||||
// stack: lambda, x0, y0, x1, y1, retdest
|
||||
|
||||
// Compute x2 = lambda^2 - x1 - x0
|
||||
@ -145,7 +145,7 @@ ec_add_valid_points_with_lambda:
|
||||
|
||||
// Secp256k1 elliptic curve addition.
|
||||
// Assumption: (x0,y0) and (x1,y1) are valid points and x0 == x1
|
||||
ec_add_equal_first_coord:
|
||||
secp_add_equal_first_coord:
|
||||
// stack: x0, y0, x1, y1, retdest with x0 == x1
|
||||
|
||||
// Check if the points are equal
|
||||
@ -155,7 +155,7 @@ ec_add_equal_first_coord:
|
||||
// stack: y1, y0, x0, y0, x1, y1, retdest
|
||||
EQ
|
||||
// stack: y1 == y0, x0, y0, x1, y1, retdest
|
||||
%jumpi(ec_add_equal_points)
|
||||
%jumpi(secp_add_equal_points)
|
||||
// stack: x0, y0, x1, y1, retdest
|
||||
|
||||
// Otherwise, one is the negation of the other so we can return (0,0).
|
||||
@ -173,7 +173,7 @@ ec_add_equal_first_coord:
|
||||
// Secp256k1 elliptic curve addition.
|
||||
// Assumption: x0 == x1 and y0 == y1
|
||||
// Standard doubling formula.
|
||||
ec_add_equal_points:
|
||||
secp_add_equal_points:
|
||||
// Compute lambda = 3/2 * x0^2 / y0
|
||||
%stack (x0, y0, x1, y1, retdest) -> (x0, x0, @SECP_BASE, @SECP_BASE, x0, y0, x1, y1, retdest)
|
||||
MULMOD
|
||||
@ -181,16 +181,16 @@ ec_add_equal_points:
|
||||
MULMOD
|
||||
DUP3
|
||||
%moddiv_secp_base
|
||||
%jump(ec_add_valid_points_with_lambda)
|
||||
%jump(secp_add_valid_points_with_lambda)
|
||||
|
||||
// Secp256k1 elliptic curve doubling.
|
||||
// Assumption: (x,y) is a valid point.
|
||||
// Standard doubling formula.
|
||||
global ec_double_secp:
|
||||
global secp_double:
|
||||
// stack: x, y, retdest
|
||||
DUP2 DUP2 %ec_isidentity
|
||||
// stack: (x,y)==(0,0), x, y, retdest
|
||||
%jumpi(retself)
|
||||
%jumpi(ec_double_retself)
|
||||
|
||||
// Compute lambda = 3/2 * x0^2 / y0
|
||||
%stack (x, y, retdest) -> (x, x, @SECP_BASE, @SECP_BASE, x, y, retdest)
|
||||
@ -200,11 +200,7 @@ global ec_double_secp:
|
||||
DUP3
|
||||
%moddiv_secp_base
|
||||
%stack (lambda, x, y, retdest) -> (lambda, x, y, x, y, retdest)
|
||||
%jump(ec_add_valid_points_with_lambda)
|
||||
|
||||
retself:
|
||||
%stack (x, y, retdest) -> (retdest, x, y)
|
||||
JUMP
|
||||
%jump(secp_add_valid_points_with_lambda)
|
||||
|
||||
// Push the order of the Secp256k1 scalar field.
|
||||
%macro secp_base
|
||||
@ -221,7 +217,7 @@ retself:
|
||||
|
||||
// Check if (x,y) is a valid curve point.
|
||||
// Puts y^2 % N == (x^3 + 3) % N & (x < N) & (y < N) || (x,y)==(0,0) on top of the stack.
|
||||
%macro ec_check_secp
|
||||
%macro secp_check
|
||||
// stack: x, y
|
||||
%secp_base
|
||||
// stack: N, x, y
|
||||
|
||||
@ -64,13 +64,13 @@ ecrecover_valid_input:
|
||||
// return msm_with_precomputation([a0, a1, b0, b1], [G, phi(G), Q, phi(Q)]) -- phi is the Secp endomorphism.
|
||||
ecdsa_msm_with_glv:
|
||||
%stack (a, b, Qx, Qy, retdest) -> (a, ecdsa_after_glv_a, b, Qx, Qy, retdest)
|
||||
%jump(glv_decompose)
|
||||
%jump(secp_glv_decompose)
|
||||
ecdsa_after_glv_a:
|
||||
%stack (a1neg, a0, a1, b, Qx, Qy, retdest) -> (b, ecdsa_after_glv_b, a1neg, a0, a1, Qx, Qy, retdest)
|
||||
%jump(glv_decompose)
|
||||
%jump(secp_glv_decompose)
|
||||
ecdsa_after_glv_b:
|
||||
%stack (b1neg, b0, b1, a1neg, a0, a1, Qx, Qy, retdest) -> (a1neg, b1neg, Qx, Qy, ecdsa_after_precompute, a0, a1, b0, b1, retdest)
|
||||
%jump(precompute_table)
|
||||
%jump(secp_precompute_table)
|
||||
ecdsa_after_precompute:
|
||||
// stack: a0, a1, b0, b1, retdest
|
||||
PUSH 0 PUSH 0 PUSH 129 // 129 is the bit length of the GLV exponents
|
||||
@ -91,11 +91,11 @@ ecdsa_after_precompute_loop:
|
||||
SWAP1 %mul_const(2)
|
||||
%mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
%stack (Px, Py, i, accx, accy, a0, a1, b0, b1, retdest) -> (Px, Py, accx, accy, ecdsa_after_precompute_loop_contd, i, a0, a1, b0, b1, retdest)
|
||||
%jump(ec_add_valid_points_secp)
|
||||
%jump(secp_add_valid_points)
|
||||
ecdsa_after_precompute_loop_contd:
|
||||
%stack (accx, accy, i, a0, a1, b0, b1, retdest) -> (i, accx, accy, ecdsa_after_precompute_loop_contd2, i, a0, a1, b0, b1, retdest)
|
||||
ISZERO %jumpi(ecdsa_after_precompute_loop_end)
|
||||
%jump(ec_double_secp)
|
||||
%jump(secp_double)
|
||||
ecdsa_after_precompute_loop_contd2:
|
||||
%stack (accx, accy, i, a0, a1, b0, b1, retdest) -> (i, accx, accy, a0, a1, b0, b1, retdest)
|
||||
%decrement %jump(ecdsa_after_precompute_loop)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// Inspired by https://github.com/AztecProtocol/weierstrudel/blob/master/huff_modules/endomorphism.huff
|
||||
// See also Sage code in evm/src/cpu/kernel/tests/ecc/glv_test_data
|
||||
// See also Sage code in evm/src/cpu/kernel/tests/ecc/secp_glv_test_data
|
||||
// Given scalar `k ∈ Secp256k1::ScalarField`, return `u, k1, k2` with `k1,k2 < 2^129` and such that
|
||||
// `k = k1 - s*k2` if `u==0` otherwise `k = k1 + s*k2`, where `s` is the scalar value representing the endomorphism.
|
||||
// In the comments below, N means @SECP_SCALAR
|
||||
@ -36,7 +36,7 @@
|
||||
// s.add(Or((k2 >= 2**129), (-k2 >= 2**129), (k1 >= 2**129), (k1 < 0)))
|
||||
// assert s.check() == unsat
|
||||
// ```
|
||||
global glv_decompose:
|
||||
global secp_glv_decompose:
|
||||
// stack: k, retdest
|
||||
PUSH @SECP_SCALAR DUP1 DUP1
|
||||
// Compute c2 which is the top 256 bits of k*g1. Use asm from https://medium.com/wicketh/mathemagic-full-multiply-27650fec525d.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Initial stack: Gneg, Qneg, Qx, Qy, retdest
|
||||
// Compute a*G ± b*phi(G) + c*Q ± d*phi(Q) for a,b,c,d in {0,1}^4 and store its x-coordinate at location `2*(8a+4b+2c+d)` and its y-coordinate at location `2*(8a+4b+2c+d)+1` in the SEGMENT_KERNEL_ECDSA_TABLE segment.
|
||||
global precompute_table:
|
||||
global secp_precompute_table:
|
||||
// First store G, ± phi(G), G ± phi(G)
|
||||
// Use Gneg for the ±, e.g., ±phi(G) is computed as `Gneg * (-phi(G)) + (1-Gneg)*phi(G)` (note only the y-coordinate needs to be filtered).
|
||||
// stack: Gneg, Qneg, Qx, Qy, retdest
|
||||
@ -30,7 +30,7 @@ global precompute_table:
|
||||
DUP5 PUSH @SECP_BASE SUB MUL ADD
|
||||
%stack (selectQy, betaQx, Qx, Qy, retdest) -> (2, betaQx, 3, selectQy, betaQx, selectQy, Qx, Qy, precompute_table_contd, retdest)
|
||||
%mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
%jump(ec_add_valid_points_no_edge_case_secp)
|
||||
%jump(secp_add_valid_points_no_edge_case)
|
||||
precompute_table_contd:
|
||||
%stack (x, y, retdest) -> (6, x, 7, y, retdest)
|
||||
%mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
@ -46,7 +46,7 @@ precompute_table_loop:
|
||||
PUSH 9 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
PUSH 8 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
// stack: Gx, Gy, x, y, precompute_table_loop_contd, x, y, i, retdest
|
||||
%jump(ec_add_valid_points_secp)
|
||||
%jump(secp_add_valid_points)
|
||||
precompute_table_loop_contd:
|
||||
%stack (Rx, Ry, x, y, i, retdest) -> (i, 8, Rx, i, 9, Ry, x, y, i, retdest)
|
||||
ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
@ -54,14 +54,14 @@ precompute_table_loop_contd:
|
||||
PUSH 17 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
PUSH 16 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
%stack (Gx, Gy, x, y, x, y, i, retdest) -> (Gx, Gy, x, y, precompute_table_loop_contd2, x, y, i, retdest)
|
||||
%jump(ec_add_valid_points_secp)
|
||||
%jump(secp_add_valid_points)
|
||||
precompute_table_loop_contd2:
|
||||
%stack (Rx, Ry, x, y, i, retdest) -> (i, 16, Rx, i, 17, Ry, x, y, i, retdest)
|
||||
ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
PUSH 25 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
PUSH 24 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
%stack (Gx, Gy, x, y, i, retdest) -> (Gx, Gy, x, y, precompute_table_loop_contd3, i, retdest)
|
||||
%jump(ec_add_valid_points_secp)
|
||||
%jump(secp_add_valid_points)
|
||||
precompute_table_loop_contd3:
|
||||
%stack (Rx, Ry, i, retdest) -> (i, 24, Rx, i, 25, Ry, i, retdest)
|
||||
ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE)
|
||||
|
||||
69
evm/src/cpu/kernel/asm/curve/wnaf.asm
Normal file
69
evm/src/cpu/kernel/asm/curve/wnaf.asm
Normal file
@ -0,0 +1,69 @@
|
||||
// wNAF expansion with w=5.
|
||||
// Stores the reversed expansion of the given scalar in memory at the given segment and offsets 0..130.
|
||||
// Should be called with scalars of bit length <= 129, which is the case when using GLV.
|
||||
// Pseudo-code:
|
||||
// def wnaf(n):
|
||||
// ans = [0 for _ in range(130)]
|
||||
// o = 0
|
||||
// while n != 0:
|
||||
// i = n.trailing_zero_bits()
|
||||
// o += i
|
||||
// n >>= i
|
||||
// m = n & 31
|
||||
// ans[o] = m
|
||||
// if m > 16:
|
||||
// ne += 32
|
||||
// ne -= m
|
||||
// return ans
|
||||
global wnaf:
|
||||
// stack: N, segment, n, retdest (N is the size of the group in which the mul is taking place)
|
||||
DUP3 MOD ISZERO %jumpi(wnaf_zero_scalar)
|
||||
PUSH 0
|
||||
wnaf_loop:
|
||||
%stack (o, segment, n, retdest) -> (n, wnaf_loop_contd, o, segment, retdest)
|
||||
%jump(trailing_zeros)
|
||||
wnaf_loop_contd:
|
||||
%stack (n, i, o, segment, retdest) -> (o, i, n, segment, retdest)
|
||||
ADD
|
||||
%stack (o, n, segment, retdest) -> (n, segment, o, retdest)
|
||||
DUP1 %and_const(31) SWAP1
|
||||
PUSH 16 DUP3 GT
|
||||
// stack: m>16, n, m, segment, o, retdest
|
||||
%mul_const(32) ADD
|
||||
// stack: n, m, segment, o, retdest
|
||||
DUP2 SWAP1 SUB
|
||||
%stack (n, m, segment, o, retdest) -> (129, o, m, o, segment, n, retdest)
|
||||
SUB
|
||||
%stack (i, m, o, segment, n, retdest) -> (0, segment, i, m, o, segment, n, retdest)
|
||||
MSTORE_GENERAL
|
||||
// stack: o, segment, n, retdest
|
||||
DUP3 ISZERO %jumpi(wnaf_end)
|
||||
// stack: o, segment, n, retdest
|
||||
%jump(wnaf_loop)
|
||||
|
||||
wnaf_end:
|
||||
// stack: o, segment, n, retdest
|
||||
%pop3 JUMP
|
||||
|
||||
wnaf_zero_scalar:
|
||||
// stack: segment, n, retdest
|
||||
%pop2 JUMP
|
||||
|
||||
|
||||
|
||||
// Number of trailing zeros computed with a simple loop and returning the scalar without its lsb zeros.
|
||||
trailing_zeros:
|
||||
// stack: x, retdest
|
||||
PUSH 0
|
||||
trailing_zeros_loop:
|
||||
// stack: count, x, retdest
|
||||
PUSH 1 DUP3 AND
|
||||
// stack: x&1, count, x, retdest
|
||||
%jumpi(trailing_zeros_end)
|
||||
// stack: count, x, retdest
|
||||
%increment SWAP1 PUSH 1 SHR SWAP1
|
||||
// stack: count, x>>1, retdest
|
||||
%jump(trailing_zeros_loop)
|
||||
trailing_zeros_end:
|
||||
%stack (count, x, retdest) -> (retdest, x, count)
|
||||
JUMP
|
||||
@ -63,7 +63,7 @@ const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [
|
||||
),
|
||||
];
|
||||
|
||||
const EC_CONSTANTS: [(&str, [u8; 32]); 10] = [
|
||||
const EC_CONSTANTS: [(&str, [u8; 32]); 18] = [
|
||||
(
|
||||
"U256_MAX",
|
||||
hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
@ -72,6 +72,39 @@ const EC_CONSTANTS: [(&str, [u8; 32]); 10] = [
|
||||
"BN_BASE",
|
||||
hex!("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"),
|
||||
),
|
||||
(
|
||||
"BN_SCALAR",
|
||||
hex!("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"),
|
||||
),
|
||||
(
|
||||
"BN_GLV_BETA",
|
||||
hex!("000000000000000059e26bcea0d48bacd4f263f1acdb5c4f5763473177fffffe"),
|
||||
),
|
||||
(
|
||||
"BN_GLV_S",
|
||||
hex!("0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd"),
|
||||
),
|
||||
(
|
||||
"BN_GLV_MINUS_G1",
|
||||
hex!("000000000000000000000000000000024ccef014a773d2cf7a7bd9d4391eb18d"),
|
||||
),
|
||||
(
|
||||
"BN_GLV_G2",
|
||||
hex!("000000000000000000000000000000000000000000000002d91d232ec7e0b3d7"),
|
||||
),
|
||||
(
|
||||
"BN_GLV_B1",
|
||||
hex!("30644e72e131a029b85045b68181585cb8e665ff8b011694c1d039a872b0eed9"),
|
||||
),
|
||||
(
|
||||
"BN_GLV_B2",
|
||||
hex!("00000000000000000000000000000000000000000000000089d3256894d213e3"),
|
||||
),
|
||||
(
|
||||
"BN_BNEG_LOC",
|
||||
// This just needs to be large enough to not interfere with anything else in SEGMENT_KERNEL_BN_TABLE_Q.
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000001337"),
|
||||
),
|
||||
(
|
||||
"SECP_BASE",
|
||||
hex!("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"),
|
||||
|
||||
1049
evm/src/cpu/kernel/tests/ecc/bn_glv_test_data
Normal file
1049
evm/src/cpu/kernel/tests/ecc/bn_glv_test_data
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,15 +4,16 @@ mod bn {
|
||||
use ethereum_types::U256;
|
||||
|
||||
use crate::cpu::kernel::aggregator::KERNEL;
|
||||
use crate::cpu::kernel::interpreter::run_interpreter;
|
||||
use crate::cpu::kernel::interpreter::{run_interpreter, Interpreter};
|
||||
use crate::cpu::kernel::tests::u256ify;
|
||||
use crate::memory::segments::Segment;
|
||||
|
||||
#[test]
|
||||
fn test_ec_ops() -> Result<()> {
|
||||
// Make sure we can parse and assemble the entire kernel.
|
||||
let ec_add = KERNEL.global_labels["ec_add"];
|
||||
let ec_double = KERNEL.global_labels["ec_double"];
|
||||
let ec_mul = KERNEL.global_labels["ec_mul"];
|
||||
let ec_add = KERNEL.global_labels["bn_add"];
|
||||
let ec_double = KERNEL.global_labels["bn_double"];
|
||||
let ec_mul = KERNEL.global_labels["bn_mul"];
|
||||
let identity = ("0x0", "0x0");
|
||||
let invalid = ("0x0", "0x3"); // Not on curve
|
||||
let point0 = (
|
||||
@ -130,11 +131,100 @@ mod bn {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_glv_verify_data() -> Result<()> {
|
||||
let glv = KERNEL.global_labels["bn_glv_decompose"];
|
||||
|
||||
let f = include_str!("bn_glv_test_data");
|
||||
for line in f.lines().filter(|s| !s.starts_with("//")) {
|
||||
let mut line = line
|
||||
.split_whitespace()
|
||||
.map(|s| U256::from_str_radix(s, 10).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let k = line.remove(0);
|
||||
line.reverse();
|
||||
|
||||
let mut initial_stack = u256ify(["0xdeadbeef"])?;
|
||||
initial_stack.push(k);
|
||||
let mut int = Interpreter::new(&KERNEL.code, glv, initial_stack, &KERNEL.prover_inputs);
|
||||
int.run()?;
|
||||
|
||||
assert_eq!(line, int.stack());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_precomputation() -> Result<()> {
|
||||
let precompute = KERNEL.global_labels["bn_precompute_table"];
|
||||
|
||||
let initial_stack = u256ify([
|
||||
"0xdeadbeef",
|
||||
"0x10d7cf0621b6e42c1dbb421f5ef5e1936ca6a87b38198d1935be31e28821d171",
|
||||
"0x11b7d55f16aaac07de9a0ed8ac2e8023570dbaa78571fc95e553c4b3ba627689",
|
||||
])?;
|
||||
let mut int = Interpreter::new(
|
||||
&KERNEL.code,
|
||||
precompute,
|
||||
initial_stack,
|
||||
&KERNEL.prover_inputs,
|
||||
);
|
||||
int.run()?;
|
||||
|
||||
let mut computed_table = Vec::new();
|
||||
for i in 0..32 {
|
||||
computed_table.push(
|
||||
int.generation_state
|
||||
.memory
|
||||
.mload_general(0, Segment::BnTableQ, i),
|
||||
);
|
||||
}
|
||||
|
||||
let table = u256ify([
|
||||
"0x11b7d55f16aaac07de9a0ed8ac2e8023570dbaa78571fc95e553c4b3ba627689",
|
||||
"0x10d7cf0621b6e42c1dbb421f5ef5e1936ca6a87b38198d1935be31e28821d171",
|
||||
"0x1565e5587d8566239c23219bc0e1d1d267d19100c3869d0c55b1e3ea4532304e",
|
||||
"0x19fd9b572558479df062632562113e4d9a3eb655698ee3be9a5350ed23e690ee",
|
||||
"0x19469e55e27021c0af1310ad266cdf1d9eef6942c80afe9c7b517acf16a2a3e1",
|
||||
"0x226ec29db9339d7ffb1bc3260f1ca008b804f78553d316c37203118466bb5f5a",
|
||||
"0x10a16b4786bd1717a031a1948010593173d36ab35535641c9fe41802d639b435",
|
||||
"0x294fe34d7ec9024c96cfde58311b9ee394ff9f8735d882005fcf0d28709b459d",
|
||||
"0x300f58e61d4ab1872f6b5fad517c6df1b23468fcfa81154786ec230cb0df6d20",
|
||||
"0x12ff1d200127d2ba7a0171cadbe0f729fc5acbe95565cc57f07c9fa42c001390",
|
||||
"0x1045a28c9a35a17b63da593c0137ac08a1fda78430b71755941d3dc501b35272",
|
||||
"0x2a3f4d91b58179451ec177f599d7eaf79e2555f169fd3e5d2af314600fad299",
|
||||
"0x21de5680f03b262f53d3252d5ca71bbc5f2c9ff5483fb63abaea1ee7e9cede1d",
|
||||
"0x144249d3fc4c82327845a38ea51181acb374ab30a1e7ea0f13bc8a8b04d96411",
|
||||
"0x2ba4ce4289de377397878c1195e21a1d573b02d9463f5c454ec50bdf11aee512",
|
||||
"0x259a447b42bab48e07388baece550607bc0a8a88e1ea224eba94c6bed08e470e",
|
||||
"0x2ba4ce4289de377397878c1195e21a1d573b02d9463f5c454ec50bdf11aee512",
|
||||
"0xaca09f79e76eb9bb117ba07b32c5255db76e0088687a83e818bc55807eeb639",
|
||||
"0x21de5680f03b262f53d3252d5ca71bbc5f2c9ff5483fb63abaea1ee7e9cede1d",
|
||||
"0x1c22049ee4e51df7400aa227dc6fd6b0e40cbf60c689e07e2864018bd3a39936",
|
||||
"0x1045a28c9a35a17b63da593c0137ac08a1fda78430b71755941d3dc501b35272",
|
||||
"0x2dc05999c5d9889566642e3727e3d9ae1d9f153251d1f6a769715ad0d7822aae",
|
||||
"0x300f58e61d4ab1872f6b5fad517c6df1b23468fcfa81154786ec230cb0df6d20",
|
||||
"0x1d653152e009cd6f3e4ed3eba5a061339b269ea8130bfe354ba3ec72ac7ce9b7",
|
||||
"0x10a16b4786bd1717a031a1948010593173d36ab35535641c9fe41802d639b435",
|
||||
"0x7146b2562689ddd2180675e5065b97a0281cb0a3299488cdc517eee67e1b7aa",
|
||||
"0x19469e55e27021c0af1310ad266cdf1d9eef6942c80afe9c7b517acf16a2a3e1",
|
||||
"0xdf58bd527fe02a9bd3482907264b854df7c730c149eb3c9ca1d7a9271c19ded",
|
||||
"0x1565e5587d8566239c23219bc0e1d1d267d19100c3869d0c55b1e3ea4532304e",
|
||||
"0x1666b31bbbd9588bc7ede2911f701a0ffd42b43bfee2e6cea1cd3b29b4966c59",
|
||||
"0x11b7d55f16aaac07de9a0ed8ac2e8023570dbaa78571fc95e553c4b3ba627689",
|
||||
"0x1f8c7f6cbf7abbfd9a950397228b76ca2adac21630583d7406625a34505b2bd6",
|
||||
])?;
|
||||
|
||||
assert_eq!(computed_table, table);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod secp {
|
||||
|
||||
use anyhow::Result;
|
||||
use ethereum_types::U256;
|
||||
|
||||
@ -146,8 +236,8 @@ mod secp {
|
||||
fn test_ec_ops() -> Result<()> {
|
||||
// Make sure we can parse and assemble the entire kernel.
|
||||
let kernel = combined_kernel();
|
||||
let ec_add = kernel.global_labels["ec_add_valid_points_secp"];
|
||||
let ec_double = kernel.global_labels["ec_double_secp"];
|
||||
let ec_add = kernel.global_labels["secp_add_valid_points"];
|
||||
let ec_double = kernel.global_labels["secp_double"];
|
||||
let identity = ("0x0", "0x0");
|
||||
let point0 = (
|
||||
"0xc82ccceebd739e646631b7270ed8c33e96c4940b19db91eaf67da6ec92d109b",
|
||||
@ -207,9 +297,9 @@ mod secp {
|
||||
|
||||
#[test]
|
||||
fn test_glv_verify_data() -> Result<()> {
|
||||
let glv = KERNEL.global_labels["glv_decompose"];
|
||||
let glv = KERNEL.global_labels["secp_glv_decompose"];
|
||||
|
||||
let f = include_str!("glv_test_data");
|
||||
let f = include_str!("secp_glv_test_data");
|
||||
for line in f.lines().filter(|s| !s.starts_with("//")) {
|
||||
let mut line = line
|
||||
.split_whitespace()
|
||||
|
||||
@ -40,10 +40,13 @@ pub(crate) enum Segment {
|
||||
ShiftTable = 16,
|
||||
JumpdestBits = 17,
|
||||
EcdsaTable = 18,
|
||||
BnWnafA = 19,
|
||||
BnWnafB = 20,
|
||||
BnTableQ = 21,
|
||||
}
|
||||
|
||||
impl Segment {
|
||||
pub(crate) const COUNT: usize = 19;
|
||||
pub(crate) const COUNT: usize = 22;
|
||||
|
||||
pub(crate) fn all() -> [Self; Self::COUNT] {
|
||||
[
|
||||
@ -66,6 +69,9 @@ impl Segment {
|
||||
Self::ShiftTable,
|
||||
Self::JumpdestBits,
|
||||
Self::EcdsaTable,
|
||||
Self::BnWnafA,
|
||||
Self::BnWnafB,
|
||||
Self::BnTableQ,
|
||||
]
|
||||
}
|
||||
|
||||
@ -91,6 +97,9 @@ impl Segment {
|
||||
Segment::ShiftTable => "SEGMENT_SHIFT_TABLE",
|
||||
Segment::JumpdestBits => "SEGMENT_JUMPDEST_BITS",
|
||||
Segment::EcdsaTable => "SEGMENT_KERNEL_ECDSA_TABLE",
|
||||
Segment::BnWnafA => "SEGMENT_KERNEL_BN_WNAF_A",
|
||||
Segment::BnWnafB => "SEGMENT_KERNEL_BN_WNAF_B",
|
||||
Segment::BnTableQ => "SEGMENT_KERNEL_BN_TABLE_Q",
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,6 +125,9 @@ impl Segment {
|
||||
Segment::ShiftTable => 256,
|
||||
Segment::JumpdestBits => 1,
|
||||
Segment::EcdsaTable => 256,
|
||||
Segment::BnWnafA => 8,
|
||||
Segment::BnWnafB => 8,
|
||||
Segment::BnTableQ => 256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user