49
49
50
50
from six import python_2_unicode_compatible
51
51
from . import numbertheory
52
- from ._rwlock import RWLock
53
52
54
53
55
54
@python_2_unicode_compatible
@@ -167,88 +166,64 @@ def __init__(self, curve, x, y, z, order=None, generator=False):
167
166
cause to precompute multiplication table generation for it
168
167
"""
169
168
self .__curve = curve
170
- # since it's generally better (faster) to use scaled points vs unscaled
171
- # ones, use writer-biased RWLock for locking:
172
- self ._update_lock = RWLock ()
173
169
if GMPY : # pragma: no branch
174
- self .__x = mpz (x )
175
- self .__y = mpz (y )
176
- self .__z = mpz (z )
170
+ self .__coords = (mpz (x ), mpz (y ), mpz (z ))
177
171
self .__order = order and mpz (order )
178
172
else : # pragma: no branch
179
- self .__x = x
180
- self .__y = y
181
- self .__z = z
173
+ self .__coords = (x , y , z )
182
174
self .__order = order
183
175
self .__generator = generator
184
176
self .__precompute = []
185
177
186
178
def _maybe_precompute (self ):
187
- if self .__generator :
188
- # since we lack promotion of read-locks to write-locks, we do a
189
- # "acquire-read-lock, check, acquire-write-lock plus recheck" cycle
190
- try :
191
- self ._update_lock .reader_acquire ()
192
- if self .__precompute :
193
- return
194
- finally :
195
- self ._update_lock .reader_release ()
196
-
197
- try :
198
- self ._update_lock .writer_acquire ()
199
- if self .__precompute :
200
- return
201
- order = self .__order
202
- assert order
203
- i = 1
204
- order *= 2
205
- doubler = PointJacobi (
206
- self .__curve , self .__x , self .__y , self .__z , order
207
- )
208
- order *= 2
209
- self .__precompute .append ((doubler .x (), doubler .y ()))
210
-
211
- while i < order :
212
- i *= 2
213
- doubler = doubler .double ().scale ()
214
- self .__precompute .append ((doubler .x (), doubler .y ()))
215
-
216
- finally :
217
- self ._update_lock .writer_release ()
179
+ if not self .__generator or self .__precompute :
180
+ return
181
+
182
+ # since this code will execute just once, and it's fully deterministic,
183
+ # depend on atomicity of the last assignment to switch from empty
184
+ # self.__precompute to filled one and just ignore the unlikely
185
+ # situation when two threads execute it at the same time (as it won't
186
+ # lead to inconsistent __precompute)
187
+ order = self .__order
188
+ assert order
189
+ precompute = []
190
+ i = 1
191
+ order *= 2
192
+ coord_x , coord_y , coord_z = self .__coords
193
+ doubler = PointJacobi (self .__curve , coord_x , coord_y , coord_z , order )
194
+ order *= 2
195
+ precompute .append ((doubler .x (), doubler .y ()))
196
+
197
+ while i < order :
198
+ i *= 2
199
+ doubler = doubler .double ().scale ()
200
+ precompute .append ((doubler .x (), doubler .y ()))
201
+
202
+ self .__precompute = precompute
218
203
219
204
def __getstate__ (self ):
220
- try :
221
- self ._update_lock .reader_acquire ()
222
- state = self .__dict__ .copy ()
223
- finally :
224
- self ._update_lock .reader_release ()
225
- del state ["_update_lock" ]
205
+ # while this code can execute at the same time as _maybe_precompute()
206
+ # is updating the __precompute or scale() is updating the __coords,
207
+ # there is no requirement for consistency between __coords and
208
+ # __precompute
209
+ state = self .__dict__ .copy ()
226
210
return state
227
211
228
212
def __setstate__ (self , state ):
229
213
self .__dict__ .update (state )
230
- self ._update_lock = RWLock ()
231
214
232
215
def __eq__ (self , other ):
233
216
"""Compare for equality two points with each-other.
234
217
235
218
Note: only points that lay on the same curve can be equal.
236
219
"""
237
- try :
238
- self ._update_lock .reader_acquire ()
239
- if other is INFINITY :
240
- return not self .__y or not self .__z
241
- x1 , y1 , z1 = self .__x , self .__y , self .__z
242
- finally :
243
- self ._update_lock .reader_release ()
220
+ x1 , y1 , z1 = self .__coords
221
+ if other is INFINITY :
222
+ return not y1 or not z1
244
223
if isinstance (other , Point ):
245
224
x2 , y2 , z2 = other .x (), other .y (), 1
246
225
elif isinstance (other , PointJacobi ):
247
- try :
248
- other ._update_lock .reader_acquire ()
249
- x2 , y2 , z2 = other .__x , other .__y , other .__z
250
- finally :
251
- other ._update_lock .reader_release ()
226
+ x2 , y2 , z2 = other .__coords
252
227
else :
253
228
return NotImplemented
254
229
if self .__curve != other .curve ():
@@ -289,14 +264,9 @@ def x(self):
289
264
call x() and y() on the returned instance. Or call `scale()`
290
265
and then x() and y() on the returned instance.
291
266
"""
292
- try :
293
- self ._update_lock .reader_acquire ()
294
- if self .__z == 1 :
295
- return self .__x
296
- x = self .__x
297
- z = self .__z
298
- finally :
299
- self ._update_lock .reader_release ()
267
+ x , _ , z = self .__coords
268
+ if z == 1 :
269
+ return x
300
270
p = self .__curve .p ()
301
271
z = numbertheory .inverse_mod (z , p )
302
272
return x * z ** 2 % p
@@ -310,14 +280,9 @@ def y(self):
310
280
call x() and y() on the returned instance. Or call `scale()`
311
281
and then x() and y() on the returned instance.
312
282
"""
313
- try :
314
- self ._update_lock .reader_acquire ()
315
- if self .__z == 1 :
316
- return self .__y
317
- y = self .__y
318
- z = self .__z
319
- finally :
320
- self ._update_lock .reader_release ()
283
+ _ , y , z = self .__coords
284
+ if z == 1 :
285
+ return y
321
286
p = self .__curve .p ()
322
287
z = numbertheory .inverse_mod (z , p )
323
288
return y * z ** 3 % p
@@ -328,37 +293,28 @@ def scale(self):
328
293
329
294
Modifies point in place, returns self.
330
295
"""
331
- try :
332
- self ._update_lock .reader_acquire ()
333
- if self .__z == 1 :
334
- return self
335
- finally :
336
- self ._update_lock .reader_release ()
337
-
338
- try :
339
- self ._update_lock .writer_acquire ()
340
- # scaling already scaled point is safe (as inverse of 1 is 1) and
341
- # quick so we don't need to optimise for the unlikely event when
342
- # two threads hit the lock at the same time
343
- p = self .__curve .p ()
344
- z_inv = numbertheory .inverse_mod (self .__z , p )
345
- zz_inv = z_inv * z_inv % p
346
- self .__x = self .__x * zz_inv % p
347
- self .__y = self .__y * zz_inv * z_inv % p
348
- # we are setting the z last so that the check above will return
349
- # true only after all values were already updated
350
- self .__z = 1
351
- finally :
352
- self ._update_lock .writer_release ()
296
+ x , y , z = self .__coords
297
+ if z == 1 :
298
+ return self
299
+
300
+ # scaling is deterministic, so even if two threads execute the below
301
+ # code at the same time, they will set __coords to the same value
302
+ p = self .__curve .p ()
303
+ z_inv = numbertheory .inverse_mod (z , p )
304
+ zz_inv = z_inv * z_inv % p
305
+ x = x * zz_inv % p
306
+ y = y * zz_inv * z_inv % p
307
+ self .__coords = (x , y , 1 )
353
308
return self
354
309
355
310
def to_affine (self ):
356
311
"""Return point in affine form."""
357
- if not self .__y or not self .__z :
312
+ _ , y , z = self .__coords
313
+ if not y or not z :
358
314
return INFINITY
359
315
self .scale ()
360
- # after point is scaled, it's immutable, so no need to perform locking
361
- return Point (self .__curve , self . __x , self . __y , self .__order )
316
+ x , y , z = self . __coords
317
+ return Point (self .__curve , x , y , self .__order )
362
318
363
319
@staticmethod
364
320
def from_affine (point , generator = False ):
@@ -423,17 +379,13 @@ def _double(self, X1, Y1, Z1, p, a):
423
379
424
380
def double (self ):
425
381
"""Add a point to itself."""
426
- if not self .__y :
382
+ X1 , Y1 , Z1 = self .__coords
383
+
384
+ if not Y1 :
427
385
return INFINITY
428
386
429
387
p , a = self .__curve .p (), self .__curve .a ()
430
388
431
- try :
432
- self ._update_lock .reader_acquire ()
433
- X1 , Y1 , Z1 = self .__x , self .__y , self .__z
434
- finally :
435
- self ._update_lock .reader_release ()
436
-
437
389
X3 , Y3 , Z3 = self ._double (X1 , Y1 , Z1 , p , a )
438
390
439
391
if not Y3 or not Z3 :
@@ -546,16 +498,9 @@ def __add__(self, other):
546
498
raise ValueError ("The other point is on different curve" )
547
499
548
500
p = self .__curve .p ()
549
- try :
550
- self ._update_lock .reader_acquire ()
551
- X1 , Y1 , Z1 = self .__x , self .__y , self .__z
552
- finally :
553
- self ._update_lock .reader_release ()
554
- try :
555
- other ._update_lock .reader_acquire ()
556
- X2 , Y2 , Z2 = other .__x , other .__y , other .__z
557
- finally :
558
- other ._update_lock .reader_release ()
501
+ X1 , Y1 , Z1 = self .__coords
502
+ X2 , Y2 , Z2 = other .__coords
503
+
559
504
X3 , Y3 , Z3 = self ._add (X1 , Y1 , Z1 , X2 , Y2 , Z2 , p )
560
505
561
506
if not Y3 or not Z3 :
@@ -603,7 +548,7 @@ def _naf(mult):
603
548
604
549
def __mul__ (self , other ):
605
550
"""Multiply point by an integer."""
606
- if not self .__y or not other :
551
+ if not self .__coords [ 1 ] or not other :
607
552
return INFINITY
608
553
if other == 1 :
609
554
return self
@@ -615,8 +560,7 @@ def __mul__(self, other):
615
560
return self ._mul_precompute (other )
616
561
617
562
self = self .scale ()
618
- # once scaled, point is immutable, not need to lock
619
- X2 , Y2 = self .__x , self .__y
563
+ X2 , Y2 , _ = self .__coords
620
564
X3 , Y3 , Z3 = 0 , 0 , 1
621
565
p , a = self .__curve .p (), self .__curve .a ()
622
566
_double = self ._double
@@ -664,11 +608,10 @@ def mul_add(self, self_mul, other, other_mul):
664
608
665
609
# as we have 6 unique points to work with, we can't scale all of them,
666
610
# but do scale the ones that are used most often
667
- # (post scale() points are immutable so no need for locking)
668
611
self .scale ()
669
- X1 , Y1 , Z1 = self .__x , self . __y , self . __z
612
+ X1 , Y1 , Z1 = self .__coords
670
613
other .scale ()
671
- X2 , Y2 , Z2 = other .__x , other . __y , other . __z
614
+ X2 , Y2 , Z2 = other .__coords
672
615
673
616
_double = self ._double
674
617
_add = self ._add
@@ -736,13 +679,8 @@ def mul_add(self, self_mul, other, other_mul):
736
679
737
680
def __neg__ (self ):
738
681
"""Return negated point."""
739
- try :
740
- self ._update_lock .reader_acquire ()
741
- return PointJacobi (
742
- self .__curve , self .__x , - self .__y , self .__z , self .__order
743
- )
744
- finally :
745
- self ._update_lock .reader_release ()
682
+ x , y , z = self .__coords
683
+ return PointJacobi (self .__curve , x , - y , z , self .__order )
746
684
747
685
748
686
class Point (object ):
0 commit comments