fix: adapt nostr transport to forgejo fork's secp256k1 library
Some checks failed
LNbits CI / lint (pull_request) Has been cancelled
codeql / analyze (pull_request) Has been cancelled
LNbits CI / test-unit (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.10) (pull_request) Has been cancelled
LNbits CI / regtest (CoreLightningRestWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / test-unit (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.11) (pull_request) Has been cancelled
LNbits CI / test-unit (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.12) (pull_request) Has been cancelled
LNbits CI / migration (3.10) (pull_request) Has been cancelled
LNbits CI / migration (3.11) (pull_request) Has been cancelled
LNbits CI / migration (3.12) (pull_request) Has been cancelled
LNbits CI / openapi (pull_request) Has been cancelled
LNbits CI / test-api (, 3.10) (pull_request) Has been cancelled
LNbits CI / test-api (, 3.11) (pull_request) Has been cancelled
LNbits CI / test-api (, 3.12) (pull_request) Has been cancelled
LNbits CI / test-api (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.10) (pull_request) Has been cancelled
LNbits CI / test-api (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.11) (pull_request) Has been cancelled
LNbits CI / test-api (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.12) (pull_request) Has been cancelled
LNbits CI / test-wallets (, 3.10) (pull_request) Has been cancelled
LNbits CI / test-wallets (, 3.11) (pull_request) Has been cancelled
LNbits CI / test-wallets (, 3.12) (pull_request) Has been cancelled
LNbits CI / test-wallets (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.10) (pull_request) Has been cancelled
LNbits CI / test-wallets (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.11) (pull_request) Has been cancelled
LNbits CI / test-wallets (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.12) (pull_request) Has been cancelled
LNbits CI / test-unit (, 3.10) (pull_request) Has been cancelled
LNbits CI / test-unit (, 3.11) (pull_request) Has been cancelled
LNbits CI / test-unit (, 3.12) (pull_request) Has been cancelled
LNbits CI / regtest (CoreLightningWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / regtest (EclairWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / regtest (LNbitsWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / regtest (LndRestWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / regtest (LndWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / jmeter (3.10) (pull_request) Has been cancelled
Some checks failed
LNbits CI / lint (pull_request) Has been cancelled
codeql / analyze (pull_request) Has been cancelled
LNbits CI / test-unit (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.10) (pull_request) Has been cancelled
LNbits CI / regtest (CoreLightningRestWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / test-unit (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.11) (pull_request) Has been cancelled
LNbits CI / test-unit (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.12) (pull_request) Has been cancelled
LNbits CI / migration (3.10) (pull_request) Has been cancelled
LNbits CI / migration (3.11) (pull_request) Has been cancelled
LNbits CI / migration (3.12) (pull_request) Has been cancelled
LNbits CI / openapi (pull_request) Has been cancelled
LNbits CI / test-api (, 3.10) (pull_request) Has been cancelled
LNbits CI / test-api (, 3.11) (pull_request) Has been cancelled
LNbits CI / test-api (, 3.12) (pull_request) Has been cancelled
LNbits CI / test-api (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.10) (pull_request) Has been cancelled
LNbits CI / test-api (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.11) (pull_request) Has been cancelled
LNbits CI / test-api (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.12) (pull_request) Has been cancelled
LNbits CI / test-wallets (, 3.10) (pull_request) Has been cancelled
LNbits CI / test-wallets (, 3.11) (pull_request) Has been cancelled
LNbits CI / test-wallets (, 3.12) (pull_request) Has been cancelled
LNbits CI / test-wallets (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.10) (pull_request) Has been cancelled
LNbits CI / test-wallets (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.11) (pull_request) Has been cancelled
LNbits CI / test-wallets (postgres://lnbits:lnbits@0.0.0.0:5432/lnbits, 3.12) (pull_request) Has been cancelled
LNbits CI / test-unit (, 3.10) (pull_request) Has been cancelled
LNbits CI / test-unit (, 3.11) (pull_request) Has been cancelled
LNbits CI / test-unit (, 3.12) (pull_request) Has been cancelled
LNbits CI / regtest (CoreLightningWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / regtest (EclairWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / regtest (LNbitsWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / regtest (LndRestWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / regtest (LndWallet, 3.10) (pull_request) Has been cancelled
LNbits CI / jmeter (3.10) (pull_request) Has been cancelled
The aiolabs/lnbits fork uses python-secp256k1 instead of coincurve for elliptic curve operations. Update crypto.py, relay_pool.py, and __init__.py to use the secp256k1 API (tweak_mul, schnorr_sign). Also comment out account.activated check since this field doesn't exist in the current fork (present in upstream dev). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5443cb75bf
commit
bae881587c
4 changed files with 19 additions and 18 deletions
|
|
@ -51,10 +51,10 @@ async def start_nostr_transport():
|
||||||
else:
|
else:
|
||||||
private_key_hex = normalize_private_key(private_key_hex)
|
private_key_hex = normalize_private_key(private_key_hex)
|
||||||
# Derive public key from private key
|
# Derive public key from private key
|
||||||
import coincurve
|
import secp256k1
|
||||||
|
|
||||||
privkey = coincurve.PrivateKey(bytes.fromhex(private_key_hex))
|
privkey = secp256k1.PrivateKey(bytes.fromhex(private_key_hex), True)
|
||||||
public_key_hex = privkey.public_key_xonly.format().hex()
|
public_key_hex = privkey.pubkey.serialize()[1:].hex()
|
||||||
settings.nostr_transport_public_key = public_key_hex
|
settings.nostr_transport_public_key = public_key_hex
|
||||||
|
|
||||||
relay_urls = settings.nostr_transport_relays
|
relay_urls = settings.nostr_transport_relays
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,9 @@ async def resolve_nostr_auth(
|
||||||
extra=UserExtra(provider="nostr"),
|
extra=UserExtra(provider="nostr"),
|
||||||
)
|
)
|
||||||
|
|
||||||
if not account.activated:
|
# TODO: upstream LNbits (dev) has account.activated field
|
||||||
raise PermissionError("Account is not activated.")
|
# if not account.activated:
|
||||||
|
# raise PermissionError("Account is not activated.")
|
||||||
|
|
||||||
# Account-level operation (no wallet specified)
|
# Account-level operation (no wallet specified)
|
||||||
if wallet_id is None:
|
if wallet_id is None:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
NIP-44 v2 encryption/decryption for nostr transport.
|
NIP-44 v2 encryption/decryption for nostr transport.
|
||||||
|
|
||||||
Implements the NIP-44 v2 spec using coincurve (secp256k1 ECDH) and
|
Implements the NIP-44 v2 spec using secp256k1 (ECDH) and
|
||||||
pycryptodome (ChaCha20, HMAC-SHA256, HKDF), all already in LNbits' deps.
|
pycryptodome (ChaCha20, HMAC-SHA256, HKDF), all already in LNbits' deps.
|
||||||
|
|
||||||
Also re-exports sign_event() and verify_event() from lnbits.utils.nostr.
|
Also re-exports sign_event() and verify_event() from lnbits.utils.nostr.
|
||||||
|
|
@ -14,7 +14,7 @@ import os
|
||||||
import struct
|
import struct
|
||||||
from math import floor, log2
|
from math import floor, log2
|
||||||
|
|
||||||
import coincurve
|
import secp256k1
|
||||||
from Cryptodome.Cipher import ChaCha20
|
from Cryptodome.Cipher import ChaCha20
|
||||||
|
|
||||||
from lnbits.utils.nostr import sign_event, verify_event # noqa: F401
|
from lnbits.utils.nostr import sign_event, verify_event # noqa: F401
|
||||||
|
|
@ -30,18 +30,18 @@ def get_conversation_key(
|
||||||
Compute NIP-44 v2 conversation key via ECDH + HKDF-extract.
|
Compute NIP-44 v2 conversation key via ECDH + HKDF-extract.
|
||||||
|
|
||||||
The public key must be 32-byte x-only (as in nostr). We prepend 0x02
|
The public key must be 32-byte x-only (as in nostr). We prepend 0x02
|
||||||
to make it a valid compressed SEC1 point for coincurve.
|
to make it a valid compressed SEC1 point for secp256k1.
|
||||||
"""
|
"""
|
||||||
privkey = coincurve.PrivateKey(bytes.fromhex(private_key_hex))
|
|
||||||
# x-only pubkey -> compressed pubkey (assume even y)
|
# x-only pubkey -> compressed pubkey (assume even y)
|
||||||
pubkey_bytes = b"\x02" + bytes.fromhex(public_key_hex)
|
pubkey_bytes = b"\x02" + bytes.fromhex(public_key_hex)
|
||||||
pubkey = coincurve.PublicKey(pubkey_bytes)
|
pubkey = secp256k1.PublicKey(pubkey_bytes, True)
|
||||||
|
|
||||||
# ECDH: multiply pubkey by privkey, get raw x coordinate (32 bytes)
|
# ECDH: multiply pubkey by privkey scalar, get shared point
|
||||||
# coincurve's ecdh returns sha256(compressed_point) by default,
|
# tweak_mul multiplies the point by a scalar, returning a new PublicKey
|
||||||
# but NIP-44 needs the unhashed x coordinate.
|
shared_point = pubkey.tweak_mul(bytes.fromhex(private_key_hex))
|
||||||
shared_point = pubkey.multiply(privkey.secret)
|
# serialize(compressed=True) gives 0x02/0x03 + 32-byte x coordinate
|
||||||
shared_x = shared_point.format(compressed=True)[1:] # strip 0x02/0x03 prefix
|
# strip the prefix byte to get the raw x coordinate
|
||||||
|
shared_x = shared_point.serialize()[1:]
|
||||||
|
|
||||||
# HKDF-extract with salt="nip44-v2"
|
# HKDF-extract with salt="nip44-v2"
|
||||||
return _hkdf_extract(salt=_NIP44_SALT, ikm=shared_x)
|
return _hkdf_extract(salt=_NIP44_SALT, ikm=shared_x)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import json
|
||||||
import time
|
import time
|
||||||
from collections.abc import Awaitable, Callable
|
from collections.abc import Awaitable, Callable
|
||||||
|
|
||||||
import coincurve
|
import secp256k1
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from websockets import connect as ws_connect
|
from websockets import connect as ws_connect
|
||||||
|
|
||||||
|
|
@ -49,8 +49,8 @@ class NostrTransportPool:
|
||||||
self.relay_urls = relay_urls
|
self.relay_urls = relay_urls
|
||||||
self.event_callback = event_callback
|
self.event_callback = event_callback
|
||||||
|
|
||||||
# coincurve key objects for signing
|
# secp256k1 key objects for signing
|
||||||
self._privkey = coincurve.PrivateKey(bytes.fromhex(private_key_hex))
|
self._privkey = secp256k1.PrivateKey(bytes.fromhex(private_key_hex), True)
|
||||||
|
|
||||||
# Deduplication: event_id -> timestamp
|
# Deduplication: event_id -> timestamp
|
||||||
self._handled_events: dict[str, float] = {}
|
self._handled_events: dict[str, float] = {}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue