Use embit for Python 3.14-compatible NIP-42 signing (#11)

* Avoid native dependency for NIP-42 signing

* Use embit for NIP-42 Schnorr signatures
This commit is contained in:
a1denvalu3
2026-07-23 22:13:45 +02:00
committed by GitHub
parent 367979ea3d
commit c467125b73
3 changed files with 21 additions and 12 deletions
+17 -8
View File
@@ -22,7 +22,7 @@ from typing import Set, List, Dict, Optional, Tuple
from urllib.parse import urlparse from urllib.parse import urlparse
import websockets import websockets
import websockets.exceptions import websockets.exceptions
from coincurve import PrivateKey from embit import ec
# Configure logging # Configure logging
@@ -35,7 +35,6 @@ logger = logging.getLogger(__name__)
# Save progress every N relays processed # Save progress every N relays processed
SAVE_POINT = 10 SAVE_POINT = 10
@dataclass @dataclass
class RelayDiscoveryStats: class RelayDiscoveryStats:
"""Statistics for the relay discovery process""" """Statistics for the relay discovery process"""
@@ -127,10 +126,14 @@ class NostrRelayDiscovery:
return base64.b64encode(random_bytes).decode() return base64.b64encode(random_bytes).decode()
@staticmethod @staticmethod
def _load_private_key(private_key_hex: Optional[str]) -> PrivateKey: def _load_private_key(private_key_hex: Optional[str]) -> ec.PrivateKey:
"""Load a configured Nostr secret or generate an ephemeral discovery key.""" """Load a configured Nostr secret or generate an ephemeral discovery key."""
if private_key_hex is None: if private_key_hex is None:
return PrivateKey() while True:
try:
return ec.PrivateKey(secrets.token_bytes(32))
except ValueError:
continue
try: try:
secret = bytes.fromhex(private_key_hex) secret = bytes.fromhex(private_key_hex)
@@ -141,13 +144,15 @@ class NostrRelayDiscovery:
raise ValueError("--private-key must be 64 hexadecimal characters") raise ValueError("--private-key must be 64 hexadecimal characters")
try: try:
return PrivateKey(secret) return ec.PrivateKey(secret)
except ValueError as exc: except ValueError as exc:
raise ValueError("--private-key is not a valid secp256k1 secret") from exc raise ValueError(
"--private-key is not a valid secp256k1 secret"
) from exc
def build_auth_event(self, relay_url: str, challenge: str) -> Dict: def build_auth_event(self, relay_url: str, challenge: str) -> Dict:
"""Build and sign a NIP-42 kind 22242 authentication event.""" """Build and sign a NIP-42 kind 22242 authentication event."""
pubkey = self.private_key.public_key.format(compressed=True)[1:].hex() pubkey = self.private_key.xonly().hex()
event = { event = {
"pubkey": pubkey, "pubkey": pubkey,
"created_at": int(time.time()), "created_at": int(time.time()),
@@ -172,7 +177,11 @@ class NostrRelayDiscovery:
).encode() ).encode()
event_id = hashlib.sha256(serialized).digest() event_id = hashlib.sha256(serialized).digest()
event["id"] = event_id.hex() event["id"] = event_id.hex()
event["sig"] = self.private_key.sign_schnorr(event_id).hex() signature = self.private_key.schnorr_sign(event_id)
public_key = ec.PublicKey.from_xonly(bytes.fromhex(pubkey))
if not public_key.schnorr_verify(signature, event_id):
raise RuntimeError("generated an invalid BIP-340 signature")
event["sig"] = signature.serialize().hex()
return event return event
async def receive_with_auth( async def receive_with_auth(
+1 -1
View File
@@ -1,5 +1,5 @@
websockets>=12.0 websockets>=12.0
coincurve>=20.0.0 embit>=0.8.0
matplotlib>=3.5.0 matplotlib>=3.5.0
pandas>=1.4.0 pandas>=1.4.0
numpy>=1.20.0 numpy>=1.20.0
+3 -3
View File
@@ -4,7 +4,7 @@ import json
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from coincurve import PublicKeyXOnly from embit import ec
from nostr_relay_discovery import NostrRelayDiscovery from nostr_relay_discovery import NostrRelayDiscovery
@@ -50,8 +50,8 @@ class Nip42Tests(unittest.TestCase):
self.assertEqual(event["id"], event_id.hex()) self.assertEqual(event["id"], event_id.hex())
self.assertTrue( self.assertTrue(
PublicKeyXOnly(bytes.fromhex(event["pubkey"])).verify( ec.PublicKey.from_xonly(bytes.fromhex(event["pubkey"])).schnorr_verify(
bytes.fromhex(event["sig"]), event_id ec.SchnorrSig.parse(bytes.fromhex(event["sig"])), event_id
) )
) )