mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Merge pull request #1193 from starius/loop-out-validation-reserves
loopd: account for channel reserves in loop out validation
This commit is contained in:
commit
aa79fa5262
3 changed files with 72 additions and 6 deletions
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
#### Bug Fixes
|
||||
|
||||
* Loop Out requests now account for channel reserves when checking outbound
|
||||
capacity, preventing swaps from starting when their off-chain payment cannot
|
||||
be funded.
|
||||
|
||||
* Taproot Asset Loop Out handling now validates RFQ timeouts and asset rates,
|
||||
keeps cached asset-name lookups responsive during slow `tapd` queries, and
|
||||
closes `tapd` connections cleanly during shutdown and startup failures.
|
||||
|
|
|
|||
|
|
@ -3013,6 +3013,13 @@ func validateLoopOutRequest(ctx context.Context, lnd lndclient.LightningClient,
|
|||
// the amount requested, the maximum possible routing fees,
|
||||
// the available channel set and the fact that equal splitting is
|
||||
// used for MPP.
|
||||
//
|
||||
// TODO: Also account for the quoted server fee and the concurrent prepay
|
||||
// payment. The CLI and autoloop obtain the fee and prepay amounts from a
|
||||
// quote, but this RPC only carries maximum limits, which direct callers may
|
||||
// set higher than the quoted amounts. Treating those limits as exact can
|
||||
// reject a viable swap, while the actual routing fees are only known when
|
||||
// the invoices are paid.
|
||||
requiredBalance := btcutil.Amount(req.Amt + req.MaxSwapRoutingFee)
|
||||
isRoutable, _ := hasBandwidth(activeChannelSet, requiredBalance,
|
||||
int(maxParts))
|
||||
|
|
@ -3047,11 +3054,24 @@ func hasBandwidth(channels []lndclient.ChannelInfo, amt btcutil.Amount,
|
|||
localBalances := make([]btcutil.Amount, len(channels))
|
||||
var totalBandwidth btcutil.Amount
|
||||
for i, channel := range channels {
|
||||
tracef("Channel %v: local=%v remote=%v", channel.ChannelID,
|
||||
channel.LocalBalance, channel.RemoteBalance)
|
||||
localBalance := channel.LocalBalance
|
||||
var reserve btcutil.Amount
|
||||
if channel.LocalConstraints != nil {
|
||||
reserve = channel.LocalConstraints.Reserve
|
||||
}
|
||||
|
||||
localBalances[i] = channel.LocalBalance
|
||||
totalBandwidth += channel.LocalBalance
|
||||
if reserve >= localBalance {
|
||||
localBalance = 0
|
||||
} else {
|
||||
localBalance -= reserve
|
||||
}
|
||||
|
||||
tracef("Channel %v: local=%v reserve=%v available=%v "+
|
||||
"remote=%v", channel.ChannelID, channel.LocalBalance,
|
||||
reserve, localBalance, channel.RemoteBalance)
|
||||
|
||||
localBalances[i] = localBalance
|
||||
totalBandwidth += localBalance
|
||||
}
|
||||
|
||||
tracef("Total bandwidth: %v", totalBandwidth)
|
||||
|
|
@ -3073,8 +3093,6 @@ func hasBandwidth(channels []lndclient.ChannelInfo, amt btcutil.Amount,
|
|||
|
||||
paid := false
|
||||
for i := range len(localBalances) {
|
||||
// TODO(hieblmi): Consider channel reserves because the
|
||||
// channel can't send its full local balance.
|
||||
if localBalances[i] >= split {
|
||||
tracef("len(shards)=%v: Local channel "+
|
||||
"balance %v can pay %v sats",
|
||||
|
|
|
|||
|
|
@ -1378,6 +1378,50 @@ func TestValidateLoopOutRequest(t *testing.T) {
|
|||
err: errBalanceTooLow,
|
||||
expectedTarget: 0,
|
||||
},
|
||||
{
|
||||
name: "channel reserve leaves balance one sat short",
|
||||
chain: chaincfg.MainNetParams,
|
||||
destAddr: mainnetAddr,
|
||||
label: "label ok",
|
||||
confTarget: 2,
|
||||
channels: []lndclient.ChannelInfo{
|
||||
{
|
||||
Active: true,
|
||||
ChannelID: chanID2.ToUint64(),
|
||||
LocalBalance: 10100,
|
||||
LocalConstraints: &lndclient.ChannelConstraints{
|
||||
Reserve: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
amount: 10000,
|
||||
maxRoutingFee: 1,
|
||||
maxParts: 1,
|
||||
err: errBalanceTooLow,
|
||||
expectedTarget: 0,
|
||||
},
|
||||
{
|
||||
name: "channel reserve leaves exact balance",
|
||||
chain: chaincfg.MainNetParams,
|
||||
destAddr: mainnetAddr,
|
||||
label: "label ok",
|
||||
confTarget: 2,
|
||||
channels: []lndclient.ChannelInfo{
|
||||
{
|
||||
Active: true,
|
||||
ChannelID: chanID2.ToUint64(),
|
||||
LocalBalance: 10101,
|
||||
LocalConstraints: &lndclient.ChannelConstraints{
|
||||
Reserve: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
amount: 10000,
|
||||
maxRoutingFee: 1,
|
||||
maxParts: 1,
|
||||
err: nil,
|
||||
expectedTarget: 2,
|
||||
},
|
||||
{
|
||||
name: "can split between channels",
|
||||
chain: chaincfg.MainNetParams,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue