Skip to content

Commit d4fb58a

Browse files
theStacksipa
andcommitted
test: EC: optimize scalar multiplication of G by using lookup table
On my machine, this speeds up the functional test feature_taproot.py by a factor of >1.66x (runtime decrease from 1m16.587s to 45.334s). Co-authored-by: Pieter Wuille <[email protected]>
1 parent 1830dd8 commit d4fb58a

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

test/functional/test_framework/secp256k1.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ def mul(*aps):
226226

227227
def __rmul__(self, a):
228228
"""Multiply an integer with a group element."""
229+
if self == G:
230+
return FAST_G.mul(a)
229231
return GE.mul((a, self))
230232

231233
def __neg__(self):
@@ -312,3 +314,33 @@ def __repr__(self):
312314

313315
# The secp256k1 generator point
314316
G = GE.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798)
317+
318+
319+
class FastGEMul:
320+
"""Table for fast multiplication with a constant group element.
321+
322+
Speed up scalar multiplication with a fixed point P by using a precomputed lookup table with
323+
its powers of 2:
324+
325+
table = [P, 2*P, 4*P, (2^3)*P, (2^4)*P, ..., (2^255)*P]
326+
327+
During multiplication, the points corresponding to each bit set in the scalar are added up,
328+
i.e. on average ~128 point additions take place.
329+
"""
330+
331+
def __init__(self, p):
332+
self.table = [p] # table[i] = (2^i) * p
333+
for _ in range(255):
334+
p = p + p
335+
self.table.append(p)
336+
337+
def mul(self, a):
338+
result = GE()
339+
a = a % GE.ORDER
340+
for bit in range(a.bit_length()):
341+
if a & (1 << bit):
342+
result += self.table[bit]
343+
return result
344+
345+
# Precomputed table with multiples of G for fast multiplication
346+
FAST_G = FastGEMul(G)

0 commit comments

Comments
 (0)