|
|
@ -51,13 +51,13 @@ from .lnutil import (Outpoint, LNPeerAddr, |
|
|
|
UnknownPaymentHash, MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE, |
|
|
|
NUM_MAX_EDGES_IN_PAYMENT_PATH, SENT, RECEIVED, HTLCOwner, |
|
|
|
UpdateAddHtlc, Direction, LnLocalFeatures, format_short_channel_id, |
|
|
|
ShortChannelID) |
|
|
|
ShortChannelID, PaymentAttemptLog, PaymentAttemptFailureDetails) |
|
|
|
from .lnutil import ln_dummy_address |
|
|
|
from .transaction import PartialTxOutput, PartialTransaction, PartialTxInput |
|
|
|
from .lnonion import OnionFailureCode |
|
|
|
from .lnmsg import decode_msg |
|
|
|
from .i18n import _ |
|
|
|
from .lnrouter import RouteEdge, is_route_sane_to_use |
|
|
|
from .lnrouter import RouteEdge, LNPaymentRoute, is_route_sane_to_use |
|
|
|
from .address_synchronizer import TX_HEIGHT_LOCAL |
|
|
|
from . import lnsweep |
|
|
|
from .lnwatcher import LNWatcher |
|
|
@ -118,7 +118,9 @@ class PaymentInfo(NamedTuple): |
|
|
|
|
|
|
|
|
|
|
|
class NoPathFound(PaymentFailure): |
|
|
|
pass |
|
|
|
def __str__(self): |
|
|
|
return _('No path found') |
|
|
|
|
|
|
|
|
|
|
|
class LNWorker(Logger): |
|
|
|
|
|
|
@ -345,7 +347,7 @@ class LNWallet(LNWorker): |
|
|
|
self.preimages = self.storage.get('lightning_preimages', {}) # RHASH -> preimage |
|
|
|
self.sweep_address = wallet.get_receiving_address() |
|
|
|
self.lock = threading.RLock() |
|
|
|
self.logs = defaultdict(list) |
|
|
|
self.logs = defaultdict(list) # type: Dict[str, List[PaymentAttemptLog]] # key is RHASH |
|
|
|
|
|
|
|
# note: accessing channels (besides simple lookup) needs self.lock! |
|
|
|
self.channels = {} # type: Dict[bytes, Channel] |
|
|
@ -891,7 +893,7 @@ class LNWallet(LNWorker): |
|
|
|
return chan |
|
|
|
|
|
|
|
@log_exceptions |
|
|
|
async def _pay(self, invoice, amount_sat=None, attempts=1): |
|
|
|
async def _pay(self, invoice, amount_sat=None, attempts=1) -> bool: |
|
|
|
lnaddr = self._check_invoice(invoice, amount_sat) |
|
|
|
payment_hash = lnaddr.paymenthash |
|
|
|
key = payment_hash.hex() |
|
|
@ -905,23 +907,23 @@ class LNWallet(LNWorker): |
|
|
|
self.save_payment_info(info) |
|
|
|
self.wallet.set_label(key, lnaddr.get_description()) |
|
|
|
log = self.logs[key] |
|
|
|
success = False |
|
|
|
for i in range(attempts): |
|
|
|
try: |
|
|
|
route = await self._create_route_from_invoice(decoded_invoice=lnaddr) |
|
|
|
except NoPathFound: |
|
|
|
success = False |
|
|
|
except NoPathFound as e: |
|
|
|
log.append(PaymentAttemptLog(success=False, exception=e)) |
|
|
|
break |
|
|
|
self.network.trigger_callback('invoice_status', key, PR_INFLIGHT) |
|
|
|
success, preimage, failure_log = await self._pay_to_route(route, lnaddr) |
|
|
|
payment_attempt_log = await self._pay_to_route(route, lnaddr) |
|
|
|
log.append(payment_attempt_log) |
|
|
|
success = payment_attempt_log.success |
|
|
|
if success: |
|
|
|
log.append((route, True, preimage)) |
|
|
|
break |
|
|
|
else: |
|
|
|
log.append((route, False, failure_log)) |
|
|
|
self.network.trigger_callback('invoice_status', key, PR_PAID if success else PR_FAILED) |
|
|
|
return success |
|
|
|
|
|
|
|
async def _pay_to_route(self, route, lnaddr): |
|
|
|
async def _pay_to_route(self, route: LNPaymentRoute, lnaddr: LnAddr) -> PaymentAttemptLog: |
|
|
|
short_channel_id = route[0].short_channel_id |
|
|
|
chan = self.get_channel_by_short_id(short_channel_id) |
|
|
|
if not chan: |
|
|
@ -948,8 +950,13 @@ class LNWallet(LNWorker): |
|
|
|
self.logger.info("payment destination reported error") |
|
|
|
else: |
|
|
|
self.network.path_finder.add_to_blacklist(short_chan_id) |
|
|
|
failure_log = (sender_idx, failure_msg, blacklist) |
|
|
|
return success, preimage, failure_log |
|
|
|
failure_log = PaymentAttemptFailureDetails(sender_idx=sender_idx, |
|
|
|
failure_msg=failure_msg, |
|
|
|
is_blacklisted=blacklist) |
|
|
|
return PaymentAttemptLog(route=route, |
|
|
|
success=success, |
|
|
|
preimage=preimage, |
|
|
|
failure_details=failure_log) |
|
|
|
|
|
|
|
def handle_error_code_from_failed_htlc(self, failure_msg, sender_idx, route, peer): |
|
|
|
code, data = failure_msg.code, failure_msg.data |
|
|
@ -1011,11 +1018,11 @@ class LNWallet(LNWorker): |
|
|
|
f"min_final_cltv_expiry: {addr.get_min_final_cltv_expiry()}")) |
|
|
|
return addr |
|
|
|
|
|
|
|
async def _create_route_from_invoice(self, decoded_invoice) -> List[RouteEdge]: |
|
|
|
async def _create_route_from_invoice(self, decoded_invoice) -> LNPaymentRoute: |
|
|
|
amount_msat = int(decoded_invoice.amount * COIN * 1000) |
|
|
|
invoice_pubkey = decoded_invoice.pubkey.serialize() |
|
|
|
# use 'r' field from invoice |
|
|
|
route = None # type: Optional[List[RouteEdge]] |
|
|
|
route = None # type: Optional[LNPaymentRoute] |
|
|
|
# only want 'r' tags |
|
|
|
r_tags = list(filter(lambda x: x[0] == 'r', decoded_invoice.tags)) |
|
|
|
# strip the tag type, it's implicitly 'r' now |
|
|
|