@@ -1481,6 +1481,7 @@ func validateLoopOutRequest(ctx context.Context, lnd lndclient.LightningClient,
1481
1481
requiredBalance := btcutil .Amount (req .Amt + req .MaxSwapRoutingFee )
1482
1482
isRoutable , _ := hasBandwidth (activeChannelSet , requiredBalance ,
1483
1483
int (maxParts ))
1484
+
1484
1485
if ! isRoutable {
1485
1486
return 0 , fmt .Errorf ("%w: Requested swap amount of %d " +
1486
1487
"sats along with the maximum routing fee of %d sats " +
@@ -1505,41 +1506,88 @@ func validateLoopOutRequest(ctx context.Context, lnd lndclient.LightningClient,
1505
1506
func hasBandwidth (channels []lndclient.ChannelInfo , amt btcutil.Amount ,
1506
1507
maxParts int ) (bool , int ) {
1507
1508
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 ))
1509
1513
var totalBandwidth btcutil.Amount
1510
1514
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
1512
1519
totalBandwidth += channel .LocalBalance
1513
1520
}
1514
1521
1522
+ log .Tracef ("Total bandwidth: %v" , totalBandwidth )
1515
1523
if totalBandwidth < amt {
1516
1524
return false , 0
1517
1525
}
1518
1526
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
+
1519
1535
split := amt
1520
1536
for shard := 0 ; shard <= maxParts ; {
1537
+ log .Tracef ("Trying to split %v sats into %v parts" , amt , shard )
1538
+
1521
1539
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
+
1525
1553
amt -= split
1554
+ log .Tracef ("len(shards)=%v: Remaining total " +
1555
+ "amount amt=%v" , shard , amt )
1556
+
1526
1557
paid = true
1527
1558
shard ++
1559
+
1528
1560
break
1529
1561
}
1530
1562
}
1531
1563
1564
+ logLocalBalances (shard )
1565
+
1532
1566
if amt == 0 {
1567
+ log .Tracef ("Payment is routable with %v part(s)" , shard )
1568
+
1533
1569
return true , shard
1534
1570
}
1535
1571
1536
1572
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
+
1537
1577
split /= 2
1538
1578
} else {
1579
+ log .Tracef ("len(shards)=%v: Payment was made, trying " +
1580
+ "to pay remaining sats %v" , shard , amt )
1581
+
1539
1582
split = amt
1540
1583
}
1541
1584
}
1542
1585
1586
+ log .Tracef ("Payment is not routable, remaining amount that can't be " +
1587
+ "sent: %v sats" , amt )
1588
+
1589
+ logLocalBalances (maxParts )
1590
+
1543
1591
return false , 0
1544
1592
}
1545
1593
0 commit comments