t23784.nim 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. discard """
  2. joinable: false
  3. """
  4. # debug ICE: genCheckedRecordField
  5. # apparently after https://github.com/nim-lang/Nim/pull/23477
  6. # bug #23784
  7. import std/bitops, std/macros
  8. # --------------------------------------------------------------
  9. type Algebra = enum
  10. BN254_Snarks
  11. type SecretWord* = distinct uint64
  12. const WordBitWidth* = sizeof(SecretWord) * 8
  13. func wordsRequired*(bits: int): int {.inline.} =
  14. const divShiftor = fastLog2(WordBitWidth)
  15. result = (bits + WordBitWidth - 1) shr divShiftor
  16. type
  17. BigInt*[bits: static int] = object
  18. limbs*: array[bits.wordsRequired, SecretWord] # <--- crash points to here
  19. # --------------------------------------------------------------
  20. const CurveBitWidth = [
  21. BN254_Snarks: 254
  22. ]
  23. const BN254_Snarks_Modulus = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x2, SecretWord 0x3, SecretWord 0x4])
  24. const BN254_Snarks_Order = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x2, SecretWord 0x2])
  25. func montyOne*(M: BigInt[254]): BigInt[254] =
  26. ## Returns "1 (mod M)" in the Montgomery domain.
  27. ## This is equivalent to R (mod M) in the natural domain
  28. BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x1, SecretWord 0x1])
  29. {.experimental: "dynamicBindSym".}
  30. type
  31. DerivedConstantMode* = enum
  32. kModulus
  33. kOrder
  34. macro genDerivedConstants*(mode: static DerivedConstantMode): untyped =
  35. ## Generate constants derived from the main constants
  36. ##
  37. ## For example
  38. ## - the Montgomery magic constant "R^2 mod N" in ROM
  39. ## For each curve under the private symbol "MyCurve_R2modP"
  40. ## - the Montgomery magic constant -1/P mod 2^Wordbitwidth
  41. ## For each curve under the private symbol "MyCurve_NegInvModWord
  42. ## - ...
  43. # Now typedesc are NimNode and there is no way to translate
  44. # NimNode -> typedesc easily so we can't
  45. # "for curve in low(Curve) .. high(Curve):"
  46. # As an ugly workaround, we count
  47. # The item at position 0 is a pragma
  48. result = newStmtList()
  49. template used(name: string): NimNode =
  50. nnkPragmaExpr.newTree(
  51. ident(name),
  52. nnkPragma.newTree(ident"used")
  53. )
  54. let ff = if mode == kModulus: "_Fp" else: "_Fr"
  55. for curveSym in low(Algebra) .. high(Algebra):
  56. let curve = $curveSym
  57. let M = if mode == kModulus: bindSym(curve & "_Modulus")
  58. else: bindSym(curve & "_Order")
  59. # const MyCurve_montyOne = montyOne(MyCurve_Modulus)
  60. result.add newConstStmt(
  61. used(curve & ff & "_MontyOne"), newCall(
  62. bindSym"montyOne",
  63. M
  64. )
  65. )
  66. # --------------------------------------------------------------
  67. {.experimental: "dynamicBindSym".}
  68. genDerivedConstants(kModulus)
  69. genDerivedConstants(kOrder)
  70. proc bindConstant(ff: NimNode, property: string): NimNode =
  71. # Need to workaround https://github.com/nim-lang/Nim/issues/14021
  72. # which prevents checking if a type FF[Name] = Fp[Name] or Fr[Name]
  73. # was instantiated with Fp or Fr.
  74. # getTypeInst only returns FF and sameType doesn't work.
  75. # so quote do + when checks.
  76. let T = getTypeInst(ff)
  77. T.expectKind(nnkBracketExpr)
  78. doAssert T[0].eqIdent("typedesc")
  79. let curve =
  80. if T[1].kind == nnkBracketExpr: # typedesc[Fp[BLS12_381]] as used internally
  81. # doAssert T[1][0].eqIdent"Fp" or T[1][0].eqIdent"Fr", "Found ident: '" & $T[1][0] & "' instead of 'Fp' or 'Fr'"
  82. T[1][1].expectKind(nnkIntLit) # static enum are ints in the VM
  83. $Algebra(T[1][1].intVal)
  84. else: # typedesc[bls12381_fp] alias as used for C exports
  85. let T1 = getTypeInst(T[1].getImpl()[2])
  86. if T1.kind != nnkBracketExpr or
  87. T1[1].kind != nnkIntLit:
  88. echo T.repr()
  89. echo T1.repr()
  90. echo getTypeInst(T1).treerepr()
  91. error "getTypeInst didn't return the full instantiation." &
  92. " Dealing with types in macros is hard, complain at https://github.com/nim-lang/RFCs/issues/44"
  93. $Algebra(T1[1].intVal)
  94. let curve_fp = bindSym(curve & "_Fp_" & property)
  95. let curve_fr = bindSym(curve & "_Fr_" & property)
  96. result = quote do:
  97. when `ff` is Fp:
  98. `curve_fp`
  99. elif `ff` is Fr:
  100. `curve_fr`
  101. else:
  102. {.error: "Unreachable, received type: " & $`ff`.}
  103. # --------------------------------------------------------------
  104. template matchingBigInt*(Name: static Algebra): untyped =
  105. ## BigInt type necessary to store the prime field Fp
  106. # Workaround: https://github.com/nim-lang/Nim/issues/16774
  107. # as we cannot do array accesses in type section.
  108. # Due to generic sandwiches, it must be exported.
  109. BigInt[CurveBitWidth[Name]]
  110. type
  111. Fp*[Name: static Algebra] = object
  112. mres*: matchingBigInt(Name)
  113. macro getMontyOne*(ff: type Fp): untyped =
  114. ## Get one in Montgomery representation (i.e. R mod P)
  115. result = bindConstant(ff, "MontyOne")
  116. func getOne*(T: type Fp): T {.noInit, inline.} =
  117. result = cast[ptr T](unsafeAddr getMontyOne(T))[]
  118. # --------------------------------------------------------------
  119. proc foo(T: Fp) =
  120. discard T
  121. let a = Fp[BN254_Snarks].getOne()
  122. foo(a) # oops this was a leftover that broke the bisect.