lnwallet: use distinct fee rate for funding transaction coin selection
This commit modifies the coin selection logic around selecting inputs for a funding transaction to query the fee estimator directly (and use fee-per-byte), rather than use the fee estimate which was passed into the context. We also use the value passed into the InitChannelReservation method directly rather than make a call to the fee estimator. With this change, the responder to a funding workflow will now properly adopt the fee-per-kw suggested by the funder of the channel.
This commit is contained in:
1 file changed
+45
-34
+45
-34
@@ -74,31 +74,31 @@ func (e *ErrInsufficientFunds) Error() string {
|
|||||||
//
|
//
|
||||||
// TODO(roasbeef): zombie reservation sweeper goroutine.
|
// TODO(roasbeef): zombie reservation sweeper goroutine.
|
||||||
type initFundingReserveMsg struct {
|
type initFundingReserveMsg struct {
|
||||||
// The ID of the remote node we would like to open a channel with.
|
// nodeId is the ID of the remote node we would like to open a channel
|
||||||
|
// with.
|
||||||
nodeID *btcec.PublicKey
|
nodeID *btcec.PublicKey
|
||||||
|
|
||||||
// The IP address plus port that we used to either establish or accept
|
// nodeAddr is the IP address plus port that we used to either
|
||||||
// the connection which led to the negotiation of this funding
|
// establish or accept the connection which led to the negotiation of
|
||||||
// workflow.
|
// this funding workflow.
|
||||||
nodeAddr *net.TCPAddr
|
nodeAddr *net.TCPAddr
|
||||||
|
|
||||||
// The number of confirmations required before the channel is considered
|
// numConfs is the number of confirmations required before the channel
|
||||||
// open.
|
// is considered open.
|
||||||
numConfs uint16
|
numConfs uint16
|
||||||
|
|
||||||
// The amount of funds requested for this channel.
|
// fundingAmount is the amount of funds requested for this channel.
|
||||||
fundingAmount btcutil.Amount
|
fundingAmount btcutil.Amount
|
||||||
|
|
||||||
// The total capacity of the channel which includes the amount of funds
|
// capacity is the total capacity of the channel which includes the
|
||||||
// the remote party contributes (if any).
|
// amount of funds the remote party contributes (if any).
|
||||||
capacity btcutil.Amount
|
capacity btcutil.Amount
|
||||||
|
|
||||||
// The minimum accepted satoshis/KB fee for the funding transaction. In
|
// feePerKw is the accepted satoshis/Kw fee for the funding
|
||||||
// order to ensure timely confirmation, it is recomened that this fee
|
// transaction. In order to ensure timely confirmation, it is
|
||||||
// should be generous, paying some multiple of the accepted base fee
|
// recommended that this fee should be generous, paying some multiple
|
||||||
// rate of the network.
|
// of the accepted base fee rate of the network.
|
||||||
// TODO(roasbeef): integrate fee estimation project...
|
feePerKw btcutil.Amount
|
||||||
minFeeRate btcutil.Amount
|
|
||||||
|
|
||||||
// ourDustLimit is the threshold below which no HTLC output should be
|
// ourDustLimit is the threshold below which no HTLC output should be
|
||||||
// generated for our commitment transaction; ie. HTLCs below
|
// generated for our commitment transaction; ie. HTLCs below
|
||||||
@@ -109,17 +109,21 @@ type initFundingReserveMsg struct {
|
|||||||
// responder as part of the initial channel creation.
|
// responder as part of the initial channel creation.
|
||||||
pushSat btcutil.Amount
|
pushSat btcutil.Amount
|
||||||
|
|
||||||
// The delay on the "pay-to-self" output(s) of the commitment transaction.
|
// csvDelay is the delay on the "pay-to-self" output(s) of the
|
||||||
|
// commitment transaction.
|
||||||
csvDelay uint32
|
csvDelay uint32
|
||||||
|
|
||||||
// A channel in which all errors will be sent accross. Will be nil if
|
// err is a channel in which all errors will be sent across. Will be
|
||||||
// this initial set is succesful.
|
// nil if this initial set is successful.
|
||||||
|
//
|
||||||
// NOTE: In order to avoid deadlocks, this channel MUST be buffered.
|
// NOTE: In order to avoid deadlocks, this channel MUST be buffered.
|
||||||
err chan error
|
err chan error
|
||||||
|
|
||||||
// A ChannelReservation with our contributions filled in will be sent
|
// resp is channel in which a ChannelReservation with our contributions
|
||||||
// accross this channel in the case of a succesfully reservation
|
// filled in will be sent across this channel in the case of a
|
||||||
// initiation. In the case of an error, this will read a nil pointer.
|
// successfully reservation initiation. In the case of an error, this
|
||||||
|
// will read a nil pointer.
|
||||||
|
//
|
||||||
// NOTE: In order to avoid deadlocks, this channel MUST be buffered.
|
// NOTE: In order to avoid deadlocks, this channel MUST be buffered.
|
||||||
resp chan *ChannelReservation
|
resp chan *ChannelReservation
|
||||||
}
|
}
|
||||||
@@ -487,16 +491,15 @@ out:
|
|||||||
// commitment transaction is valid.
|
// commitment transaction is valid.
|
||||||
func (l *LightningWallet) InitChannelReservation(capacity,
|
func (l *LightningWallet) InitChannelReservation(capacity,
|
||||||
ourFundAmt btcutil.Amount, theirID *btcec.PublicKey,
|
ourFundAmt btcutil.Amount, theirID *btcec.PublicKey,
|
||||||
theirAddr *net.TCPAddr, numConfs uint16,
|
theirAddr *net.TCPAddr, numConfs uint16, csvDelay uint32,
|
||||||
csvDelay uint32, ourDustLimit btcutil.Amount,
|
ourDustLimit btcutil.Amount, pushSat btcutil.Amount,
|
||||||
pushSat btcutil.Amount) (*ChannelReservation, error) {
|
feePerKw btcutil.Amount) (*ChannelReservation, error) {
|
||||||
|
|
||||||
// TODO(roasbeef): make the above into an initial config as part of the
|
// TODO(roasbeef): make the above into an initial config as part of the
|
||||||
// refactor to implement spec compliant funding flow
|
// refactor to implement spec compliant funding flow
|
||||||
|
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
respChan := make(chan *ChannelReservation, 1)
|
respChan := make(chan *ChannelReservation, 1)
|
||||||
minFeeRate := btcutil.Amount(l.FeeEstimator.EstimateFeePerWeight(1))
|
|
||||||
|
|
||||||
l.msgChan <- &initFundingReserveMsg{
|
l.msgChan <- &initFundingReserveMsg{
|
||||||
capacity: capacity,
|
capacity: capacity,
|
||||||
@@ -504,7 +507,7 @@ func (l *LightningWallet) InitChannelReservation(capacity,
|
|||||||
fundingAmount: ourFundAmt,
|
fundingAmount: ourFundAmt,
|
||||||
csvDelay: csvDelay,
|
csvDelay: csvDelay,
|
||||||
ourDustLimit: ourDustLimit,
|
ourDustLimit: ourDustLimit,
|
||||||
minFeeRate: minFeeRate,
|
feePerKw: feePerKw,
|
||||||
pushSat: pushSat,
|
pushSat: pushSat,
|
||||||
nodeID: theirID,
|
nodeID: theirID,
|
||||||
nodeAddr: theirAddr,
|
nodeAddr: theirAddr,
|
||||||
@@ -528,7 +531,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
|
|||||||
|
|
||||||
id := atomic.AddUint64(&l.nextFundingID, 1)
|
id := atomic.AddUint64(&l.nextFundingID, 1)
|
||||||
reservation := NewChannelReservation(req.capacity, req.fundingAmount,
|
reservation := NewChannelReservation(req.capacity, req.fundingAmount,
|
||||||
req.minFeeRate, l, id, req.numConfs, req.pushSat)
|
req.feePerKw, l, id, req.numConfs, req.pushSat)
|
||||||
|
|
||||||
// Grab the mutex on the ChannelReservation to ensure thread-safety
|
// Grab the mutex on the ChannelReservation to ensure thread-safety
|
||||||
reservation.Lock()
|
reservation.Lock()
|
||||||
@@ -549,10 +552,14 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
|
|||||||
// don't need to perform any coin selection. Otherwise, attempt to
|
// don't need to perform any coin selection. Otherwise, attempt to
|
||||||
// obtain enough coins to meet the required funding amount.
|
// obtain enough coins to meet the required funding amount.
|
||||||
if req.fundingAmount != 0 {
|
if req.fundingAmount != 0 {
|
||||||
// TODO(roasbeef): consult model for proper fee rate on funding
|
// Coin selection is done on the basis of sat-per-byte, so
|
||||||
// tx
|
// we'll query the fee estimator for a fee to use to ensure the
|
||||||
err := l.selectCoinsAndChange(uint64(req.minFeeRate),
|
// funding transaction gets into the _next_ block.
|
||||||
req.fundingAmount, ourContribution)
|
//
|
||||||
|
// TODO(roasbeef): shouldn't be targeting next block
|
||||||
|
satPerByte := l.FeeEstimator.EstimateFeePerByte(1)
|
||||||
|
err := l.selectCoinsAndChange(satPerByte, req.fundingAmount,
|
||||||
|
ourContribution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.err <- err
|
req.err <- err
|
||||||
req.resp <- nil
|
req.resp <- nil
|
||||||
@@ -1212,7 +1219,8 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
|||||||
|
|
||||||
// Add the complete funding transaction to the DB, in it's open bucket
|
// Add the complete funding transaction to the DB, in it's open bucket
|
||||||
// which will be used for the lifetime of this channel.
|
// which will be used for the lifetime of this channel.
|
||||||
if err := pendingReservation.partialState.SyncPending(pendingReservation.nodeAddr); err != nil {
|
err = pendingReservation.partialState.SyncPending(pendingReservation.nodeAddr)
|
||||||
|
if err != nil {
|
||||||
req.err <- err
|
req.err <- err
|
||||||
req.completeChan <- nil
|
req.completeChan <- nil
|
||||||
return
|
return
|
||||||
@@ -1241,9 +1249,12 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate uint64, amt btcutil.Amoun
|
|||||||
l.coinSelectMtx.Lock()
|
l.coinSelectMtx.Lock()
|
||||||
defer l.coinSelectMtx.Unlock()
|
defer l.coinSelectMtx.Unlock()
|
||||||
|
|
||||||
|
walletLog.Infof("Performing coin selection using %v sat/byte as fee "+
|
||||||
|
"rate", feeRate)
|
||||||
|
|
||||||
// Find all unlocked unspent witness outputs with greater than 1
|
// Find all unlocked unspent witness outputs with greater than 1
|
||||||
// confirmation.
|
// confirmation.
|
||||||
// TODO(roasbeef): make num confs a configuration paramter
|
// TODO(roasbeef): make num confs a configuration parameter
|
||||||
coins, err := l.ListUnspentWitness(1)
|
coins, err := l.ListUnspentWitness(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -1442,7 +1453,7 @@ func coinSelect(feeRate uint64, amt btcutil.Amount,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StaticFeeEstimator will return a static value for all fee calculation
|
// StaticFeeEstimator will return a static value for all fee calculation
|
||||||
// requests. It is designed to be replaced by a proper fee calcuation
|
// requests. It is designed to be replaced by a proper fee calculation
|
||||||
// implementation.
|
// implementation.
|
||||||
type StaticFeeEstimator struct {
|
type StaticFeeEstimator struct {
|
||||||
FeeRate uint64
|
FeeRate uint64
|
||||||
|
|||||||
Reference in new issue
Block a user