mratsim / constantine

Constantine: modular, high-performance, zero-dependency cryptography stack for verifiable computation, proof systems and blockchain protocols.
Other
390 stars 43 forks source link

Workaround for compile-time modulus and negInvModWord #16

Closed mratsim closed 4 years ago

mratsim commented 4 years ago

Due to upstream bug https://github.com/nim-lang/Nim/issues/9679 and another hard-to reproduce one for static Word, the use of compile-time property of the modulus requires lots of workarounds.

Bug 2 gives the following errors:

========================================================================================
Running tests/test_io_fields
========================================================================================
/home/beta/Programming/Nim/constantine/tests/test_io_fields.nim(19, 9) template/generic instantiation of `suite` from here
/home/beta/Programming/Nim/constantine/tests/test_io_fields.nim(20, 10) template/generic instantiation of `test` from here
/home/beta/Programming/Nim/constantine/tests/test_io_fields.nim(26, 10) template/generic instantiation of `fromUint` from here
/home/beta/Programming/Nim/constantine/constantine/io/io_fields.nim(29, 6) template/generic instantiation of `fromBig` from here
/home/beta/Programming/Nim/constantine/constantine/arithmetic/finite_fields.nim(64, 11) Error: type mismatch: got <BigInt[61], BigInt[61], BigInt[61], BigInt[61], static[Word](2305843009213693953)>
but expected one of: 
func montyResidue(mres: var BigInt; a, N, r2modN: BigInt; negInvModWord: static Word)
  first type mismatch at position: 5
  required type for negInvModWord: static[Word]
  but expression '2305843009213693953' is of type: static[Word](2305843009213693953)

Trying to reduce it to a minimal example makes it disappear ...

import macros

type Word = distinct uint64
const WordBitSize = 63

func wordsRequired*(bits: int): int {.compileTime.} =
  (bits + WordBitSize - 1) div WordBitSize

type
  BigInt*[bits: static int] = object
    bitLength: uint32
    limbs: array[bits.wordsRequired, Word]

  Curve = enum
    BN254
    BLS12_381

const CurveBitSize = [
  BN254: 254,
  BLS12_381: 381
]

template matchingBigInt(C: static Curve): untyped =
  # Need template indirection to avoid a sigmatch bug
  BigInt[CurveBitSize[C]]

type
  FiniteField*[C: static enum] = object
    big: matchingBigInt(C)

const BN254_Modulus = BigInt[254]()
const BN254_NegInvModWord = Word(2305843009213693953)

{.experimental: "dynamicBindSym".}

macro getModulus(C: static Curve): untyped =
  result = bindSym($C & "_Modulus")

macro getNegInvModWord(C: static Curve): untyped =
  result = bindSym($C & "_NegInvModWord")

func foo(mres: var BigInt, a, N: BigInt, negInvModWord: static Word) =
  discard

func bar(r: var FiniteField, a: FiniteField) =
  foo(r.big, a.big, a.C.getModulus(), a.C.getNegInvModWord())

var r: FiniteField[BN254]
let a = FiniteField[BN254]()

bar(r, a)
mratsim commented 4 years ago

Closing, even if it's fixed upstream, we are not trying to write a generic all-purpose finite field libraries.

Only supporting preconfigured moduli as like today is enough.