Skip to content

crypto: Spell out the BN254 ate loop schedule digits - #1646

Merged
chfast merged 1 commit into
masterfrom
crypto/naf-array
Aug 11, 2026
Merged

crypto: Spell out the BN254 ate loop schedule digits#1646
chfast merged 1 commit into
masterfrom
crypto/naf-array

Conversation

@chfast

@chfast chfast commented Aug 11, 2026

Copy link
Copy Markdown
Member

The Miller loop schedule was a uint128 of 2-bit groups, unpacked with a shift and two bit tests per iteration and paired with a separate length constant. Storing the digits as -1, 0 and 1 lets the loop range over them, so the shift, the length constant and one of the two add branches go away.

The digits are not the non-adjacent form of 6x+2, which is worth knowing before anyone regenerates them: the NAF has 66 digits and leads with 1, 0, -1, while this schedule has 65 and leads with 1, 1. Same value, same 22 non-zero digits, one loop iteration less, so a plain NAF routine would add that iteration back. gnark-crypto's LoopCounter [66]int8 is the canonical NAF and pays it. Hence the rename to _DIGITS and the "semi-NAF" note in the comment.

Within digits {-1, 0, 1} this schedule is optimal on both axes: 65 digits is the minimum length, since 64 digits reach only 2^64-1 < 6x+2, and 22 non-zero digits is the minimum weight, which is the NAF's. Wider digit sets do not help either: from w=3 up, every digit above 1 needs an extra full Fq12 multiplication by a precomputed f_d(P), because Miller's addition rule is f_{i+j} = f_i * f_j * l / v and f_1 is the only constant one. Measured against a deliberately wrong table with 5-NAF's shape, that ceiling is -9% instructions, while the Fq12 factors, the seven affine odd multiples and the seven per-pair f_d(P) constants cost about 2.7 times more than the ceiling saves.

Instructions and branches are unchanged, -0.01% and -0.18% on the ECPAIRING benchmark. Cycles could not resolve a difference on this machine under load: four runs gave -4.3%, -1.3%, -3.2% and +1.1%.

The digits were checked by reconstructing 6x+2 from them, and every single-digit mutation of the table, as well as a change of its length, is caught by the existing pairing tests.

This touches the same function as #1544, so whichever lands second needs a rebase.

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.72%. Comparing base (2184b24) to head (37feca2).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1646      +/-   ##
==========================================
- Coverage   97.72%   97.72%   -0.01%     
==========================================
  Files         171      171              
  Lines       15629    15624       -5     
  Branches     3613     3613              
==========================================
- Hits        15273    15268       -5     
  Misses        269      269              
  Partials       87       87              
Flag Coverage Δ
eest-develop 88.57% <100.00%> (-0.01%) ⬇️
eest-develop-gmp 26.48% <100.00%> (-0.03%) ⬇️
eest-legacy 17.15% <0.00%> (+<0.01%) ⬆️
eest-libsecp256k1 28.78% <100.00%> (-0.03%) ⬇️
eest-stable 88.57% <100.00%> (-0.01%) ⬇️
evmone-unittests 93.44% <100.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
core 96.10% <100.00%> (-0.01%) ⬇️
tooling 91.92% <ø> (ø)
tests 99.80% <ø> (ø)
Files with missing lines Coverage Δ
lib/evmone_precompiles/pairing/bn254/pairing.cpp 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The Miller loop schedule was a uint128 of 2-bit groups, unpacked with a
shift and two bit tests per iteration and paired with a separate length
constant. Storing the digits as -1, 0 and 1 lets the loop range over
them: the shift, the length constant and one of the two add branches go
away. Instruction and branch counts are unchanged.

The name claimed NAF, but the digits are only a semi-NAF: the
non-adjacent form of 6x+2 leads with 1, 0, -1 where this leads with
1, 1, encoding the same value with the same 22 non-zero digits but one
iteration less. Renamed accordingly.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Replaces the packed BN254 Miller-loop schedule with explicit signed digits while preserving pairing behavior.

Changes:

  • Adds a documented 64-digit semi-NAF schedule.
  • Iterates directly over signed digits, simplifying branch logic.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@chfast
chfast merged commit fead50d into master Aug 11, 2026
24 checks passed
@chfast
chfast deleted the crypto/naf-array branch August 11, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants