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
|
#### 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,
|
* Taproot Asset Loop Out handling now validates RFQ timeouts and asset rates,
|
||||||
keeps cached asset-name lookups responsive during slow `tapd` queries, and
|
keeps cached asset-name lookups responsive during slow `tapd` queries, and
|
||||||
closes `tapd` connections cleanly during shutdown and startup failures.
|
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 amount requested, the maximum possible routing fees,
|
||||||
// the available channel set and the fact that equal splitting is
|
// the available channel set and the fact that equal splitting is
|
||||||
// used for MPP.
|
// 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)
|
requiredBalance := btcutil.Amount(req.Amt + req.MaxSwapRoutingFee)
|
||||||
isRoutable, _ := hasBandwidth(activeChannelSet, requiredBalance,
|
isRoutable, _ := hasBandwidth(activeChannelSet, requiredBalance,
|
||||||
int(maxParts))
|
int(maxParts))
|
||||||
|
|
@ -3047,11 +3054,24 @@ func hasBandwidth(channels []lndclient.ChannelInfo, amt btcutil.Amount,
|
||||||
localBalances := make([]btcutil.Amount, len(channels))
|
localBalances := make([]btcutil.Amount, len(channels))
|
||||||
var totalBandwidth btcutil.Amount
|
var totalBandwidth btcutil.Amount
|
||||||
for i, channel := range channels {
|
for i, channel := range channels {
|
||||||
tracef("Channel %v: local=%v remote=%v", channel.ChannelID,
|
localBalance := channel.LocalBalance
|
||||||
channel.LocalBalance, channel.RemoteBalance)
|
var reserve btcutil.Amount
|
||||||
|
if channel.LocalConstraints != nil {
|
||||||
|
reserve = channel.LocalConstraints.Reserve
|
||||||
|
}
|
||||||
|
|
||||||
localBalances[i] = channel.LocalBalance
|
if reserve >= localBalance {
|
||||||
totalBandwidth += channel.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)
|
tracef("Total bandwidth: %v", totalBandwidth)
|
||||||
|
|
@ -3073,8 +3093,6 @@ func hasBandwidth(channels []lndclient.ChannelInfo, amt btcutil.Amount,
|
||||||
|
|
||||||
paid := false
|
paid := false
|
||||||
for i := range len(localBalances) {
|
for i := range len(localBalances) {
|
||||||
// TODO(hieblmi): Consider channel reserves because the
|
|
||||||
// channel can't send its full local balance.
|
|
||||||
if localBalances[i] >= split {
|
if localBalances[i] >= split {
|
||||||
tracef("len(shards)=%v: Local channel "+
|
tracef("len(shards)=%v: Local channel "+
|
||||||
"balance %v can pay %v sats",
|
"balance %v can pay %v sats",
|
||||||
|
|
|
||||||
|
|
@ -1378,6 +1378,50 @@ func TestValidateLoopOutRequest(t *testing.T) {
|
||||||
err: errBalanceTooLow,
|
err: errBalanceTooLow,
|
||||||
expectedTarget: 0,
|
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",
|
name: "can split between channels",
|
||||||
chain: chaincfg.MainNetParams,
|
chain: chaincfg.MainNetParams,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue