-
Notifications
You must be signed in to change notification settings - Fork 3.4k
lnsweep: simplify maybe_reveal_preimage_for_htlc #10442
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1022,7 +1022,7 @@ def __init__(self, wallet: 'Abstract_Wallet', xprv, *, features: LnFeatures = No | |
| self.lnrater: LNRater = None | ||
| # "RHASH:direction" -> amount_msat, status, min_final_cltv_delta, expiry_delay, creation_ts, invoice_features | ||
| self.payment_info = self.db.get_dict('lightning_payments') # type: dict[str, Tuple[Optional[int], int, int, int, int, int]] | ||
| self._preimages = self.db.get_dict('lightning_preimages') # RHASH -> preimage | ||
| self._preimages = self.db.get_dict('lightning_preimages') # RHASH -> (preimage, is_public) | ||
| self._bolt11_cache = {} | ||
| # note: this sweep_address is only used as fallback; as it might result in address-reuse | ||
| self.logs = defaultdict(list) # type: Dict[str, List[HtlcLog]] # key is RHASH # (not persisted) | ||
|
|
@@ -2699,19 +2699,32 @@ def delete_payment_bundle( | |
| del self._payment_bundles_pkey_to_canon[pkey] | ||
| del self._payment_bundles_canon_to_pkeylist[canon_pkey] | ||
|
|
||
| def save_preimage(self, payment_hash: bytes, preimage: bytes, *, write_to_disk: bool = True): | ||
| def save_preimage( | ||
| self, | ||
| payment_hash: bytes, | ||
| preimage: bytes, | ||
| *, | ||
| write_to_disk: bool = True, | ||
| mark_as_public: bool = False, # see is_preimage_public | ||
| ): | ||
| assert isinstance(payment_hash, bytes), f"expected bytes, but got {type(payment_hash)}" | ||
| assert isinstance(preimage, bytes), f"expected bytes, but got {type(preimage)}" | ||
| if sha256(preimage) != payment_hash: | ||
| raise Exception("tried to save incorrect preimage for payment_hash") | ||
| if self._preimages.get(payment_hash.hex()) is not None: | ||
| return # we already have this preimage | ||
| self.logger.debug(f"saving preimage for {payment_hash.hex()}") | ||
| self._preimages[payment_hash.hex()] = preimage.hex() | ||
| old_tuple = _, old_is_public = self._preimages.get(payment_hash.hex(), (None, False)) | ||
| mark_as_public |= old_is_public # disallow True->False transition | ||
| # sanity checks and conversions done. | ||
| new_tuple = preimage.hex(), mark_as_public | ||
| if old_tuple == new_tuple: # no change | ||
| return | ||
| self.logger.debug(f"saving preimage for {payment_hash.hex()} (public={mark_as_public})") | ||
| self._preimages[payment_hash.hex()] = new_tuple | ||
|
Comment on lines
+2702
to
+2721
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I changed to this, as the previous API was too error-prone - there was already a bug in this branch due to that. (ordering of calls to Unfortunately, to keep the API (for callers) simple, I had to put quite a bit of complexity inside the implementation of
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. regarding complexity, why does
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No substantial reason. Indeed it is a final sanity check that tests the preimage is correct. |
||
| if write_to_disk: | ||
| self.wallet.save_db() | ||
|
|
||
| def get_preimage(self, payment_hash: bytes) -> Optional[bytes]: | ||
| assert isinstance(payment_hash, bytes), f"expected bytes, but got {type(payment_hash)}" | ||
| preimage_hex = self._preimages.get(payment_hash.hex()) | ||
| preimage_hex, _ = self._preimages.get(payment_hash.hex(), (None, None)) | ||
| if preimage_hex is None: | ||
| return None | ||
| preimage_bytes = bytes.fromhex(preimage_hex) | ||
|
|
@@ -2723,6 +2736,20 @@ def get_preimage_hex(self, payment_hash: str) -> Optional[str]: | |
| preimage_bytes = self.get_preimage(bytes.fromhex(payment_hash)) or b"" | ||
| return preimage_bytes.hex() or None | ||
|
|
||
| def is_preimage_public(self, payment_hash: bytes) -> bool: | ||
| """If another LN node knows a preimage besides us, we consider it public. | ||
| If a preimage is public, it is safe to reveal it in an arbitrary context. | ||
|
|
||
| For example, if there is a pending incoming partial MPP for an invoice we created, | ||
| we must not reveal the preimage, otherwise we will get paid less than invoice amount. | ||
| What if there is a force-close around that time? When is it safe to reveal the preimage on-chain? | ||
| e.g. if we already revealed the preimage either offchain or onchain, it is fine to reveal it again. | ||
| """ | ||
| assert isinstance(payment_hash, bytes), f"expected bytes, but got {type(payment_hash)}" | ||
| preimage_hex, is_public = self._preimages.get(payment_hash.hex(), (None, None)) | ||
| assert preimage_hex is not None | ||
| return bool(is_public) | ||
|
|
||
| def get_payment_info(self, payment_hash: bytes, *, direction: lnutil.Direction) -> Optional[PaymentInfo]: | ||
| """returns None if payment_hash is a payment we are forwarding""" | ||
| key = PaymentInfo.calc_db_key(payment_hash_hex=payment_hash.hex(), direction=direction) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lnsweep desperately needs unit tests. I have a WIP branch to build the architecture for it but we can merge this before I get to finish that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tested the current branch using regtest (using the 'timebomb' approach).
It behaves well.