From d4fb58ae8ae9772d025ead184ef8f2c0ea50df3e Mon Sep 17 00:00:00 2001 From: Sebastian Falbesoner Date: Sun, 2 Apr 2023 21:57:32 +0200 Subject: [PATCH] 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 --- test/functional/test_framework/secp256k1.py | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/functional/test_framework/secp256k1.py b/test/functional/test_framework/secp256k1.py index 62840976b18..2e9e419da5b 100644 --- a/test/functional/test_framework/secp256k1.py +++ b/test/functional/test_framework/secp256k1.py @@ -226,6 +226,8 @@ class GE: def __rmul__(self, a): """Multiply an integer with a group element.""" + if self == G: + return FAST_G.mul(a) return GE.mul((a, self)) def __neg__(self): @@ -312,3 +314,33 @@ class GE: # The secp256k1 generator point G = GE.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798) + + +class FastGEMul: + """Table for fast multiplication with a constant group element. + + Speed up scalar multiplication with a fixed point P by using a precomputed lookup table with + its powers of 2: + + table = [P, 2*P, 4*P, (2^3)*P, (2^4)*P, ..., (2^255)*P] + + During multiplication, the points corresponding to each bit set in the scalar are added up, + i.e. on average ~128 point additions take place. + """ + + def __init__(self, p): + self.table = [p] # table[i] = (2^i) * p + for _ in range(255): + p = p + p + self.table.append(p) + + def mul(self, a): + result = GE() + a = a % GE.ORDER + for bit in range(a.bit_length()): + if a & (1 << bit): + result += self.table[bit] + return result + +# Precomputed table with multiples of G for fast multiplication +FAST_G = FastGEMul(G)