diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index be273452..0c42aa4d 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -504,13 +504,13 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg // don't need to perform any coin selection. Otherwise, attempt to // obtain enough coins to meet the required funding amount. if req.fundingAmount != 0 { - // Coin selection is done on the basis of sat-per-byte, so + // Coin selection is done on the basis of sat-per-weight, so // we'll query the fee estimator for a fee to use to ensure the // funding transaction gets into the _next_ block. // // TODO(roasbeef): shouldn't be targeting next block - satPerByte := l.Cfg.FeeEstimator.EstimateFeePerByte(1) - err := l.selectCoinsAndChange(satPerByte, req.fundingAmount, + satPerWeight := l.Cfg.FeeEstimator.EstimateFeePerWeight(1) + err := l.selectCoinsAndChange(satPerWeight, req.fundingAmount, reservation.ourContribution) if err != nil { req.err <- err @@ -1039,20 +1039,19 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // Next, create the spending scriptSig, and then verify that the script // is complete, allowing us to spend from the funding transaction. - theirCommitSig := msg.theirCommitmentSig channelValue := int64(res.partialState.Capacity) hashCache := txscript.NewTxSigHashes(&commitTx) sigHash, err := txscript.CalcWitnessSigHash(witnessScript, hashCache, txscript.SigHashAll, &commitTx, 0, channelValue) if err != nil { - msg.err <- fmt.Errorf("counterparty's commitment signature is "+ - "invalid: %v", err) + msg.err <- err msg.completeChan <- nil return } // Verify that we've received a valid signature from the remote party // for our version of the commitment transaction. + theirCommitSig := msg.theirCommitmentSig sig, err := btcec.ParseSignature(theirCommitSig, btcec.S256()) if err != nil { msg.err <- err @@ -1268,8 +1267,8 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { // within the passed contribution's inputs. If necessary, a change address will // also be generated. // TODO(roasbeef): remove hardcoded fees and req'd confs for outputs. -func (l *LightningWallet) selectCoinsAndChange(feeRate uint64, amt btcutil.Amount, - contribution *ChannelContribution) error { +func (l *LightningWallet) selectCoinsAndChange(feeRatePerWeight uint64, + amt btcutil.Amount, contribution *ChannelContribution) error { // We hold the coin select mutex while querying for outputs, and // performing coin selection in order to avoid inadvertent double @@ -1277,8 +1276,8 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate uint64, amt btcutil.Amoun l.coinSelectMtx.Lock() defer l.coinSelectMtx.Unlock() - walletLog.Infof("Performing coin selection using %v sat/byte as fee "+ - "rate", feeRate) + walletLog.Infof("Performing coin selection using %v sat/weight as fee "+ + "rate", feeRatePerWeight) // Find all unlocked unspent witness outputs with greater than 1 // confirmation. @@ -1291,7 +1290,7 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate uint64, amt btcutil.Amoun // Perform coin selection over our available, unlocked unspent outputs // in order to find enough coins to meet the funding amount // requirements. - selectedCoins, changeAmt, err := coinSelect(feeRate, amt, coins) + selectedCoins, changeAmt, err := coinSelect(feeRatePerWeight, amt, coins) if err != nil { return err } @@ -1419,33 +1418,9 @@ func selectInputs(amt btcutil.Amount, coins []*Utxo) (btcutil.Amount, []*wire.Ou // change output to fund amt satoshis, adhering to the specified fee rate. The // specified fee rate should be expressed in sat/byte for coin selection to // function properly. -func coinSelect(feeRate uint64, amt btcutil.Amount, +func coinSelect(feeRatePerWeight uint64, amt btcutil.Amount, coins []*Utxo) ([]*wire.OutPoint, btcutil.Amount, error) { - const ( - // txOverhead is the overhead of a transaction residing within - // the version number and lock time. - txOverhead = 8 - - // p2wkhSpendSize an estimate of the number of bytes it takes - // to spend a p2wkh output. - // - // (p2wkh witness) + txid + index + varint script size + sequence - // TODO(roasbeef): div by 3 due to witness size? - p2wkhSpendSize = (1 + 73 + 1 + 33) + 32 + 4 + 1 + 4 - - // p2wkhOutputSize is an estimate of the size of a regualr - // p2wkh output. - // - // 8 (output) + 1 (var int script) + 22 (p2wkh output) - p2wkhOutputSize = 8 + 1 + 22 - - // p2wkhOutputSize is an estimate of the p2wsh funding uotput. - p2wshOutputSize = 8 + 1 + 34 - ) - - var estimatedSize int - amtNeeded := amt for { // First perform an initial round of coin selection to estimate @@ -1455,10 +1430,20 @@ func coinSelect(feeRate uint64, amt btcutil.Amount, return nil, 0, err } - // Based on the selected coins, estimate the size of the final - // fully signed transaction. - estimatedSize = ((len(selectedUtxos) * p2wkhSpendSize) + - p2wshOutputSize + txOverhead) + var weightEstimate TxWeightEstimator + + for range selectedUtxos { + // Assume all selected inputs are P2WKH inputs. + // TODO: Handle wallets that have non-witness UTXOs. + weightEstimate.AddP2WKHInput() + } + + // Channel funding multisig output is P2WSH. + weightEstimate.AddP2WSHOutput() + + // Assume that change output is a P2WKH output. + // TODO: Handle wallets that generate non-witness change addresses. + weightEstimate.AddP2WKHOutput() // The difference between the selected amount and the amount // requested will be used to pay fees, and generate a change @@ -1469,9 +1454,10 @@ func coinSelect(feeRate uint64, amt btcutil.Amount, // amount isn't enough to pay fees, then increase the requested // coin amount by the estimate required fee, performing another // round of coin selection. - requiredFee := btcutil.Amount(uint64(estimatedSize) * feeRate) + requiredFee := btcutil.Amount( + uint64(weightEstimate.Weight()) * feeRatePerWeight) if overShootAmt < requiredFee { - amtNeeded += requiredFee + amtNeeded = amt + requiredFee continue }