dlog.sage 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. # Initialize an elliptic curve
  2. p = 115792089237316195423570985008687907853269984665640564039457584007908834671663
  3. r = 115792089237316195423570985008687907852837564279074904382605163141518161494337
  4. Fp = GF(p) # Base Field
  5. Fr = GF(r) # Scalar Field
  6. A = 0
  7. B = 7
  8. E = EllipticCurve(GF(p), [A,B])
  9. assert(E.cardinality() == r)
  10. K.<x> = PolynomialRing(Fp, implementation="generic")
  11. L.<y> = PolynomialRing(K, implementation="generic")
  12. M.<z> = L[]
  13. eqn = y^2 - x^3 - A * x - B
  14. # Returns line passing through points, works for all points and returns 1 for O + O = O
  15. def line(A, B):
  16. if A == 0 and B == 0:
  17. return 1
  18. else:
  19. [a, b, c] = Matrix([A, B, -(A+B)]).transpose().kernel().basis()[0]
  20. return a*x + b*y + c
  21. def dlog(D):
  22. # Derivative via partials
  23. Dx = D.differentiate(x)
  24. Dy = D.differentiate(y)
  25. Dz = Dx + Dy * ((3*x^2 + A) / (2*y))
  26. assert D != 0
  27. return Dz/D
  28. def dlog_alt(D):
  29. Dx = D.differentiate(x)
  30. Dy = D.differentiate(y)
  31. #Dz = Dx + Dy * ((3*x^2 + A) / (2*y))
  32. # Normally we calculate:
  33. # Dz/D
  34. # Due to a bug in sage, we will make the denominator D
  35. # solely an equation in x by taking its norm.
  36. # Denominator = V · V'
  37. V = 2*y * D
  38. # 2y Dz
  39. Dz_numer = (2*y*Dx + Dy * (3*x^2 + A) * V(y=-y)).mod(eqn)
  40. # Change denominator to the norm
  41. D_denom = (V * V(y=-y)).mod(eqn)
  42. # This calculation is quite slow...
  43. print("Calculating Dlog = Dz/D")
  44. return Dz_numer / D_denom
  45. # Accepts arbitrary list of points, including duplicates and inverses, and constructs function
  46. # intersecting exactly those points if they form a principal divisor (i.e. sum to zero).
  47. def construct_function(Ps):
  48. # List of intermediate sums/principal divisors, removes 0
  49. xs = [(P, line(P, -P)) for P in Ps if P != 0]
  50. while len(xs) != 1:
  51. assert(sum(P for (P, _) in xs) == 0)
  52. xs2 = []
  53. # Carry extra point forward
  54. if mod(len(xs), 2) == 1:
  55. x0 = xs[0]
  56. xs = xs[1:]
  57. else:
  58. x0 = None
  59. # Combine the functions for all pairs
  60. for n in range(0, floor(len(xs)/2)):
  61. (A, aNum) = xs[2*n]
  62. (B, bNum) = xs[2*n+1]
  63. # Divide out intermediate (P, -P) factors
  64. num = L((aNum * bNum * line(A, B)).mod(eqn))
  65. den = line(A, -A) * line(B, -B)
  66. D = num / K(den)
  67. # Add new element
  68. xs2.append((A+B, D))
  69. if x0 != None:
  70. xs2.append(x0)
  71. xs = xs2
  72. assert(xs[0][0] == 0)
  73. # Normalize, might fail but negl probability for random points. Must be done for zkps
  74. # although free to use any coefficient
  75. return D / D(x=0, y=0)
  76. P0 = E.random_element()
  77. P1 = E.random_element()
  78. P2 = E.random_element()
  79. Q = -int(Fr(5)^-1) * (P0 + 2*P1 + 3*P2)
  80. assert P0 + 2*P1 + 3*P2 + 5*Q == 0
  81. def div_add(div_f, div_g):
  82. div = div_f.copy()
  83. for P, n in div_g.items():
  84. if P in div:
  85. div[P] += n
  86. else:
  87. div[P] = n
  88. div = dict((P, n) for P, n in div.items() if n != 0)
  89. return div
  90. def div_invert(div):
  91. return dict((P, -n) for P, n in div.items())
  92. def div_sub(div_f, div_g):
  93. inv_div_g = div_invert(div_g)
  94. return div_add(div_f, inv_div_g)
  95. # 2[P₂] + [-2P₂] - 3[∞]
  96. f1 = line(P2, P2)
  97. D1 = {"P2": 2, "-2P2": 1, "∞": -3}
  98. # 2[P₁] + [-2P₁] - 3[∞]
  99. f2 = line(P1, P1)
  100. D2 = {"P1": 2, "-2P1": 1, "∞": -3}
  101. # [P₂] + [-P₂] - 2[∞]
  102. f3 = line(P2, -P2)
  103. D3 = {"P2": 1, "-P2": 1, "∞": -2}
  104. # [P₀] + [-P₀] - 2[∞]
  105. f4 = line(P0, -P0)
  106. D4 = {"P0": 1, "-P0": 1, "∞": -2}
  107. # (2[P₂] + [-2P₂] - 3[∞]
  108. # + 2[P₁] + [-2P₁] - 3[∞]
  109. # + [P₂] + [-P₂] - 2[∞]
  110. # + [P₀] + [-P₀] - 2[∞])
  111. # =
  112. # [P₀] + 2[P₁] + 3[P₂] + [-P₀] + [-2P₁] + [-2P₂] + [-P₂] - 10[∞]
  113. f5 = f1*f2*f3*f4
  114. D5 = div_add(div_add(D1, D2), div_add(D3, D4))
  115. assert D5 == {
  116. "P0": 1,
  117. "P1": 2,
  118. "P2": 3,
  119. "-P0": 1,
  120. "-2P1": 1,
  121. "-2P2": 1,
  122. "-P2": 1,
  123. "∞": -10
  124. }
  125. # [-2P₂] + [-2P₁] + [2(P₁ + P₂)] - 3[∞]
  126. f6 = line(-2*P2, -2*P1)
  127. D6 = {"-2P2": 1, "-2P1": 1, "2P1 + 2P2": 1, "∞": -3}
  128. # [-P₂] + [-P₀] + [P₀ + P₂] - 3[∞]
  129. f7 = line(-P2, -P0)
  130. D7 = {"-P2": 1, "-P0": 1, "P0 + P2": 1, "∞": -3}
  131. # ([P₀] + 2[P₁] + 3[P₂] + [-P₀] + [-2P₁] + [-2P₂] + [-P₂] - 10[∞]
  132. # - [-2P₂] - [-2P₁] - [2(P₁ + P₂)] + 3[∞]
  133. # - [-P₂] - [-P₀] - [P₀ + P₂] + 3[∞])
  134. # =
  135. # [P₀] + 2[P₁] + 3[P₂] - [2(P₁ + P₂)] - [P₀ + P₂] - 4[∞]
  136. f8 = f5/(f6*f7)
  137. D8 = div_sub(D5, div_add(D6, D7))
  138. assert D8 == {
  139. "P0": 1,
  140. "P1": 2,
  141. "P2": 3,
  142. "2P1 + 2P2": -1,
  143. "P0 + P2": -1,
  144. "∞": -4
  145. }
  146. # [P₀ + P₂] + [2(P₁ + P₂)] + [-(P₀ + 2P₁ + 3P₂)] - 3[∞]
  147. f9 = line(P0 + P2, 2*(P1 + P2))
  148. D9 = {"P0 + P2": 1, "2P1 + 2P2": 1, "5Q": 1, "∞": -3}
  149. # ([P₀] + 2[P₁] + 3[P₂] - [2(P₁ + P₂)] - [P₀ + P₂] - 4[∞]
  150. # + [P₀ + P₂] + [2(P₁ + P₂)] + [-(P₀ + 2P₁ + 3P₂)] - 3[∞])
  151. # =
  152. # [P₀] + 2[P₁] + 3[P₂] + [-(P₀ + 2P₁ + 3P₂)] - 7[∞]
  153. # = [P₀] + 2[P₁] + 3[P₂] + [5Q] - 7[∞]
  154. f10 = f8*f9
  155. D10 = div_add(D8, D9)
  156. assert D10 == {
  157. "P0": 1,
  158. "P1": 2,
  159. "P2": 3,
  160. "5Q": 1,
  161. "∞": -7
  162. }
  163. # Now construct 5[Q]
  164. # 2[Q] + [-2Q] - 3[∞]
  165. f11 = line(Q, Q)
  166. D11 = {"Q": 2, "-2Q": 1, "∞": -3}
  167. # [-2Q] + [2Q] - 2[∞]
  168. f12 = line(-2*Q, 2*Q)
  169. D12 = {"-2Q": 1, "2Q": 1, "∞": -2}
  170. # (2[Q] + [-2Q] - 3[∞]) - ([-2Q] + [2Q] - 2[∞])
  171. # ==
  172. # 2[Q] - [2Q] - [∞]
  173. f13 = f11/f12
  174. D13 = div_sub(D11, D12)
  175. assert D13 == {
  176. "Q": 2,
  177. "2Q": -1,
  178. "∞": -1
  179. }
  180. # multiply by 3
  181. # 6[Q] - 3[2Q] - 3[∞]
  182. f14 = f13*f13*f13
  183. D14 = div_add(div_add(D13, D13), D13)
  184. assert D14 == {
  185. "Q": 6,
  186. "2Q": -3,
  187. "∞": -3
  188. }
  189. # 2[2Q] + [-4Q] - 3[∞]
  190. f15 = line(2*Q, 2*Q)
  191. D15 = {"2Q": 2, "-4Q": 1, "∞": -3}
  192. # (6[Q] - 3[2Q] - 3[∞]) + (2[2Q] + [-4Q] - 3[∞])
  193. # ==
  194. # 6[Q] - [2Q] + [-4Q] - 6[∞]
  195. f16 = f14*f15
  196. D16 = div_add(D14, D15)
  197. assert D16 == {
  198. "Q": 6,
  199. "2Q": -1,
  200. "-4Q": 1,
  201. "∞": -6
  202. }
  203. # [2Q] + [-2Q] - 2[∞]
  204. f17 = line(2*Q, -2*Q)
  205. D17 = {"2Q": 1, "-2Q": 1, "∞": -2}
  206. # (6[Q] - [2Q] + [-4Q] - 6[∞]) + ([2Q] + [-2Q] - 2[∞])
  207. # ==
  208. # 6[Q] + [-2Q] + [-4Q] - 8[∞]
  209. f18 = f16*f17
  210. D18 = div_add(D16, D17)
  211. assert D18 == {
  212. "Q": 6,
  213. "-2Q": 1,
  214. "-4Q": 1,
  215. "∞": -8
  216. }
  217. # [-2Q] + [-4Q] + [6Q] - 3[∞]
  218. f19 = line(-2*Q, -4*Q)
  219. D19 = {"-2Q": 1, "-4Q": 1, "6Q": 1, "∞": -3}
  220. # (6[Q] + [-2Q] + [-4Q] - 8[∞]) - ([-2Q] + [-4Q] + [6Q] - 3[∞])
  221. # ==
  222. # 6[Q] - [6Q] - 5[∞]
  223. f20 = f18/f19
  224. D20 = div_sub(D18, D19)
  225. assert D20 == {
  226. "Q": 6,
  227. "6Q": -1,
  228. "∞": -5
  229. }
  230. # [6Q] + [-6Q] - 2[∞]
  231. f21 = line(6*Q, -6*Q)
  232. D21 = {"6Q": 1, "-6Q": 1, "∞": -2}
  233. # (6[Q] - [6Q] - 5[∞]) + ([6Q] + [-6Q] - 2[∞])
  234. # ==
  235. # 6[Q] + [-6Q] - 7[∞]
  236. f22 = f20*f21
  237. D22 = div_add(D20, D21)
  238. assert D22 == {"Q": 6, "-6Q": 1, "∞": -7}
  239. # [Q] + [-6Q] + [5Q] - 3[∞]
  240. f23 = line(Q, -6*Q)
  241. D23 = {"Q": 1, "-6Q": 1, "5Q": 1, "∞": -3}
  242. # (6[Q] + [-6Q] - 7[∞]) - ([Q] + [-6Q] + [5Q] - 3[∞])
  243. # ==
  244. # 5[Q] - [5Q] - 4[∞]
  245. f24 = f22/f23
  246. D24 = div_sub(D22, D23)
  247. assert D24 == {"Q": 5, "5Q": -1, "∞": -4}
  248. # Now combine the result
  249. f = f10*f24
  250. D = div_add(D10, D24)
  251. assert D == {
  252. "P0": 1,
  253. "P1": 2,
  254. "P2": 3,
  255. "Q": 5,
  256. "∞": -11
  257. }
  258. f_numer = f.numerator().mod(eqn)
  259. f_denom = f.denominator().mod(eqn)
  260. # ZeroDivisionError
  261. #DLog = dlog(f_numer)
  262. assert f(x=P0[0], y=P0[1]) == 0
  263. assert f(x=P1[0], y=P1[1]) == 0
  264. assert f(x=P2[0], y=P2[1]) == 0
  265. # Need to modify f because this is 0/0
  266. #assert f(x=Q[0], y=Q[1]) == 0
  267. f_denom *= f_denom(y=-y)
  268. f_denom = K(f_denom.mod(eqn))
  269. f_numer *= f_denom(y=-y)
  270. f_numer = f_numer.mod(eqn)
  271. f = f_numer / f_denom
  272. print("Created f such that div(f) = D")
  273. #Ps = [P0] + 2*[P1] + 3*[P2] + 5*[Q]
  274. #D = construct_function(Ps)
  275. #
  276. #assert D(x=P0[0], y=P0[1]) == 0
  277. #assert D(x=P1[0], y=P1[1]) == 0
  278. #assert D(x=P2[0], y=P2[1]) == 0
  279. #assert D(x=Q[0], y=Q[1]) == 0
  280. # This will fail due to a bug in sage:
  281. #DLog = dlog(D)
  282. # ZeroDivisionError
  283. #DLog = dlog_alt(f)
  284. print("Random A₀, A₁")
  285. [A0, A1] = [E.random_element() for _ in range(2)]
  286. A2 = -(A0 + A1)
  287. A0x, A0y = A0.xy()
  288. A1x, A1y = A1.xy()
  289. A2x, A2y = A2.xy()
  290. λ = (A1y - A0y) / (A1x - A0x)
  291. μ = A1y - λ*A1x
  292. assert A2y - λ*A2x - μ == 0
  293. f_a = K(f(y=0))
  294. f_b = K((f - f_a)(y=1))
  295. assert f_a + y*f_b == f
  296. #deg_f = f_b.degree() + 1
  297. f_A = 1
  298. for Ai in [A0, A1, A2]:
  299. Aix, Aiy = Ai.xy()
  300. f_A *= f(x=Aix, y=Aiy)
  301. print(f_A)
  302. g = y - λ*x - μ
  303. g_P = 1
  304. for Pi, v in [(P0, 1), (P1, 2), (P2, 3), (Q, 5)]:
  305. Pix, Piy = Pi.xy()
  306. g_P *= -g(x=Pix, y=Piy)^v
  307. print(g_P)