123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- discard """
- joinable: false
- """
- # debug ICE: genCheckedRecordField
- # apparently after https://github.com/nim-lang/Nim/pull/23477
- # bug #23784
- import std/bitops, std/macros
- # --------------------------------------------------------------
- type Algebra = enum
- BN254_Snarks
- type SecretWord* = distinct uint64
- const WordBitWidth* = sizeof(SecretWord) * 8
- func wordsRequired*(bits: int): int {.inline.} =
- const divShiftor = fastLog2(WordBitWidth)
- result = (bits + WordBitWidth - 1) shr divShiftor
- type
- BigInt*[bits: static int] = object
- limbs*: array[bits.wordsRequired, SecretWord] # <--- crash points to here
- # --------------------------------------------------------------
- const CurveBitWidth = [
- BN254_Snarks: 254
- ]
- const BN254_Snarks_Modulus = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x2, SecretWord 0x3, SecretWord 0x4])
- const BN254_Snarks_Order = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x2, SecretWord 0x2])
- func montyOne*(M: BigInt[254]): BigInt[254] =
- ## Returns "1 (mod M)" in the Montgomery domain.
- ## This is equivalent to R (mod M) in the natural domain
- BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x1, SecretWord 0x1])
- {.experimental: "dynamicBindSym".}
- type
- DerivedConstantMode* = enum
- kModulus
- kOrder
- macro genDerivedConstants*(mode: static DerivedConstantMode): untyped =
- ## Generate constants derived from the main constants
- ##
- ## For example
- ## - the Montgomery magic constant "R^2 mod N" in ROM
- ## For each curve under the private symbol "MyCurve_R2modP"
- ## - the Montgomery magic constant -1/P mod 2^Wordbitwidth
- ## For each curve under the private symbol "MyCurve_NegInvModWord
- ## - ...
- # Now typedesc are NimNode and there is no way to translate
- # NimNode -> typedesc easily so we can't
- # "for curve in low(Curve) .. high(Curve):"
- # As an ugly workaround, we count
- # The item at position 0 is a pragma
- result = newStmtList()
- template used(name: string): NimNode =
- nnkPragmaExpr.newTree(
- ident(name),
- nnkPragma.newTree(ident"used")
- )
- let ff = if mode == kModulus: "_Fp" else: "_Fr"
- for curveSym in low(Algebra) .. high(Algebra):
- let curve = $curveSym
- let M = if mode == kModulus: bindSym(curve & "_Modulus")
- else: bindSym(curve & "_Order")
- # const MyCurve_montyOne = montyOne(MyCurve_Modulus)
- result.add newConstStmt(
- used(curve & ff & "_MontyOne"), newCall(
- bindSym"montyOne",
- M
- )
- )
- # --------------------------------------------------------------
- {.experimental: "dynamicBindSym".}
- genDerivedConstants(kModulus)
- genDerivedConstants(kOrder)
- proc bindConstant(ff: NimNode, property: string): NimNode =
- # Need to workaround https://github.com/nim-lang/Nim/issues/14021
- # which prevents checking if a type FF[Name] = Fp[Name] or Fr[Name]
- # was instantiated with Fp or Fr.
- # getTypeInst only returns FF and sameType doesn't work.
- # so quote do + when checks.
- let T = getTypeInst(ff)
- T.expectKind(nnkBracketExpr)
- doAssert T[0].eqIdent("typedesc")
- let curve =
- if T[1].kind == nnkBracketExpr: # typedesc[Fp[BLS12_381]] as used internally
- # doAssert T[1][0].eqIdent"Fp" or T[1][0].eqIdent"Fr", "Found ident: '" & $T[1][0] & "' instead of 'Fp' or 'Fr'"
- T[1][1].expectKind(nnkIntLit) # static enum are ints in the VM
- $Algebra(T[1][1].intVal)
- else: # typedesc[bls12381_fp] alias as used for C exports
- let T1 = getTypeInst(T[1].getImpl()[2])
- if T1.kind != nnkBracketExpr or
- T1[1].kind != nnkIntLit:
- echo T.repr()
- echo T1.repr()
- echo getTypeInst(T1).treerepr()
- error "getTypeInst didn't return the full instantiation." &
- " Dealing with types in macros is hard, complain at https://github.com/nim-lang/RFCs/issues/44"
- $Algebra(T1[1].intVal)
- let curve_fp = bindSym(curve & "_Fp_" & property)
- let curve_fr = bindSym(curve & "_Fr_" & property)
- result = quote do:
- when `ff` is Fp:
- `curve_fp`
- elif `ff` is Fr:
- `curve_fr`
- else:
- {.error: "Unreachable, received type: " & $`ff`.}
- # --------------------------------------------------------------
- template matchingBigInt*(Name: static Algebra): untyped =
- ## BigInt type necessary to store the prime field Fp
- # Workaround: https://github.com/nim-lang/Nim/issues/16774
- # as we cannot do array accesses in type section.
- # Due to generic sandwiches, it must be exported.
- BigInt[CurveBitWidth[Name]]
- type
- Fp*[Name: static Algebra] = object
- mres*: matchingBigInt(Name)
- macro getMontyOne*(ff: type Fp): untyped =
- ## Get one in Montgomery representation (i.e. R mod P)
- result = bindConstant(ff, "MontyOne")
- func getOne*(T: type Fp): T {.noInit, inline.} =
- result = cast[ptr T](unsafeAddr getMontyOne(T))[]
- # --------------------------------------------------------------
- proc foo(T: Fp) =
- discard T
- let a = Fp[BN254_Snarks].getOne()
- foo(a) # oops this was a leftover that broke the bisect.
|