mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-14 12:43:03 +02:00
Replace the recursive full-deposit autoloop selector with a bounded-memory DP implementation in staticaddr/loopin/autoloop_dp.go. The new selector keeps the existing no-change semantics, first finds the best reachable total, then applies the 25 percent band rule so earlier-expiring deposits can win inside that near-optimal range. The DP table is capped at 128 MiB and keeps exact satoshi sums alongside compressed bucket weights, so planning stays memory-bounded without allowing oversized candidates. The compressed weighting now rounds down with a minimum of one bucket, which avoids rejecting valid sums after multiple per-deposit rounding steps while leaving the exact-sum check as the real safety boundary.
96 lines
3 KiB
Go
96 lines
3 KiB
Go
package loopin
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/lightninglabs/loop"
|
|
"github.com/lightninglabs/loop/staticaddr/deposit"
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
|
)
|
|
|
|
var (
|
|
// ErrNoAutoloopCandidate is returned when the static-address side
|
|
// cannot build a full-deposit, no-change loop-in candidate that fits
|
|
// the planner's requested amount bounds.
|
|
ErrNoAutoloopCandidate = errors.New("no autoloop candidate")
|
|
)
|
|
|
|
// PrepareAutoloopLoopIn builds a static-address loop-in request for autoloop
|
|
// without dispatching it. The returned request always uses full deposits,
|
|
// explicit outpoints, and an explicit selected amount, so the caller can
|
|
// account for the suggestion without depending on static-address internals.
|
|
func (m *Manager) PrepareAutoloopLoopIn(ctx context.Context,
|
|
lastHop route.Vertex, minAmount, maxAmount btcutil.Amount, label,
|
|
initiator string, excludedOutpoints []string) (
|
|
*loop.StaticAddressLoopInRequest, int, bool, error) {
|
|
|
|
if minAmount <= 0 || maxAmount < minAmount {
|
|
return nil, 0, false, ErrNoAutoloopCandidate
|
|
}
|
|
|
|
allDeposits, err := m.cfg.DepositManager.GetActiveDepositsInState(
|
|
deposit.Deposited,
|
|
)
|
|
if err != nil {
|
|
return nil, 0, false, err
|
|
}
|
|
|
|
params, err := m.cfg.AddressManager.GetStaticAddressParameters(ctx)
|
|
if err != nil {
|
|
return nil, 0, false, err
|
|
}
|
|
|
|
excluded := make(map[string]struct{}, len(excludedOutpoints))
|
|
for _, outpoint := range excludedOutpoints {
|
|
excluded[outpoint] = struct{}{}
|
|
}
|
|
|
|
selectedDeposits, err := selectNoChangeDeposits(
|
|
maxAmount, minAmount, allDeposits, params.Expiry,
|
|
m.currentHeight.Load(), excluded,
|
|
)
|
|
if err != nil {
|
|
return nil, 0, false, err
|
|
}
|
|
|
|
selectedAmount := sumOfDeposits(selectedDeposits)
|
|
quote, err := m.cfg.QuoteGetter.GetLoopInQuote(
|
|
ctx, selectedAmount, m.cfg.NodePubkey, &lastHop, nil,
|
|
initiator, uint32(len(selectedDeposits)), false,
|
|
)
|
|
if err != nil {
|
|
return nil, 0, false, err
|
|
}
|
|
|
|
outpoints := make([]string, 0, len(selectedDeposits))
|
|
for _, selectedDeposit := range selectedDeposits {
|
|
outpoints = append(outpoints, selectedDeposit.OutPoint.String())
|
|
}
|
|
|
|
request := &loop.StaticAddressLoopInRequest{
|
|
DepositOutpoints: outpoints,
|
|
SelectedAmount: selectedAmount,
|
|
MaxSwapFee: quote.SwapFee,
|
|
LastHop: &lastHop,
|
|
Label: label,
|
|
Initiator: initiator,
|
|
Fast: false,
|
|
}
|
|
|
|
return request, len(selectedDeposits), false, nil
|
|
}
|
|
|
|
// selectNoChangeDeposits chooses the highest-value swappable deposit set whose
|
|
// full value stays within the requested range. The selector never creates
|
|
// change, so the returned set's total is the actual swap amount.
|
|
func selectNoChangeDeposits(maxAmount, minAmount btcutil.Amount,
|
|
unfilteredDeposits []*deposit.Deposit, csvExpiry, blockHeight uint32,
|
|
excludedOutpoints map[string]struct{}) ([]*deposit.Deposit, error) {
|
|
|
|
return selectNoChangeDepositsWithMemoryBudget(
|
|
maxAmount, minAmount, unfilteredDeposits, csvExpiry,
|
|
blockHeight, excludedOutpoints, autoloopDPMaxMemoryBytes,
|
|
)
|
|
}
|