@@ -1481,6 +1481,7 @@ func validateLoopOutRequest(ctx context.Context, lnd lndclient.LightningClient,
14811481 requiredBalance := btcutil .Amount (req .Amt + req .MaxSwapRoutingFee )
14821482 isRoutable , _ := hasBandwidth (activeChannelSet , requiredBalance ,
14831483 int (maxParts ))
1484+
14841485 if ! isRoutable {
14851486 return 0 , fmt .Errorf ("%w: Requested swap amount of %d " +
14861487 "sats along with the maximum routing fee of %d sats " +
@@ -1505,41 +1506,88 @@ func validateLoopOutRequest(ctx context.Context, lnd lndclient.LightningClient,
15051506func hasBandwidth (channels []lndclient.ChannelInfo , amt btcutil.Amount ,
15061507 maxParts int ) (bool , int ) {
15071508
1508- scratch := make ([]btcutil.Amount , len (channels ))
1509+ log .Tracef ("Checking if %v sats can be routed with %v parts over " +
1510+ "channel set of length %v" , amt , maxParts , len (channels ))
1511+
1512+ localBalances := make ([]btcutil.Amount , len (channels ))
15091513 var totalBandwidth btcutil.Amount
15101514 for i , channel := range channels {
1511- scratch [i ] = channel .LocalBalance
1515+ log .Tracef ("Channel %v: local=%v remote=%v" , channel .ChannelID ,
1516+ channel .LocalBalance , channel .RemoteBalance )
1517+
1518+ localBalances [i ] = channel .LocalBalance
15121519 totalBandwidth += channel .LocalBalance
15131520 }
15141521
1522+ log .Tracef ("Total bandwidth: %v" , totalBandwidth )
15151523 if totalBandwidth < amt {
15161524 return false , 0
15171525 }
15181526
1527+ logLocalBalances := func (shard int ) {
1528+ log .Tracef ("Local balances for %v shards:" , shard )
1529+ for i , balance := range localBalances {
1530+ log .Tracef ("Channel %v: localBalances[%v]=%v" ,
1531+ channels [i ].ChannelID , i , balance )
1532+ }
1533+ }
1534+
15191535 split := amt
15201536 for shard := 0 ; shard <= maxParts ; {
1537+ log .Tracef ("Trying to split %v sats into %v parts" , amt , shard )
1538+
15211539 paid := false
1522- for i := 0 ; i < len (scratch ); i ++ {
1523- if scratch [i ] >= split {
1524- scratch [i ] -= split
1540+ for i := 0 ; i < len (localBalances ); i ++ {
1541+ // TODO(hieblmi): Consider channel reserves because the
1542+ // channel can't send its full local balance.
1543+ if localBalances [i ] >= split {
1544+ log .Tracef ("len(shards)=%v: Local channel " +
1545+ "balance %v can pay %v sats" ,
1546+ shard , localBalances [i ], split )
1547+
1548+ localBalances [i ] -= split
1549+ log .Tracef ("len(shards)=%v: Subtracted " +
1550+ "%v sats from localBalance[%v]=%v" ,
1551+ shard , split , i , localBalances [i ])
1552+
15251553 amt -= split
1554+ log .Tracef ("len(shards)=%v: Remaining total " +
1555+ "amount amt=%v" , shard , amt )
1556+
15261557 paid = true
15271558 shard ++
1559+
15281560 break
15291561 }
15301562 }
15311563
1564+ logLocalBalances (shard )
1565+
15321566 if amt == 0 {
1567+ log .Tracef ("Payment is routable with %v part(s)" , shard )
1568+
15331569 return true , shard
15341570 }
15351571
15361572 if ! paid {
1573+ log .Tracef ("len(shards)=%v: No channel could pay %v " +
1574+ "sats, halving payment to %v and trying again" ,
1575+ split / 2 )
1576+
15371577 split /= 2
15381578 } else {
1579+ log .Tracef ("len(shards)=%v: Payment was made, trying " +
1580+ "to pay remaining sats %v" , shard , amt )
1581+
15391582 split = amt
15401583 }
15411584 }
15421585
1586+ log .Tracef ("Payment is not routable, remaining amount that can't be " +
1587+ "sent: %v sats" , amt )
1588+
1589+ logLocalBalances (maxParts )
1590+
15431591 return false , 0
15441592}
15451593
0 commit comments