chanfunding: adds ability to fund up to some maximum amount
Allows to define a maximum amount to provision a channel opening with using a new field `FundUpToMaxAmt` on the `Request` struct. Also adds a new coin select function `CoinSelectUpToAmount` to select coins up to a maximum amount respecting a minimum amount.
This commit is contained in:
1 parent
f97454d4ff
commit
0c8aa81ed5
6 files changed
+370
-7
No files matched your search
@@ -1,6 +1,7 @@
|
||||
package chanfunding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
@@ -53,6 +54,7 @@ type Coin struct {
|
||||
// selected coins are returned in order for the caller to properly handle
|
||||
// change+fees.
|
||||
func selectInputs(amt btcutil.Amount, coins []Coin) (btcutil.Amount, []Coin, error) {
|
||||
|
||||
satSelected := btcutil.Amount(0)
|
||||
for i, coin := range coins {
|
||||
satSelected += btcutil.Amount(coin.Value)
|
||||
@@ -251,3 +253,78 @@ func CoinSelectSubtractFees(feeRate chainfee.SatPerKWeight, amt,
|
||||
|
||||
return selectedUtxos, outputAmt, changeAmt, nil
|
||||
}
|
||||
|
||||
// CoinSelectUpToAmount attempts to select coins such that we'll select up to
|
||||
// maxAmount exclusive of fees if sufficient funds are available. If
|
||||
// insufficient funds are available this method selects all available coins.
|
||||
func CoinSelectUpToAmount(feeRate chainfee.SatPerKWeight, minAmount, maxAmount,
|
||||
dustLimit btcutil.Amount, coins []Coin) ([]Coin, btcutil.Amount,
|
||||
btcutil.Amount, error) {
|
||||
|
||||
var (
|
||||
// selectSubtractFee is tracking if our coin selection was
|
||||
// unsuccessful and whether we have to start a new round of
|
||||
// selecting coins considering fees.
|
||||
selectSubtractFee = false
|
||||
outputAmount = maxAmount
|
||||
)
|
||||
|
||||
// First we try to select coins to create an output of the specified
|
||||
// maxAmount with or without a change output that covers the miner fee.
|
||||
selected, changeAmt, err := CoinSelect(
|
||||
feeRate, maxAmount, dustLimit, coins,
|
||||
)
|
||||
|
||||
var errInsufficientFunds *ErrInsufficientFunds
|
||||
if errors.As(err, &errInsufficientFunds) {
|
||||
// If the initial coin selection fails due to insufficient funds
|
||||
// we select our total available balance minus fees.
|
||||
selectSubtractFee = true
|
||||
} else if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// If we determined that our local balance is insufficient we check our
|
||||
// total balance minus fees.
|
||||
if selectSubtractFee {
|
||||
// Get balance from coins.
|
||||
var totalBalance btcutil.Amount
|
||||
for _, coin := range coins {
|
||||
totalBalance += btcutil.Amount(coin.Value)
|
||||
}
|
||||
|
||||
selected, outputAmount, changeAmt, err = CoinSelectSubtractFees(
|
||||
feeRate, totalBalance, dustLimit, coins,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check the resulting output values to make sure we don't burn a
|
||||
// great part to fees.
|
||||
totalOut := outputAmount + changeAmt
|
||||
sum := func(coins []Coin) btcutil.Amount {
|
||||
var sum btcutil.Amount
|
||||
for _, coin := range coins {
|
||||
sum += btcutil.Amount(coin.Value)
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
||||
err = sanityCheckFee(totalOut, sum(selected)-totalOut)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// In case the selected amount is lower than minimum funding amount we
|
||||
// must return an error. The minimum funding amount is determined
|
||||
// upstream and denotes either the minimum viable channel size or an
|
||||
// amount sufficient to cover for the initial remote balance.
|
||||
if outputAmount < minAmount {
|
||||
return nil, 0, 0, fmt.Errorf("available funds(%v) below the "+
|
||||
"minimum amount(%v)", outputAmount, minAmount)
|
||||
}
|
||||
|
||||
return selected, outputAmount, changeAmt, nil
|
||||
}
|
||||
Reference in new issue
Block a user