sweep: wrap weight estimator

Preparation for a cpfp-aware weight estimator. For cpfp, a regular
weight estimator isn't sufficient, because it needs to take into account
the weight of the parent transaction(s) as well.
This commit is contained in:
Joost Jager 2020-09-04 14:03:14 +02:00
parent 2ebfb64b9b
commit 3e3d8487fb
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
3 changed files with 61 additions and 24 deletions

@ -33,7 +33,7 @@ const (
type txInputSetState struct { type txInputSetState struct {
// weightEstimate is the (worst case) tx weight with the current set of // weightEstimate is the (worst case) tx weight with the current set of
// inputs. // inputs.
weightEstimate input.TxWeightEstimator weightEstimate *weightEstimator
// inputTotal is the total value of all inputs. // inputTotal is the total value of all inputs.
inputTotal btcutil.Amount inputTotal btcutil.Amount
@ -54,7 +54,7 @@ type txInputSetState struct {
func (t *txInputSetState) clone() txInputSetState { func (t *txInputSetState) clone() txInputSetState {
s := txInputSetState{ s := txInputSetState{
weightEstimate: t.weightEstimate, weightEstimate: t.weightEstimate.clone(),
inputTotal: t.inputTotal, inputTotal: t.inputTotal,
outputValue: t.outputValue, outputValue: t.outputValue,
walletInputTotal: t.walletInputTotal, walletInputTotal: t.walletInputTotal,
@ -95,15 +95,20 @@ func newTxInputSet(wallet Wallet, feePerKW,
btcutil.Amount(relayFee.FeePerKVByte()), btcutil.Amount(relayFee.FeePerKVByte()),
) )
state := txInputSetState{
weightEstimate: newWeightEstimator(),
}
b := txInputSet{ b := txInputSet{
feePerKW: feePerKW, feePerKW: feePerKW,
dustLimit: dustLimit, dustLimit: dustLimit,
maxInputs: maxInputs, maxInputs: maxInputs,
wallet: wallet, wallet: wallet,
txInputSetState: state,
} }
// Add the sweep tx output to the weight estimate. // Add the sweep tx output to the weight estimate.
b.weightEstimate.AddP2WKHOutput() b.weightEstimate.addP2WKHOutput()
return &b return &b
} }
@ -126,29 +131,22 @@ func (t *txInputSet) addToState(inp input.Input, constraints addConstraints) *tx
return nil return nil
} }
// Can ignore error, because it has already been checked when
// calculating the yields.
size, isNestedP2SH, _ := inp.WitnessType().SizeUpperBound()
// Clone the current set state. // Clone the current set state.
s := t.clone() s := t.clone()
// Add the new input. // Add the new input.
s.inputs = append(s.inputs, inp) s.inputs = append(s.inputs, inp)
// Add weight of the new input. // Can ignore error, because it has already been checked when
if isNestedP2SH { // calculating the yields.
s.weightEstimate.AddNestedP2WSHInput(size) _ = s.weightEstimate.add(inp)
} else {
s.weightEstimate.AddWitnessInput(size)
}
// Add the value of the new input. // Add the value of the new input.
value := btcutil.Amount(inp.SignDesc().Output.Value) value := btcutil.Amount(inp.SignDesc().Output.Value)
s.inputTotal += value s.inputTotal += value
// Recalculate the tx fee. // Recalculate the tx fee.
weight := s.weightEstimate.Weight() weight := s.weightEstimate.weight()
fee := t.feePerKW.FeeForWeight(int64(weight)) fee := t.feePerKW.FeeForWeight(int64(weight))
// Calculate the new output value. // Calculate the new output value.

@ -120,7 +120,7 @@ func generateInputPartitionings(sweepableInputs []txInput,
"has yield=%v, weight=%v", "has yield=%v, weight=%v",
inputCount, len(txInputs.inputs)-inputCount, inputCount, len(txInputs.inputs)-inputCount,
txInputs.outputValue-txInputs.walletInputTotal, txInputs.outputValue-txInputs.walletInputTotal,
txInputs.weightEstimate.Weight()) txInputs.weightEstimate.weight())
sets = append(sets, txInputs.inputs) sets = append(sets, txInputs.inputs)
sweepableInputs = sweepableInputs[inputCount:] sweepableInputs = sweepableInputs[inputCount:]
@ -222,11 +222,11 @@ func getWeightEstimate(inputs []input.Input) ([]input.Input, int64) {
// //
// TODO(roasbeef): can be more intelligent about buffering outputs to // TODO(roasbeef): can be more intelligent about buffering outputs to
// be more efficient on-chain. // be more efficient on-chain.
var weightEstimate input.TxWeightEstimator weightEstimate := newWeightEstimator()
// Our sweep transaction will pay to a single segwit p2wkh address, // Our sweep transaction will pay to a single segwit p2wkh address,
// ensure it contributes to our weight estimate. // ensure it contributes to our weight estimate.
weightEstimate.AddP2WKHOutput() weightEstimate.addP2WKHOutput()
// For each output, use its witness type to determine the estimate // For each output, use its witness type to determine the estimate
// weight of its witness, and add it to the proper set of spendable // weight of its witness, and add it to the proper set of spendable
@ -235,8 +235,7 @@ func getWeightEstimate(inputs []input.Input) ([]input.Input, int64) {
for i := range inputs { for i := range inputs {
inp := inputs[i] inp := inputs[i]
wt := inp.WitnessType() err := weightEstimate.add(inp)
err := wt.AddWeightEstimation(&weightEstimate)
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)
@ -248,7 +247,7 @@ func getWeightEstimate(inputs []input.Input) ([]input.Input, int64) {
sweepInputs = append(sweepInputs, inp) sweepInputs = append(sweepInputs, inp)
} }
return sweepInputs, int64(weightEstimate.Weight()) return sweepInputs, int64(weightEstimate.weight())
} }
// inputSummary returns a string containing a human readable summary about the // inputSummary returns a string containing a human readable summary about the

40
sweep/weight_estimator.go Normal file

@ -0,0 +1,40 @@
package sweep
import (
"github.com/lightningnetwork/lnd/input"
)
// weightEstimator wraps a standard weight estimator instance.
type weightEstimator struct {
estimator input.TxWeightEstimator
}
// newWeightEstimator instantiates a new sweeper weight estimator.
func newWeightEstimator() *weightEstimator {
return &weightEstimator{}
}
// clone returns a copy of this weight estimator.
func (w *weightEstimator) clone() *weightEstimator {
return &weightEstimator{
estimator: w.estimator,
}
}
// add adds the weight of the given input to the weight estimate.
func (w *weightEstimator) add(inp input.Input) error {
wt := inp.WitnessType()
return wt.AddWeightEstimation(&w.estimator)
}
// addP2WKHOutput updates the weight estimate to account for an additional
// native P2WKH output.
func (w *weightEstimator) addP2WKHOutput() {
w.estimator.AddP2WKHOutput()
}
// weight gets the estimated weight of the transaction.
func (w *weightEstimator) weight() int {
return w.estimator.Weight()
}