Conversation
…iply by p) Co-authored-by: shargon <3167973+shargon@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master-n3 #4738 +/- ##
=============================================
+ Coverage 83.26% 83.55% +0.28%
=============================================
Files 237 237
Lines 16566 16566
Branches 2421 2421
=============================================
+ Hits 13793 13841 +48
+ Misses 1987 1938 -49
- Partials 786 787 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
|
| } | ||
|
|
||
| [TestMethod] | ||
| [DataRow(1, 3, 1, 1)] |
There was a problem hiding this comment.
[bug] Every DataRow passes field modulus p = 1. BigInteger.Mod(1) is 0 for every integer, so FastLucasSequence and NaiveLucasSequence both always return (0, 0). The pre-loop reductions already zero Uh/Vl before for (j = 1; j <= s; ++j), so the old Uh * Vl * p (0 * 0 * 1) and the new (Uh * Vl).Mod(p) agree on every even-k row as well. Reverting line 92 of ECFieldElement.cs to Uh = Uh * Vl * p would still pass this test. The even-k cases do enter the doubling loop, but the assertions are tautologies and do not lock the BouncyCastle identity. The same P/Q/k values with prime p = 17 distinguish the bug on even k (e.g. k = 2 → fixed (3, 7) vs buggy (51, 7)) and still match the existing naive recurrence.
Suggestion: Keep the even-k rows and the naive oracle, but use a real prime modulus (13, 17, …). NaiveLucasSequence is a correct U_n/V_n recurrence; it only needs a non-degenerate p. Odd-k rows are fine as extra coverage of the non-doubling path once p is prime.
There was a problem hiding this comment.
@copilot
Replace the p = 1 DataRows with a real prime so the naive oracle can fail the old Uh * Vl * p path. Same P / Q / k values, p = 17:
[TestMethod]
[DataRow(17, 3, 1, 1)]
[DataRow(17, 3, 1, 2)]
[DataRow(17, 3, 1, 7)]
[DataRow(17, 3, 1, 8)]
[DataRow(17, 3, 1, 15)]
[DataRow(17, 3, 1, 16)]
[DataRow(17, 3, 1, 100)]
[DataRow(17, 5, 7, 33)]
[DataRow(17, 5, 7, 64)]
public void TestFastLucasSequence(int p, int P, int Q, int k)Even-k rows then distinguish the typo (e.g. k = 2 → fixed (3, 7) vs buggy (51, 7)). Odd-k rows still cover the non-doubling path. NaiveLucasSequence does not need to change.
There was a problem hiding this comment.
Pull request overview
Corrects modular reduction in FastLucasSequence and adds direct sequence tests.
Changes:
- Replaces multiplication by
pwith modulo reduction. - Exposes the helper internally for testing.
- Adds naive-reference tests, though their modulus currently makes them ineffective.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/Neo/Cryptography/ECC/ECFieldElement.cs |
Corrects the Lucas sequence update. |
tests/Neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs |
Adds direct sequence comparisons. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| for (var j = 1; j <= s; ++j) | ||
| { | ||
| Uh = Uh * Vl * p; | ||
| Uh = (Uh * Vl).Mod(p); |
|
|
||
| [TestMethod] | ||
| [DataRow(1, 3, 1, 1)] | ||
| [DataRow(1, 3, 1, 2)] |
cschuchardt88
left a comment
There was a problem hiding this comment.
Summary
One-line correction of the even-k doubling step (Uh = Uh * Vl * p → Uh = (Uh * Vl).Mod(p)), matching BouncyCastle Java Uh.multiply(Vl).mod(p) and the identity U_{2n} ≡ U_n V_n (mod p). Making the helper internal for tests is fine.
This is not a current N3 consensus change. Both secp256r1 and secp256k1 have Q ≡ 3 (mod 4), so Sqrt() uses ModPow and never calls FastLucasSequence. DecompressPoint also rejects any other curve. Even on a p ≡ 1 (mod 4) field, k = (q + 1) / 2 is odd, so s = 0 and the patched loop does not run. The issue/PR impact on signature verification and point decompression is overstated.
The remaining problem is the new tests: every DataRow uses p = 1, so they cannot fail the old code.
Issues
- bug
tests/Neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs:205—p = 1makesFastLucasSequenceand the naive oracle both return(0, 0)on every row. Reverting line 92 still passes. Status: open - suggestion
tests/Neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs:88—Sqrt()never callsFastLucasSequencein CI (Q ≡ 3 (mod 4)/ Legendre miss). Ap ≡ 1 (mod 4)Sqrt()case would cover the only production caller; it still would not execute line 92 unlesskis even. Status: open
| } | ||
|
|
||
| [TestMethod] | ||
| [DataRow(1, 3, 1, 1)] |
There was a problem hiding this comment.
Bug: Every DataRow still uses field modulus p = 1. BigInteger.Mod(1) is 0 for every integer, so FastLucasSequence and NaiveLucasSequence both always return (0, 0).
The pre-loop reductions already zero Uh/Vl before for (j = 1; j <= s; ++j), so the old Uh * Vl * p (0 * 0 * 1) and the new (Uh * Vl).Mod(p) agree on every even-k row as well. Reverting ECFieldElement.cs line 92 to Uh = Uh * Vl * p would still pass this test. The even-k cases do enter the doubling loop, but the assertions are tautologies and do not lock U_{2n} ≡ U_n V_n (mod p).
Same P / Q / k with prime p = 17 distinguishes the typo:
[DataRow(17, 3, 1, 1)]
[DataRow(17, 3, 1, 2)] // fixed (3, 7) vs buggy (51, 7)
[DataRow(17, 3, 1, 7)]
[DataRow(17, 3, 1, 8)]
[DataRow(17, 3, 1, 15)]
[DataRow(17, 3, 1, 16)]
[DataRow(17, 3, 1, 100)]
[DataRow(17, 5, 7, 33)]
[DataRow(17, 5, 7, 64)]NaiveLucasSequence does not need to change. Line 92 is only reachable for even k; Sqrt() always passes odd k = (q + 1) / 2, so this helper test is the regression lock.
ECFieldElement.FastLucasSequencecontained a typo that multipliedUhbypinstead of reducing it modulop, causingUhto grow exponentially each iteration and producing incorrect square root values. This breaks elliptic curve point decompression and signature verification.Change
ECFieldElement.csline 92:Uh = Uh * Vl * p→Uh = (Uh * Vl).Mod(p)