curve_tree.sage 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import hashlib
  2. from collections import namedtuple
  3. # Your Funds Are Safu
  4. p = [
  5. 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001,
  6. 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
  7. ]
  8. # Pallas, Vesta
  9. K = [GF(p_i) for p_i in p]
  10. E = [EllipticCurve(K_i, (0, 5)) for K_i in K]
  11. # Scalar fields
  12. Scalar = [K[1], K[0]]
  13. base_G = [E_i.gens()[0] for E_i in E]
  14. assert all(base_G_i.order() == p_i for p_i, base_G_i in zip(reversed(p), base_G))
  15. E1, E2 = 0, 1
  16. gens = [
  17. [E[E1].random_point() for _ in range(5)],
  18. [E[E2].random_point() for _ in range(5)],
  19. ]
  20. def hash_nodes(Ei, P1, P2, r):
  21. G1, G2, G3, G4, H = gens[Ei]
  22. (P1_x, P1_y), (P2_x, P2_y) = P1.xy(), P2.xy()
  23. v1G1 = int(P1_x) * G1
  24. v2G2 = int(P1_y) * G2
  25. v3G3 = int(P2_x) * G3
  26. v4G4 = int(P2_y) * G4
  27. rH = int(r) * H
  28. return v1G1 + v2G2 + v3G3 + v4G4 + rH
  29. def hash_point(Ei, P, b):
  30. G1, G2, G3, G4, H = gens[Ei]
  31. x, y = P.xy()
  32. return int(x)*G1 + int(y)*G2 + int(b)*H
  33. # You can ignore this particular impl.
  34. # Just some rough code to illustrate the main concept.
  35. # The proofs enforce these relations:
  36. #
  37. # σ ∈ {0, 1}
  38. # C = x1 G1 + y1 G2 + x2 G3 + y2 G4 + rH
  39. # Ĉ = x_i G1 + y_i G2 + bH
  40. #
  41. # where
  42. #
  43. # x_i = { x0 if σ = 0
  44. # { x1 if σ = 1
  45. #
  46. # y_i = { y0 if σ = 0
  47. # { y1 if σ = 1
  48. #
  49. # It is just a quick hackjob proof of concept and horribly inefficient
  50. load("curve_tree_proofs.sage")
  51. test_proof()
  52. # Our tree is a height of D=3
  53. def create_tree(C3):
  54. assert len(C3) == 2**3
  55. # j = 2
  56. C2 = []
  57. for i in range(4):
  58. C2_i = hash_nodes(E2, C3[2*i], C3[2*i + 1], 0)
  59. C2.append(C2_i)
  60. # j = 1
  61. C1 = []
  62. for i in range(2):
  63. C1_i = hash_nodes(E1, C2[2*i], C2[2*i + 1], 0)
  64. C1.append(C1_i)
  65. # j = 0 (root)
  66. C0 = hash_nodes(E2, C1[0], C1[1], 0)
  67. return C0
  68. def create_path(C3):
  69. # To make things easier, we assume that our coin is
  70. # always on the left hand side of the tree.
  71. X3 = C3[1]
  72. X2 = hash_nodes(E2, C3[2], C3[3], 0)
  73. X1 = hash_nodes(
  74. E1,
  75. hash_nodes(E2, C3[4], C3[5], 0),
  76. hash_nodes(E2, C3[6], C3[7], 0),
  77. 0
  78. )
  79. return (X3, X2, X1)
  80. def main():
  81. coins = [E[E1].random_point() for _ in range(2**3)]
  82. root = create_tree(coins)
  83. path = create_path(coins)
  84. # Test the path works
  85. X3, X2, X1 = path
  86. C3 = coins[0]
  87. C2 = hash_nodes(
  88. E2,
  89. C3,
  90. X3,
  91. 0
  92. )
  93. C1 = hash_nodes(
  94. E1,
  95. C2,
  96. X2,
  97. 0
  98. )
  99. C0 = hash_nodes(
  100. E2,
  101. C1,
  102. X1,
  103. 0
  104. )
  105. assert C0 == root
  106. # E1 point
  107. C3 = coins[0]
  108. Ĉ0 = root
  109. r0 = 0
  110. # Same as this:
  111. # Ĉ0 = hash_nodes(E1, C2, X2, 0)
  112. # j = 1
  113. b1 = int(Scalar[E2].random_element())
  114. Ĉ1 = hash_point(E2, C1, b1)
  115. C1_x, C1_y = C1.xy()
  116. X1_x, X1_y = X1.xy()
  117. proof1, public1 = make_proof(
  118. E2,
  119. ProofWitness(
  120. C1_x,
  121. C1_y,
  122. X1_x,
  123. X1_y,
  124. r0,
  125. b1,
  126. 0
  127. )
  128. )
  129. public1.C = Ĉ0
  130. public1.D = Ĉ1
  131. assert verify_proof(E2, proof1, public1)
  132. # j = 2
  133. # Now we know that Ĉ1 is the root of a new subtree
  134. # But Ĉ1 ∈ E2, whereas we need to produce a blinded
  135. # Ĉ1 ∈ E1.
  136. # The reason this system uses curve cycles is because
  137. # EC arithmetic is efficient to represent.
  138. # We skip this part so assume these next to lines are
  139. # part of the previous proof.
  140. r1 = int(Scalar[E1].random_element())
  141. Ĉ1 = hash_nodes(E1, C2, X2, r1)
  142. ################################
  143. b2 = int(Scalar[E1].random_element())
  144. Ĉ2 = hash_point(E1, C2, b2)
  145. C2_x, C2_y = C2.xy()
  146. X2_x, X2_y = X2.xy()
  147. proof2, public2 = make_proof(
  148. E1,
  149. ProofWitness(
  150. C2_x,
  151. C2_y,
  152. X2_x,
  153. X2_y,
  154. r1,
  155. b2,
  156. 0
  157. )
  158. )
  159. public2.C = Ĉ1
  160. public2.D = Ĉ2
  161. assert verify_proof(E1, proof2, public2)
  162. # j = 3
  163. # Same as before. We now have a randomized C2
  164. r2 = int(Scalar[E2].random_element())
  165. Ĉ2 = hash_nodes(E2, C3, X3, r2)
  166. #################################
  167. b3 = int(Scalar[E2].random_element())
  168. Ĉ3 = hash_point(E2, C3, b3)
  169. C3_x, C3_y = C3.xy()
  170. X3_x, X3_y = X3.xy()
  171. proof3, public3 = make_proof(
  172. E2,
  173. ProofWitness(
  174. C3_x,
  175. C3_y,
  176. X3_x,
  177. X3_y,
  178. r2,
  179. b3,
  180. 0
  181. )
  182. )
  183. public3.C = Ĉ2
  184. public3.D = Ĉ3
  185. assert verify_proof(E2, proof3, public3)
  186. # Now just unblind Ĉ3
  187. main()