4848
4949from  six  import  python_2_unicode_compatible 
5050from  . import  numbertheory 
51+ from  ._rwlock  import  RWLock 
5152
5253
5354@python_2_unicode_compatible  
@@ -145,6 +146,9 @@ def __init__(self, curve, x, y, z, order=None, generator=False):
145146        cause to precompute multiplication table for it 
146147      """ 
147148      self .__curve  =  curve 
149+       # since it's generally better (faster) to use scaled points vs unscaled 
150+       # ones, use writer-biased RWLock for locking: 
151+       self ._scale_lock  =  RWLock ()
148152      if  GMPY :
149153          self .__x  =  mpz (x )
150154          self .__y  =  mpz (y )
@@ -171,19 +175,25 @@ def __init__(self, curve, x, y, z, order=None, generator=False):
171175
172176  def  __eq__ (self , other ):
173177      """Compare two points with each-other.""" 
174-       if  (not  self .__y  or  not  self .__z ) and  other  is  INFINITY :
175-           return  True 
176-       if  self .__y  and  self .__z  and  other  is  INFINITY :
177-           return  False 
178+       try :
179+           self ._scale_lock .reader_acquire ()
180+           if  other  is  INFINITY :
181+               return  not  self .__y  or  not  self .__z 
182+           x1 , y1 , z1  =  self .__x , self .__y , self .__z 
183+       finally :
184+           self ._scale_lock .reader_release ()
178185      if  isinstance (other , Point ):
179186          x2 , y2 , z2  =  other .x (), other .y (), 1 
180187      elif  isinstance (other , PointJacobi ):
181-           x2 , y2 , z2  =  other .__x , other .__y , other .__z 
188+           try :
189+               other ._scale_lock .reader_acquire ()
190+               x2 , y2 , z2  =  other .__x , other .__y , other .__z 
191+           finally :
192+               other ._scale_lock .reader_release ()
182193      else :
183194          return  NotImplemented 
184195      if  self .__curve  !=  other .curve ():
185196          return  False 
186-       x1 , y1 , z1  =  self .__x , self .__y , self .__z 
187197      p  =  self .__curve .p ()
188198
189199      zz1  =  z1  *  z1  %  p 
@@ -214,11 +224,17 @@ def x(self):
214224      call x() and y() on the returned instance. Or call `scale()` 
215225      and then x() and y() on the returned instance. 
216226      """ 
217-       if  self .__z  ==  1 :
218-           return  self .__x 
227+       try :
228+           self ._scale_lock .reader_acquire ()
229+           if  self .__z  ==  1 :
230+               return  self .__x 
231+           x  =  self .__x 
232+           z  =  self .__z 
233+       finally :
234+           self ._scale_lock .reader_release ()
219235      p  =  self .__curve .p ()
220-       z  =  numbertheory .inverse_mod (self . __z , p )
221-       return  self . __x  *  z ** 2  %  p 
236+       z  =  numbertheory .inverse_mod (z , p )
237+       return  x  *  z ** 2  %  p 
222238
223239  def  y (self ):
224240      """ 
@@ -229,31 +245,54 @@ def y(self):
229245      call x() and y() on the returned instance. Or call `scale()` 
230246      and then x() and y() on the returned instance. 
231247      """ 
232-       if  self .__z  ==  1 :
233-           return  self .__y 
248+       try :
249+           self ._scale_lock .reader_acquire ()
250+           if  self .__z  ==  1 :
251+               return  self .__y 
252+           y  =  self .__y 
253+           z  =  self .__z 
254+       finally :
255+           self ._scale_lock .reader_release ()
234256      p  =  self .__curve .p ()
235-       z  =  numbertheory .inverse_mod (self . __z , p )
236-       return  self . __y  *  z ** 3  %  p 
257+       z  =  numbertheory .inverse_mod (z , p )
258+       return  y  *  z ** 3  %  p 
237259
238260  def  scale (self ):
239261      """ 
240262      Return point scaled so that z == 1. 
241263
242264      Modifies point in place, returns self. 
243265      """ 
244-       p  =  self .__curve .p ()
245-       z_inv  =  numbertheory .inverse_mod (self .__z , p )
246-       zz_inv  =  z_inv  *  z_inv  %  p 
247-       self .__x  =  self .__x  *  zz_inv  %  p 
248-       self .__y  =  self .__y  *  zz_inv  *  z_inv  %  p 
249-       self .__z  =  1 
266+       try :
267+           self ._scale_lock .reader_acquire ()
268+           if  self .__z  ==  1 :
269+               return  self 
270+       finally :
271+           self ._scale_lock .reader_release ()
272+ 
273+       try :
274+           self ._scale_lock .writer_acquire ()
275+           # scaling already scaled point is safe (as inverse of 1 is 1) and 
276+           # quick so we don't need to optimise for the unlikely event when 
277+           # two threads hit the lock at the same time 
278+           p  =  self .__curve .p ()
279+           z_inv  =  numbertheory .inverse_mod (self .__z , p )
280+           zz_inv  =  z_inv  *  z_inv  %  p 
281+           self .__x  =  self .__x  *  zz_inv  %  p 
282+           self .__y  =  self .__y  *  zz_inv  *  z_inv  %  p 
283+           # we are setting the z last so that the check above will return true 
284+           # only after all values were already updated 
285+           self .__z  =  1 
286+       finally :
287+           self ._scale_lock .writer_release ()
250288      return  self 
251289
252290  def  to_affine (self ):
253291      """Return point in affine form.""" 
254292      if  not  self .__y  or  not  self .__z :
255293          return  INFINITY 
256294      self .scale ()
295+       # after point is scaled, it's immutable, so no need to perform locking 
257296      return  Point (self .__curve , self .__x ,
258297                   self .__y , self .__order )
259298
@@ -323,7 +362,11 @@ def double(self):
323362
324363      p , a  =  self .__curve .p (), self .__curve .a ()
325364
326-       X1 , Y1 , Z1  =  self .__x , self .__y , self .__z 
365+       try :
366+           self ._scale_lock .reader_acquire ()
367+           X1 , Y1 , Z1  =  self .__x , self .__y , self .__z 
368+       finally :
369+           self ._scale_lock .reader_release ()
327370
328371      X3 , Y3 , Z3  =  self ._double (X1 , Y1 , Z1 , p , a )
329372
@@ -437,8 +480,16 @@ def __add__(self, other):
437480          raise  ValueError ("The other point is on different curve" )
438481
439482      p  =  self .__curve .p ()
440-       X1 , Y1 , Z1  =  self .__x , self .__y , self .__z 
441-       X2 , Y2 , Z2  =  other .__x , other .__y , other .__z 
483+       try :
484+           self ._scale_lock .reader_acquire ()
485+           X1 , Y1 , Z1  =  self .__x , self .__y , self .__z 
486+       finally :
487+           self ._scale_lock .reader_release ()
488+       try :
489+           other ._scale_lock .reader_acquire ()
490+           X2 , Y2 , Z2  =  other .__x , other .__y , other .__z 
491+       finally :
492+           other ._scale_lock .reader_release ()
442493      X3 , Y3 , Z3  =  self ._add (X1 , Y1 , Z1 , X2 , Y2 , Z2 , p )
443494
444495      if  not  Y3  or  not  Z3 :
@@ -497,6 +548,7 @@ def __mul__(self, other):
497548          return  self ._mul_precompute (other )
498549
499550      self  =  self .scale ()
551+       # once scaled, point is immutable, not need to lock 
500552      X2 , Y2  =  self .__x , self .__y 
501553      X3 , Y3 , Z3  =  0 , 0 , 1 
502554      p , a  =  self .__curve .p (), self .__curve .a ()
@@ -550,6 +602,7 @@ def mul_add(self, self_mul, other, other_mul):
550602      X3 , Y3 , Z3  =  0 , 0 , 1 
551603      p , a  =  self .__curve .p (), self .__curve .a ()
552604      self  =  self .scale ()
605+       # after scaling, point is immutable, no need for locking 
553606      X1 , Y1  =  self .__x , self .__y 
554607      other  =  other .scale ()
555608      X2 , Y2  =  other .__x , other .__y 
@@ -575,8 +628,12 @@ def mul_add(self, self_mul, other, other_mul):
575628
576629  def  __neg__ (self ):
577630      """Return negated point.""" 
578-       return  PointJacobi (self .__curve , self .__x , - self .__y , self .__z ,
579-                          self .__order )
631+       try :
632+           self ._scale_lock .reader_acquire ()
633+           return  PointJacobi (self .__curve , self .__x , - self .__y , self .__z ,
634+                              self .__order )
635+       finally :
636+           self ._scale_lock .reader_release ()
580637
581638
582639class  Point (object ):
0 commit comments