@@ -3,6 +3,7 @@ import { toPositiveNumber } from '@/lib/format'
33import { authenticatedLndGrpc } from '@/lib/lnd'
44import { randomBytes } from 'crypto'
55import { chanNumber } from 'bolt07'
6+ import { once } from 'events'
67import { getIdentity , getHeight , getWalletInfo , getNode , getPayment , parsePaymentRequest } from 'ln-service'
78
89const lnd = global . lnd || authenticatedLndGrpc ( {
@@ -22,10 +23,8 @@ getWalletInfo({ lnd }, (err, result) => {
2223 console . log ( 'LND GRPC connection successful' )
2324} )
2425
25- // we don't use this because it doesn't solve https://github.com/lightningnetwork/lnd/discussions/10427
26- // due to this bug, real probes cause the channel to be marked as unusable and the real payment fails
27- // this should be useful in the future though when need more control over probing
28- export async function rawProbePayment ( { lnd, request, maxFeeMsat, timeoutSeconds, maxCltvDelta } ) {
26+ // we create our own probe because estimateRouteFee is busted https://github.com/lightningnetwork/lnd/discussions/10427
27+ export async function estimateRouteFeeProbe ( { lnd, request, maxFeeMsat, timeoutSeconds, maxCltvDelta } ) {
2928 if ( ! request ) {
3029 throw new Error ( 'Payment request is required' )
3130 }
@@ -34,11 +33,11 @@ export async function rawProbePayment ({ lnd, request, maxFeeMsat, timeoutSecond
3433 allow_self_payment : true ,
3534 amt_msat : inv . mtokens ,
3635 cancelable : true ,
37- cltv_limit : maxCltvDelta ,
36+ cltv_limit : maxCltvDelta ? toPositiveNumber ( maxCltvDelta ) : undefined ,
3837 dest : Buffer . from ( inv . destination , 'hex' ) ,
3938 dest_custom_records : undefined ,
4039 dest_features : inv . features . map ( n => n . bit ) ,
41- fee_limit_msat : maxFeeMsat ,
40+ fee_limit_msat : maxFeeMsat ? toPositiveNumber ( maxFeeMsat ) : undefined ,
4241 final_cltv_delta : inv . cltv_delta ,
4342 last_hop_pubkey : undefined ,
4443 max_parts : 1 ,
@@ -47,7 +46,7 @@ export async function rawProbePayment ({ lnd, request, maxFeeMsat, timeoutSecond
4746 outgoing_chan_id : undefined ,
4847 outgoing_chan_ids : [ ] ,
4948 payment_addr : Buffer . from ( inv . payment , 'hex' ) ,
50- payment_hash : randomBytes ( 32 ) ,
49+ payment_hash : randomBytes ( 32 ) , // this is what makes it a probe
5150 payment_request : undefined ,
5251 route_hints : inv . routes ?. map ( r => ( {
5352 hop_hints : r . slice ( 1 ) . map ( ( h , i ) => ( {
@@ -59,20 +58,23 @@ export async function rawProbePayment ({ lnd, request, maxFeeMsat, timeoutSecond
5958 } ) )
6059 } ) ) ?? [ ] ,
6160 time_pref : 1 ,
62- timeout_seconds : timeoutSeconds
61+ timeout_seconds : timeoutSeconds ? toPositiveNumber ( timeoutSeconds ) : undefined
6362 }
64- return await new Promise ( ( resolve , reject ) => {
65- const sub = lnd . router . sendPaymentV2 ( params )
66- sub . on ( 'data' , ( res ) => {
67- resolve ( res )
68- } )
69- sub . on ( 'error' , ( err ) => {
70- reject ( err )
71- } )
72- sub . on ( 'end' , ( ) => {
73- reject ( new Error ( 'Payment timed out' ) )
74- } )
75- } )
63+ const sub = lnd . router . sendPaymentV2 ( params )
64+ const [ probe ] = await once ( sub , 'data' )
65+
66+ // a successful probe returns FAILURE_REASON_INCORRECT_PAYMENT_DETAILS
67+ if ( probe . failure_reason === 'FAILURE_REASON_INCORRECT_PAYMENT_DETAILS' ) {
68+ // there's only one htlc in the probe, because max_parts is 1
69+ const { htlcs : [ { route : { total_fees_msat : routingFeeMsat , total_time_lock : timeLockDelay } } ] } = probe
70+ return {
71+ routingFeeMsat : toPositiveNumber ( routingFeeMsat ) ,
72+ // because we are simulating estimateRouteFee's probe, we remove the final hop's cltv_delta
73+ timeLockDelay : toPositiveNumber ( timeLockDelay - inv . cltv_delta )
74+ }
75+ }
76+
77+ throw new Error ( `Unable to estimate route: ${ probe . failure_reason || 'unknown reason' } ` )
7678}
7779
7880export async function estimateRouteFee ( { lnd, destination, tokens, mtokens, request, timeout } ) {
@@ -97,6 +99,7 @@ export async function estimateRouteFee ({ lnd, destination, tokens, mtokens, req
9799 // XXX we don't use the payment request anymore because it causes the channel to be marked as unusable
98100 // and the real payment fails see: https://github.com/lightningnetwork/lnd/discussions/10427
99101 // without the request, the estimate is a statistical estimate based on past payments
102+ // NOTE: this should be fixed in v0.20.1-beta
100103 request = false
101104 }
102105
0 commit comments