Allow compilation of FInite Field and Montgomery domain, workaround showstopper bug: https://github.com/nim-lang/Nim/issues/11142 and #5
This commit is contained in:
parent
bd2b10817e
commit
18625cc5ac
|
@ -27,6 +27,11 @@ import
|
|||
# to name curves according to the length of the prime bit length.
|
||||
# i.e. the BN254 was previously named BN128.
|
||||
|
||||
# Generates:
|
||||
# - type Curve = enum
|
||||
# - const CurveBitSize: array[Curve, int]
|
||||
# - proc Mod(curve: static Curve): auto
|
||||
# which returns the field modulus of the curve
|
||||
declareCurves:
|
||||
# Barreto-Naehrig curve, Prime 254 bit, 128-bit security, https://eprint.iacr.org/2013/879.pdf
|
||||
# Usage: Zero-Knowledge Proofs / zkSNARKs in ZCash and Ethereum 1
|
||||
|
|
|
@ -14,21 +14,22 @@
|
|||
|
||||
# We assume that p is prime known at compile-time
|
||||
|
||||
import
|
||||
./word_types, ./bigints
|
||||
|
||||
import ./word_types, ./bigints, ./curves_config
|
||||
from ./private/word_types_internal import unsafe_div2n1n
|
||||
|
||||
type
|
||||
Fp*[P: static BigInt] = object
|
||||
## P is a prime number
|
||||
## All operations on a field are modulo P
|
||||
value: type(P)
|
||||
static: echo CurveBitSize
|
||||
|
||||
Montgomery*[M: static BigInt] = object
|
||||
## All operations in the Montgomery domain
|
||||
## are modulo M. M **must** be odd
|
||||
value: type(M)
|
||||
type
|
||||
Fp*[C: static Curve] = object
|
||||
## P is the prime modulus of the Curve C
|
||||
## All operations on a field are modulo P
|
||||
value: BigInt[CurveBitSize[C]]
|
||||
|
||||
Montgomery*[C: static Curve] = object
|
||||
## P is the prime modulus of the Curve C
|
||||
## All operations in the Montgomery domain are modulo P
|
||||
## P **must** be odd
|
||||
value: BigInt[CurveBitSize[C]]
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
@ -67,43 +68,43 @@ func `+`*(a, b: Fp): Fp =
|
|||
|
||||
result = a
|
||||
var ctl = add(result, b, CtTrue)
|
||||
ctl = ctl or not sub(result, Fp.P, CtFalse)
|
||||
sub(result, Fp.P, ctl)
|
||||
ctl = ctl or not sub(result, Fp.C.Mod, CtFalse)
|
||||
sub(result, Fp.C.Mod, ctl)
|
||||
|
||||
template shiftAddImpl(a: var Fp, c: Word) =
|
||||
func shiftAdd*(a: var Fp, c: Word) =
|
||||
## Shift-accumulate
|
||||
## Shift input a by a word and add c.
|
||||
## Shift input `a` by a word and add `c`.
|
||||
##
|
||||
## With a word W = 2^WordBitSize and a field Fp
|
||||
## Does a <- a * W + c (mod p)
|
||||
const len = a.value.limbs.len
|
||||
|
||||
when Fp.P.bits <= WordBitSize:
|
||||
when Fp.C.Mod.bits <= WordBitSize:
|
||||
# If the prime fits in a single limb
|
||||
var q: Word
|
||||
|
||||
# (hi, lo) = a * 2^63 + c
|
||||
let hi = a[0] shr 1 # 64 - 63 = 1
|
||||
let lo = (a[0] shl WordBitSize) or c # Assumes most-significant bit in c is not set
|
||||
unsafeDiv2n1n(q, a[0], hi, lo, Fp.P.limbs[0]) # (hi, lo) mod P
|
||||
let hi = a[0] shr 1 # 64 - 63 = 1
|
||||
let lo = (a[0] shl WordBitSize) or c # Assumes most-significant bit in c is not set
|
||||
unsafeDiv2n1n(q, a[0], hi, lo, Fp.C.Mod.limbs[0]) # (hi, lo) mod P
|
||||
|
||||
else:
|
||||
## Multiple limbs
|
||||
let hi = a[^1] # Save the high word to detect carries
|
||||
const R = Fp.P.bits and WordBitSize # R = bits mod 64
|
||||
const R = Fp.C.Mod.bits and WordBitSize # R = bits mod 64
|
||||
|
||||
when R == 0: # If the number of bits is a multiple of 64
|
||||
let a1 = a[^2] #
|
||||
let a0 = a[^1] #
|
||||
moveMem(a[1], a[0], (len-1) * Word.sizeof) # we can just shift words
|
||||
a[0] = c # and replace the first one by c
|
||||
const p0 = Fp.P[^1]
|
||||
const p0 = Fp.C.Mod[^1]
|
||||
else: # Need to deal with partial word shifts at the edge.
|
||||
let a1 = ((a[^2] shl (WordBitSize-R)) or (a[^3] shr R)) and MaxWord
|
||||
let a0 = ((a[^1] shl (WordBitSize-R)) or (a[^2] shr R)) and MaxWord
|
||||
moveMem(a[1], a[0], (len-1) * Word.sizeof)
|
||||
a[0] = c
|
||||
const p0 = ((Fp.P[^1] shl (WordBitSize-R)) or (Fp.P[^2] shr R)) and MaxWord
|
||||
const p0 = ((Fp.C.Mod[^1] shl (WordBitSize-R)) or (Fp.C.Mod[^2] shr R)) and MaxWord
|
||||
|
||||
# p0 has its high bit set. (a0, a1)/p0 fits in a limb.
|
||||
# Get a quotient q, at most we will be 2 iterations off
|
||||
|
@ -113,9 +114,9 @@ template shiftAddImpl(a: var Fp, c: Word) =
|
|||
a_hi = a0 shr 1 # 64 - 63 = 1
|
||||
a_lo = (a0 shl WordBitSize) or a1
|
||||
var q, r: Word
|
||||
q = unsafeDiv2n1n(q, r, a_hi, a_lo, p0) # Estimate quotient
|
||||
q = unsafeDiv2n1n(q, r, a_hi, a_lo, p0) # Estimate quotient
|
||||
q = mux( # If n_hi == divisor
|
||||
a0 == b0, MaxWord, # Quotient == MaxWord (0b0111...1111)
|
||||
a0 == b0, MaxWord, # Quotient == MaxWord (0b0111...1111)
|
||||
mux(
|
||||
q == 0, 0, # elif q == 0, true quotient = 0
|
||||
q - 1 # else instead of being of by 0, 1 or 2
|
||||
|
@ -124,30 +125,30 @@ template shiftAddImpl(a: var Fp, c: Word) =
|
|||
|
||||
# Now substract a*2^63 - q*p
|
||||
var carry = Word(0)
|
||||
var over_p = Word(1) # Track if quotient than the modulus
|
||||
var over_p = Word(1) # Track if quotient than the modulus
|
||||
|
||||
for i in static(0 ..< Fp.P.limbs.len):
|
||||
for i in static(0 ..< Fp.C.Mod.limbs.len):
|
||||
var qp_lo: Word
|
||||
|
||||
block: # q*p
|
||||
qp_hi: Word
|
||||
unsafeExtendedPrecMul(qp_hi, qp_lo, q, Fp.P[i]) # q * p
|
||||
unsafeExtendedPrecMul(qp_hi, qp_lo, q, Fp.C.Mod[i]) # q * p
|
||||
assert qp_lo.isMsbSet.not
|
||||
assert carry.isMsbSet.not
|
||||
qp_lo += carry # Add carry from previous limb
|
||||
qp_lo += carry # Add carry from previous limb
|
||||
let qp_carry = qp_lo.isMsbSet
|
||||
carry = mux(qp_carry, qp_hi + Word(1), qp_hi) # New carry
|
||||
carry = mux(qp_carry, qp_hi + Word(1), qp_hi) # New carry
|
||||
|
||||
qp_lo = qp_lo and MaxWord # Normalize to u63
|
||||
qp_lo = qp_lo and MaxWord # Normalize to u63
|
||||
|
||||
block: # a*2^63 - q*p
|
||||
a[i] -= qp_lo
|
||||
carry += Word(a[i].isMsbSet) # Adjust if borrow
|
||||
a[i] = a[i] and MaxWord # Normalize to u63
|
||||
carry += Word(a[i].isMsbSet) # Adjust if borrow
|
||||
a[i] = a[i] and MaxWord # Normalize to u63
|
||||
|
||||
over_p = mux(
|
||||
a[i] == Fp.P[i], over_p,
|
||||
a[i] > Fp.P[i]
|
||||
a[i] == Fp.C.Mod[i], over_p,
|
||||
a[i] > Fp.C.Mod[i]
|
||||
)
|
||||
|
||||
# Fix quotient, the true quotient is either q-1, q or q+1
|
||||
|
@ -158,21 +159,5 @@ template shiftAddImpl(a: var Fp, c: Word) =
|
|||
let neg = carry < hi
|
||||
let tooBig = not neg and (over_p or (carry < hi))
|
||||
|
||||
add(a, Fp.P, neg)
|
||||
sub(a, Fp.P, tooBig)
|
||||
|
||||
func shiftAdd*(a: var Fp, c: Word) =
|
||||
## Shift-accumulate modulo P
|
||||
## Shift input a by a word and add c modulo P
|
||||
##
|
||||
## With a word W = 2^WordBitSize and a field Fp
|
||||
## Does a <- a * W + c (mod p)
|
||||
shiftAddImpl(a, c)
|
||||
|
||||
func shiftAdd*(a: var Fp, c: static Word) =
|
||||
## Scale-accumulate modulo P
|
||||
## Shift input a by a word and add c modulo P
|
||||
##
|
||||
## With a word W = 2^WordBitSize and a field Fp
|
||||
## Does a <- a * W + c (mod p)
|
||||
shiftAddImpl(a, c)
|
||||
add(a, Fp.C.Mod, neg)
|
||||
sub(a, Fp.C.Mod, tooBig)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
# ############################################################
|
||||
|
||||
import
|
||||
./word_types, ./bigints, ./field_fp
|
||||
./word_types, ./bigints, ./field_fp, ./curves_config
|
||||
|
||||
from bitops import fastLog2
|
||||
# This will only be used at compile-time
|
||||
|
@ -70,12 +70,12 @@ func montyMagic*(M: static BigInt): static Word =
|
|||
for _ in static(0 ..< k):
|
||||
result *= 2 + M * result # x' = x(2 + ax) (`+` to avoid negating at the end)
|
||||
|
||||
func toMonty*[P: static BigInt](a: Fp[P]): Montgomery[P] =
|
||||
func toMonty*[C: static Curve](a: Fp[C]): Montgomery[C] =
|
||||
## Convert a big integer over Fp to it's montgomery representation
|
||||
## over Fp.
|
||||
## i.e. Does "a * (2^LimbSize)^W (mod p), where W is the number
|
||||
## of words needed to represent p in base 2^LimbSize
|
||||
|
||||
result = a
|
||||
for i in static(countdown(P.limbs.high, 0)):
|
||||
for i in static(countdown(C.Mod.limbs.high, 0)):
|
||||
shiftAdd(result, 0)
|
||||
|
|
|
@ -119,8 +119,9 @@ macro declareCurves*(curves: untyped): untyped =
|
|||
pure = false
|
||||
)
|
||||
|
||||
let cbs = ident("CurveBitSize")
|
||||
result.add quote do:
|
||||
const CurveBitSize: array[Curve, int] = `CurveBitSize`
|
||||
const `cbs`*: array[Curve, int] = `CurveBitSize`
|
||||
|
||||
result.add curveModStmts
|
||||
|
||||
|
@ -134,7 +135,7 @@ macro declareCurves*(curves: untyped): untyped =
|
|||
)
|
||||
)
|
||||
result.add newProc(
|
||||
name = nnkPostfix.newTree(ident"*", ident"fieldModulus"),
|
||||
name = nnkPostfix.newTree(ident"*", ident"Mod"),
|
||||
params = [
|
||||
ident"auto",
|
||||
newIdentDefs(
|
||||
|
|
Loading…
Reference in New Issue