From fae672c60cbe30448da743abbcfb12731adbceb7 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 12 Apr 2024 16:57:51 +0000 Subject: [PATCH] ecc: make libsecp256k1 "schnorrsig" module only required when used It would be simple to hard fail at import time if any of the interesting libsecp modules are missing, as it was done before this commit. However, some Linux distros (atm current ubuntu lts, 22.04) lack new enough libsecp. Also, for now, we don't use the schnorr APIs yet anyway. Until we start to rely on them more, it is feasible to only require them when they are used. I am hoping we will be able to revert this commit later though, to keep things simple. --- electrum/ecc.py | 15 ++++++++++++++- electrum/ecc_fast.py | 15 +++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/electrum/ecc.py b/electrum/ecc.py index 299f84365..b28759f76 100644 --- a/electrum/ecc.py +++ b/electrum/ecc.py @@ -35,7 +35,8 @@ from .util import bfh, assert_bytes, to_bytes, InvalidPassword, profiler, randra from .crypto import (sha256, sha256d, aes_encrypt_with_iv, aes_decrypt_with_iv, hmac_oneshot) from . import constants from .logging import get_logger -from .ecc_fast import _libsecp256k1, SECP256K1_EC_UNCOMPRESSED +from . import ecc_fast +from .ecc_fast import _libsecp256k1, SECP256K1_EC_UNCOMPRESSED, LibModuleMissing _logger = get_logger(__name__) @@ -251,6 +252,10 @@ class ECPubkey(object): def _to_libsecp256k1_xonly_pubkey_ptr(self): """pointer to `secp256k1_xonly_pubkey` C struct""" + if not ecc_fast.HAS_SCHNORR: + raise LibModuleMissing( + 'libsecp256k1 library found but it was built ' + 'without required modules (--enable-module-schnorrsig --enable-module-extrakeys)') pubkey = create_string_buffer(64) pk_bytes = self.get_public_key_bytes(compressed=True)[1:] ret = _libsecp256k1.secp256k1_xonly_pubkey_parse( @@ -359,6 +364,10 @@ class ECPubkey(object): assert len(sig64) == 64, len(sig64) assert isinstance(msg32, bytes), type(msg32) assert len(msg32) == 32, len(msg32) + if not ecc_fast.HAS_SCHNORR: + raise LibModuleMissing( + 'libsecp256k1 library found but it was built ' + 'without required modules (--enable-module-schnorrsig --enable-module-extrakeys)') msglen = 32 pubkey = self._to_libsecp256k1_xonly_pubkey_ptr() if 1 != _libsecp256k1.secp256k1_schnorrsig_verify(_libsecp256k1.ctx, sig64, msg32, msglen, pubkey): @@ -534,6 +543,10 @@ class ECPrivkey(ECPubkey): aux_rand32 = bytes(32) assert isinstance(aux_rand32, bytes), type(aux_rand32) assert len(aux_rand32) == 32, len(aux_rand32) + if not ecc_fast.HAS_SCHNORR: + raise LibModuleMissing( + 'libsecp256k1 library found but it was built ' + 'without required modules (--enable-module-schnorrsig --enable-module-extrakeys)') # construct "keypair" obj privkey_bytes = self.secret_scalar.to_bytes(32, byteorder="big") keypair = create_string_buffer(96) diff --git a/electrum/ecc_fast.py b/electrum/ecc_fast.py index 1730cc263..b4ba9f8fe 100644 --- a/electrum/ecc_fast.py +++ b/electrum/ecc_fast.py @@ -42,6 +42,8 @@ class LibModuleMissing(Exception): pass def load_library(): + global HAS_SCHNORR + # note: for a mapping between bitcoin-core/secp256k1 git tags and .so.V libtool version numbers, # see https://github.com/bitcoin-core/secp256k1/pull/1055#issuecomment-1227505189 tested_libversions = [2, 1, 0, ] # try latest version first @@ -140,8 +142,10 @@ def load_library(): secp256k1.secp256k1_schnorrsig_verify.argtypes = [c_void_p, c_char_p, c_char_p, c_size_t, c_char_p] secp256k1.secp256k1_schnorrsig_verify.restype = c_int except (OSError, AttributeError): - raise LibModuleMissing('libsecp256k1 library found but it was built ' - 'without required module (--enable-module-schnorrsig)') + _logger.warning(f"libsecp256k1 library found but it was built without desired module (--enable-module-schnorrsig)") + HAS_SCHNORR = False + # raise LibModuleMissing('libsecp256k1 library found but it was built ' + # 'without required module (--enable-module-schnorrsig)') # --enable-module-extrakeys try: @@ -154,8 +158,10 @@ def load_library(): secp256k1.secp256k1_keypair_create.argtypes = [c_void_p, c_char_p, c_char_p] secp256k1.secp256k1_keypair_create.restype = c_int except (OSError, AttributeError): - raise LibModuleMissing('libsecp256k1 library found but it was built ' - 'without required module (--enable-module-extrakeys)') + _logger.warning(f"libsecp256k1 library found but it was built without desired module (--enable-module-extrakeys)") + HAS_SCHNORR = False + # raise LibModuleMissing('libsecp256k1 library found but it was built ' + # 'without required module (--enable-module-extrakeys)') secp256k1.ctx = secp256k1.secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY) ret = secp256k1.secp256k1_context_randomize(secp256k1.ctx, os.urandom(32)) @@ -170,6 +176,7 @@ def load_library(): _libsecp256k1 = None +HAS_SCHNORR = True try: _libsecp256k1 = load_library() except BaseException as e: