mod+chanfunding: use coin selection strategy for channel funding
The wallet assembler is now aware of the node config level coin selection strategy, so we can use it when creating new channels.
This commit is contained in:
8 files changed
+131
-114
No files matched your search
@@ -6,20 +6,20 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcwallet/wallet"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
)
|
||||
|
||||
// ErrInsufficientFunds is a type matching the error interface which is
|
||||
// returned when coin selection for a new funding transaction fails to due
|
||||
// returned when coin selection for a new funding transaction fails due to
|
||||
// having an insufficient amount of confirmed funds.
|
||||
type ErrInsufficientFunds struct {
|
||||
amountAvailable btcutil.Amount
|
||||
amountSelected btcutil.Amount
|
||||
}
|
||||
|
||||
// Error returns a human readable string describing the error.
|
||||
// Error returns a human-readable string describing the error.
|
||||
func (e *ErrInsufficientFunds) Error() string {
|
||||
return fmt.Sprintf("not enough witness outputs to create funding "+
|
||||
"transaction, need %v only have %v available",
|
||||
@@ -33,33 +33,33 @@ type errUnsupportedInput struct {
|
||||
PkScript []byte
|
||||
}
|
||||
|
||||
// Error returns a human readable string describing the error.
|
||||
// Error returns a human-readable string describing the error.
|
||||
func (e *errUnsupportedInput) Error() string {
|
||||
return fmt.Sprintf("unsupported address type: %x", e.PkScript)
|
||||
}
|
||||
|
||||
// Coin represents a spendable UTXO which is available for channel funding.
|
||||
// This UTXO need not reside in our internal wallet as an example, and instead
|
||||
// may be derived from an existing watch-only wallet. It wraps both the output
|
||||
// present within the UTXO set, and also the outpoint that generates this coin.
|
||||
type Coin struct {
|
||||
wire.TxOut
|
||||
|
||||
wire.OutPoint
|
||||
}
|
||||
|
||||
// selectInputs selects a slice of inputs necessary to meet the specified
|
||||
// selection amount. If input selection is unable to succeed due to insufficient
|
||||
// funds, a non-nil error is returned. Additionally, the total amount of the
|
||||
// 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) {
|
||||
func selectInputs(amt btcutil.Amount, coins []wallet.Coin,
|
||||
strategy wallet.CoinSelectionStrategy,
|
||||
feeRate chainfee.SatPerKWeight) (btcutil.Amount, []wallet.Coin, error) {
|
||||
|
||||
// All coin selection code in the btcwallet library requires sat/KB.
|
||||
feeSatPerKB := btcutil.Amount(feeRate.FeePerKVByte())
|
||||
|
||||
arrangedCoins, err := strategy.ArrangeCoins(coins, feeSatPerKB)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
satSelected := btcutil.Amount(0)
|
||||
for i, coin := range coins {
|
||||
for i, coin := range arrangedCoins {
|
||||
satSelected += btcutil.Amount(coin.Value)
|
||||
if satSelected >= amt {
|
||||
return satSelected, coins[:i+1], nil
|
||||
return satSelected, arrangedCoins[:i+1], nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +69,9 @@ func selectInputs(amt btcutil.Amount, coins []Coin) (btcutil.Amount, []Coin, err
|
||||
// calculateFees returns for the specified utxos and fee rate two fee
|
||||
// estimates, one calculated using a change output and one without. The weight
|
||||
// added to the estimator from a change output is for a P2WKH output.
|
||||
func calculateFees(utxos []Coin, feeRate chainfee.SatPerKWeight) (btcutil.Amount,
|
||||
btcutil.Amount, error) {
|
||||
func calculateFees(utxos []wallet.Coin,
|
||||
feeRate chainfee.SatPerKWeight) (btcutil.Amount, btcutil.Amount,
|
||||
error) {
|
||||
|
||||
var weightEstimate input.TxWeightEstimator
|
||||
for _, utxo := range utxos {
|
||||
@@ -129,13 +130,17 @@ func sanityCheckFee(totalOut, fee btcutil.Amount) error {
|
||||
// specified fee rate should be expressed in sat/kw for coin selection to
|
||||
// function properly.
|
||||
func CoinSelect(feeRate chainfee.SatPerKWeight, amt, dustLimit btcutil.Amount,
|
||||
coins []Coin) ([]Coin, btcutil.Amount, error) {
|
||||
coins []wallet.Coin,
|
||||
strategy wallet.CoinSelectionStrategy) ([]wallet.Coin, btcutil.Amount,
|
||||
error) {
|
||||
|
||||
amtNeeded := amt
|
||||
for {
|
||||
// First perform an initial round of coin selection to estimate
|
||||
// the required fee.
|
||||
totalSat, selectedUtxos, err := selectInputs(amtNeeded, coins)
|
||||
totalSat, selectedUtxos, err := selectInputs(
|
||||
amtNeeded, coins, strategy, feeRate,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -198,12 +203,15 @@ func CoinSelect(feeRate chainfee.SatPerKWeight, amt, dustLimit btcutil.Amount,
|
||||
// amt in total after fees, adhering to the specified fee rate. The selected
|
||||
// coins, the final output and change values are returned.
|
||||
func CoinSelectSubtractFees(feeRate chainfee.SatPerKWeight, amt,
|
||||
dustLimit btcutil.Amount, coins []Coin) ([]Coin, btcutil.Amount,
|
||||
dustLimit btcutil.Amount, coins []wallet.Coin,
|
||||
strategy wallet.CoinSelectionStrategy) ([]wallet.Coin, btcutil.Amount,
|
||||
btcutil.Amount, error) {
|
||||
|
||||
// First perform an initial round of coin selection to estimate
|
||||
// the required fee.
|
||||
totalSat, selectedUtxos, err := selectInputs(amt, coins)
|
||||
totalSat, selectedUtxos, err := selectInputs(
|
||||
amt, coins, strategy, feeRate,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
@@ -259,8 +267,9 @@ func CoinSelectSubtractFees(feeRate chainfee.SatPerKWeight, amt,
|
||||
// available. If insufficient funds are available this method selects all
|
||||
// available coins.
|
||||
func CoinSelectUpToAmount(feeRate chainfee.SatPerKWeight, minAmount, maxAmount,
|
||||
reserved, dustLimit btcutil.Amount, coins []Coin) ([]Coin,
|
||||
btcutil.Amount, btcutil.Amount, error) {
|
||||
reserved, dustLimit btcutil.Amount, coins []wallet.Coin,
|
||||
strategy wallet.CoinSelectionStrategy) ([]wallet.Coin, btcutil.Amount,
|
||||
btcutil.Amount, error) {
|
||||
|
||||
var (
|
||||
// selectSubtractFee is tracking if our coin selection was
|
||||
@@ -280,7 +289,7 @@ func CoinSelectUpToAmount(feeRate chainfee.SatPerKWeight, minAmount, 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,
|
||||
feeRate, maxAmount, dustLimit, coins, strategy,
|
||||
)
|
||||
|
||||
var errInsufficientFunds *ErrInsufficientFunds
|
||||
@@ -320,6 +329,7 @@ func CoinSelectUpToAmount(feeRate chainfee.SatPerKWeight, minAmount, maxAmount,
|
||||
if selectSubtractFee {
|
||||
selected, outputAmount, changeAmt, err = CoinSelectSubtractFees(
|
||||
feeRate, totalBalance-reserved, dustLimit, coins,
|
||||
strategy,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
@@ -329,7 +339,7 @@ func CoinSelectUpToAmount(feeRate chainfee.SatPerKWeight, minAmount, maxAmount,
|
||||
// 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 {
|
||||
sum := func(coins []wallet.Coin) btcutil.Amount {
|
||||
var sum btcutil.Amount
|
||||
for _, coin := range coins {
|
||||
sum += btcutil.Amount(coin.Value)
|
||||
|
||||
Reference in new issue
Block a user