Browse Source

lnpeer: new payment secret, derived without preimage.

(this is needed for hold invoices)
master
ThomasV 3 years ago
parent
commit
21e06b7065
  1. 8
      electrum/lnpeer.py
  2. 1
      electrum/lnutil.py
  3. 10
      electrum/lnworker.py
  4. 2
      electrum/tests/test_lnpeer.py

8
electrum/lnpeer.py

@ -1826,8 +1826,12 @@ class Peer(Logger):
raise exc_incorrect_or_unknown_pd
preimage = self.lnworker.get_preimage(htlc.payment_hash)
if payment_secret_from_onion:
if payment_secret_from_onion != derive_payment_secret_from_payment_preimage(preimage):
log_fail_reason(f'incorrect payment secret {payment_secret_from_onion.hex()} != {derive_payment_secret_from_payment_preimage(preimage).hex()}')
expected_payment_secrets = [self.lnworker.get_payment_secret(htlc.payment_hash)]
if preimage:
# legacy secret for old invoices
expected_payment_secrets.append(derive_payment_secret_from_payment_preimage(preimage))
if payment_secret_from_onion not in expected_payment_secrets:
log_fail_reason(f'incorrect payment secret {payment_secret_from_onion.hex()} != {expected_payment_secrets[0].hex()}')
raise exc_incorrect_or_unknown_pd
invoice_msat = info.amount_msat
if not (invoice_msat is None or invoice_msat <= total_msat <= 2 * invoice_msat):

1
electrum/lnutil.py

@ -1538,6 +1538,7 @@ class LnKeyFamily(IntEnum):
REVOCATION_ROOT = 5 | BIP32_PRIME
NODE_KEY = 6
BACKUP_CIPHER = 7 | BIP32_PRIME
PAYMENT_SECRET_KEY = 8 | BIP32_PRIME
def generate_keypair(node: BIP32Node, key_family: LnKeyFamily) -> Keypair:

10
electrum/lnworker.py

@ -217,6 +217,7 @@ class LNWorker(Logger, EventListener, NetworkRetryManager[LNPeerAddr]):
self.lock = threading.RLock()
self.node_keypair = generate_keypair(BIP32Node.from_xkey(xprv), LnKeyFamily.NODE_KEY)
self.backup_key = generate_keypair(BIP32Node.from_xkey(xprv), LnKeyFamily.BACKUP_CIPHER).privkey
self.payment_secret_key = generate_keypair(BIP32Node.from_xkey(xprv), LnKeyFamily.PAYMENT_SECRET_KEY).privkey
self._peers = {} # type: Dict[bytes, Peer] # pubkey -> Peer # needs self.lock
self.taskgroup = OldTaskGroup()
self.listen_server = None # type: Optional[asyncio.AbstractServer]
@ -1824,9 +1825,7 @@ class LNWallet(LNWorker):
routing_hints, trampoline_hints = self.calc_routing_hints_for_invoice(amount_msat, channels=channels)
self.logger.info(f"creating bolt11 invoice with routing_hints: {routing_hints}")
invoice_features = self.features.for_invoice()
payment_preimage = self.get_preimage(payment_hash)
if payment_preimage is None: # e.g. when export/importing requests between wallets
raise Exception("missing preimage for payment_hash")
payment_secret = self.get_payment_secret(payment_hash)
amount_btc = amount_msat/Decimal(COIN*1000) if amount_msat else None
if expiry == 0:
expiry = LN_EXPIRY_NEVER
@ -1843,12 +1842,15 @@ class LNWallet(LNWorker):
+ routing_hints
+ trampoline_hints,
date=timestamp,
payment_secret=derive_payment_secret_from_payment_preimage(payment_preimage))
payment_secret=payment_secret)
invoice = lnencode(lnaddr, self.node_keypair.privkey)
pair = lnaddr, invoice
self._bolt11_cache[payment_hash] = pair
return pair
def get_payment_secret(self, payment_hash):
return sha256(sha256(self.payment_secret_key) + payment_hash)
def create_payment_info(self, *, amount_msat: Optional[int], write_to_disk=True) -> bytes:
payment_preimage = os.urandom(32)
payment_hash = sha256(payment_preimage)

2
electrum/tests/test_lnpeer.py

@ -138,6 +138,7 @@ class MockLNWallet(Logger, EventListener, NetworkRetryManager[LNPeerAddr]):
Logger.__init__(self)
NetworkRetryManager.__init__(self, max_retry_delay_normal=1, init_retry_delay_normal=1)
self.node_keypair = local_keypair
self.payment_secret_key = os.urandom(256) # does not need to be deterministic in tests
self._user_dir = tempfile.mkdtemp(prefix="electrum-lnpeer-test-")
self.config = SimpleConfig({}, read_user_dir_function=lambda: self._user_dir)
self.network = MockNetwork(tx_queue, config=self.config)
@ -239,6 +240,7 @@ class MockLNWallet(Logger, EventListener, NetworkRetryManager[LNPeerAddr]):
full_path=full_path)]
get_payments = LNWallet.get_payments
get_payment_secret = LNWallet.get_payment_secret
get_payment_info = LNWallet.get_payment_info
save_payment_info = LNWallet.save_payment_info
set_invoice_status = LNWallet.set_invoice_status

Loading…
Cancel
Save