Open mratsim opened 2 months ago
!nim c
type BigInt[N: static int] = object
raw: array[N, uint64]
func isZero(a: BigInt): bool =
discard
func isOne(a: BigInt): bool =
discard
type
QuadraticExt*[F] = object
## Quadratic Extension field
coords*: array[2, F]
CubicExt*[F] = object
## Cubic Extension field
coords*: array[3, F]
ExtensionField*[F] = QuadraticExt[F] or CubicExt[F]
func isOne*(a: ExtensionField): bool =
## Constant-time check if one
result = 1'u64
result = result and a.coords[0].isOne()
for i in 1 ..< a.coords.len:
result = result and a.coords[i].isZero()
func mulCheckSparse*(a: var QuadraticExt, b: static QuadraticExt) {.inline.} =
static:
debugEcho "QuadraticExt b: ", b is QuadraticExt # This is true
debugEcho "ExtensionField b: ", b is ExtensionField # This is false
when isOne(b).bool:
discard
else:
debugEcho "a *= b"
type EllipticCurve*[F] = object
x*, y*, z*: F
template sumImpl[F](
r: var EllipticCurve[F],
P, Q: EllipticCurve[F],
CoefA: typed) {.dirty.} =
when CoefA is int:
debugEcho "EC addition with int CoefA"
else:
var b = P.x
b.mulCheckSparse(CoefA)
func sum[F](
r: var EllipticCurve[F],
P, Q: EllipticCurve[F],
CoefA: static F) =
r.sumImpl(P, Q, CoefA)
## ----------------------------------
const h2c_const_ec = QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 1, 1, 1]),
BigInt[4](raw: [uint64 1, 1, 1, 1])]
)
var P = EllipticCurve[QuadraticExt[BigInt[4]]](
x: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
),
y: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
),
z: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
)
)
var Q = EllipticCurve[QuadraticExt[BigInt[4]]](
x: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
),
y: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
),
z: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
)
)
var R: EllipticCurve[QuadraticExt[BigInt[4]]]
R.sum(P, Q, h2c_const_ec)
0 bytes (0 bytes)
```cpp
```
2024-04-29T11:46:09
2024-04-29T11:46:09
0 bytes (0 bytes)
```cpp
```
2024-04-29T11:46:10
2024-04-29T11:46:11
0 bytes (0 bytes)
```cpp
```
2024-04-29T11:46:13
2024-04-29T11:46:13
0 bytes (0 bytes)
```cpp
```
2024-04-29T11:46:16
2024-04-29T11:46:16
0 bytes (0 bytes)
```cpp
```
2024-04-29T11:46:19
2024-04-29T11:46:19
0 bytes (0 bytes)
```cpp
```
2024-04-29T11:46:21
2024-04-29T11:46:21
0 bytes (0 bytes)
```cpp
```
2024-04-29T11:46:23
2024-04-29T11:46:23
11.4.0
14.0.0
20.2
2024-04-29T11:45:44Z
1
nim c --run -d:nimDebug -d:nimDebugDlOpen -d:ssl -d:nimDisableCertificateValidation --forceBuild:on --colors:off --verbosity:0 --hints:off --lineTrace:off --nimcache:/home/runner/work/Nim/Nim --out:/home/runner/work/Nim/Nim/temp /home/runner/work/Nim/Nim/temp.nim
:robot: Bug found in 17 minutes
bisecting 7
commits at 0
commits per second
Bisecting with the full Constantine lib.
The regression happened in 5e20e935ddb58bc10bcdddd92555c16743e35ce5 / #22828 that was trying to fix #22826
I assume #22828 fixed a caching issue that happened to make my full-blown library work and that fix surfaced the long-standing underlying bug.
I also assume that the test case @alaviss mentioned being broken is related
import macros
type
A[T] = T
macro eqTyp(x, y: typed) =
# NOTE: the `or` is due to sameType not being commutative: https://github.com/nim-lang/Nim/issues/18867
doAssert sameType(x, y) or sameType(y, x), "nim does not consider these to be the same!"
eqTyp(A[float], float)
This compiles:
type BigInt[N: static int] = object
raw: array[N, uint64]
func isZero(a: BigInt): bool =
discard
func isOne(a: BigInt): bool =
discard
type
QuadraticExt*[F] = object
## Quadratic Extension field
coords*: array[2, F]
CubicExt*[F] = object
## Cubic Extension field
coords*: array[3, F]
ExtensionField*[F] = QuadraticExt[F] | CubicExt[F]
func isOne*[T](a: ExtensionField[T]): bool =
## Constant-time check if one
result = a.coords[0].isOne()
for i in 1 ..< a.coords.len:
result = result and a.coords[i].isZero()
template mulCheckSparse*[T](a: var QuadraticExt[T], b: QuadraticExt[T]) =
static:
debugEcho "QuadraticExt b: ", b is QuadraticExt # This is true
debugEcho "ExtensionField b: ", b is ExtensionField # This is false
when isOne(b).bool:
discard
else:
debugEcho "a *= b"
type EllipticCurve*[F] = object
x*, y*, z*: F
template sumImpl[F](
r: var EllipticCurve[F],
P, Q: EllipticCurve[F],
CoefA: F) {.dirty.} =
when CoefA is int:
debugEcho "EC addition with int CoefA"
else:
var b = P.x
b.mulCheckSparse(CoefA)
func sum[F](
r: var EllipticCurve[F],
P, Q: EllipticCurve[F],
CoefA: static F) =
r.sumImpl(P, Q, CoefA)
## ----------------------------------
const h2c_const_ec = QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 1, 1, 1]),
BigInt[4](raw: [uint64 1, 1, 1, 1])]
)
var P = EllipticCurve[QuadraticExt[BigInt[4]]](
x: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
),
y: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
),
z: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
)
)
var Q = EllipticCurve[QuadraticExt[BigInt[4]]](
x: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
),
y: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
),
z: QuadraticExt[BigInt[4]](
coords: [BigInt[4](raw: [uint64 1, 2, 3, 4]),
BigInt[4](raw: [uint64 1, 2, 3, 4])]
)
)
var R: EllipticCurve[QuadraticExt[BigInt[4]]]
R.sum(P, Q, h2c_const_ec)
So the only difference is
func isOne*(a: ExtensionField): bool =
## Constant-time check if one
result = 1'u64
result = result and a.coords[0].isOne()
for i in 1 ..< a.coords.len:
result = result and a.coords[i].isZero()
was changed to
func isOne*[T](a: ExtensionField[T]): bool =
## Constant-time check if one
result = a.coords[0].isOne()
for i in 1 ..< a.coords.len:
result = result and a.coords[i].isZero()
?
Original issue: https://github.com/mratsim/constantine/issues/373
It can be reproduced in its full environment with:
In the original issue, Nim v2.0.4 works but latest version-2-0 branch (v2.0.5) doesn't.
Now here is a single file reproduction. Surprisingly this fails on both Nim v2.0.4 and latest version-2-0