• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 13035292482

29 Jan 2025 03:59PM UTC coverage: 49.3% (-9.5%) from 58.777%
13035292482

Pull #9456

github

mohamedawnallah
docs: update release-notes-0.19.0.md

In this commit, we warn users about the removal
of RPCs `SendToRoute`, `SendToRouteSync`, `SendPayment`,
and `SendPaymentSync` in the next release 0.20.
Pull Request #9456: lnrpc+docs: deprecate warning `SendToRoute`, `SendToRouteSync`, `SendPayment`, and `SendPaymentSync` in Release 0.19

100634 of 204126 relevant lines covered (49.3%)

1.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.43
/sweep/txgenerator.go
1
package sweep
2

3
import (
4
        "errors"
5
        "fmt"
6
        "sort"
7
        "strings"
8

9
        "github.com/btcsuite/btcd/blockchain"
10
        "github.com/btcsuite/btcd/btcutil"
11
        "github.com/btcsuite/btcd/txscript"
12
        "github.com/btcsuite/btcd/wire"
13
        "github.com/lightningnetwork/lnd/input"
14
        "github.com/lightningnetwork/lnd/lnwallet"
15
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
16
)
17

18
var (
19
        // DefaultMaxInputsPerTx specifies the default maximum number of inputs
20
        // allowed in a single sweep tx. If more need to be swept, multiple txes
21
        // are created and published.
22
        DefaultMaxInputsPerTx = uint32(100)
23

24
        // ErrLocktimeConflict is returned when inputs with different
25
        // transaction nLockTime values are included in the same transaction.
26
        //
27
        // NOTE: due the SINGLE|ANYONECANPAY sighash flag, which is used in the
28
        // second level success/timeout txns, only the txns sharing the same
29
        // nLockTime can exist in the same tx.
30
        ErrLocktimeConflict = errors.New("incompatible locktime")
31
)
32

33
// createSweepTx builds a signed tx spending the inputs to the given outputs,
34
// sending any leftover change to the change script.
35
func createSweepTx(inputs []input.Input, outputs []*wire.TxOut,
36
        changePkScript []byte, currentBlockHeight uint32,
37
        feeRate, maxFeeRate chainfee.SatPerKWeight,
38
        signer input.Signer) (*wire.MsgTx, btcutil.Amount, error) {
3✔
39

3✔
40
        inputs, estimator, err := getWeightEstimate(
3✔
41
                inputs, outputs, feeRate, maxFeeRate, [][]byte{changePkScript},
3✔
42
        )
3✔
43
        if err != nil {
3✔
44
                return nil, 0, err
×
45
        }
×
46

47
        txFee := estimator.feeWithParent()
3✔
48

3✔
49
        var (
3✔
50
                // Create the sweep transaction that we will be building. We
3✔
51
                // use version 2 as it is required for CSV.
3✔
52
                sweepTx = wire.NewMsgTx(2)
3✔
53

3✔
54
                // Track whether any of the inputs require a certain locktime.
3✔
55
                locktime = int32(-1)
3✔
56

3✔
57
                // We keep track of total input amount, and required output
3✔
58
                // amount to use for calculating the change amount below.
3✔
59
                totalInput     btcutil.Amount
3✔
60
                requiredOutput btcutil.Amount
3✔
61

3✔
62
                // We'll add the inputs as we go so we know the final ordering
3✔
63
                // of inputs to sign.
3✔
64
                idxs []input.Input
3✔
65
        )
3✔
66

3✔
67
        // We start by adding all inputs that commit to an output. We do this
3✔
68
        // since the input and output index must stay the same for the
3✔
69
        // signatures to be valid.
3✔
70
        for _, o := range inputs {
6✔
71
                if o.RequiredTxOut() == nil {
6✔
72
                        continue
3✔
73
                }
74

75
                idxs = append(idxs, o)
×
76
                sweepTx.AddTxIn(&wire.TxIn{
×
77
                        PreviousOutPoint: o.OutPoint(),
×
78
                        Sequence:         o.BlocksToMaturity(),
×
79
                })
×
80
                sweepTx.AddTxOut(o.RequiredTxOut())
×
81

×
82
                if lt, ok := o.RequiredLockTime(); ok {
×
83
                        // If another input commits to a different locktime,
×
84
                        // they cannot be combined in the same transaction.
×
85
                        if locktime != -1 && locktime != int32(lt) {
×
86
                                return nil, 0, ErrLocktimeConflict
×
87
                        }
×
88

89
                        locktime = int32(lt)
×
90
                }
91

92
                totalInput += btcutil.Amount(o.SignDesc().Output.Value)
×
93
                requiredOutput += btcutil.Amount(o.RequiredTxOut().Value)
×
94
        }
95

96
        // Sum up the value contained in the remaining inputs, and add them to
97
        // the sweep transaction.
98
        for _, o := range inputs {
6✔
99
                if o.RequiredTxOut() != nil {
3✔
100
                        continue
×
101
                }
102

103
                idxs = append(idxs, o)
3✔
104
                sweepTx.AddTxIn(&wire.TxIn{
3✔
105
                        PreviousOutPoint: o.OutPoint(),
3✔
106
                        Sequence:         o.BlocksToMaturity(),
3✔
107
                })
3✔
108

3✔
109
                if lt, ok := o.RequiredLockTime(); ok {
3✔
110
                        if locktime != -1 && locktime != int32(lt) {
×
111
                                return nil, 0, ErrLocktimeConflict
×
112
                        }
×
113

114
                        locktime = int32(lt)
×
115
                }
116

117
                totalInput += btcutil.Amount(o.SignDesc().Output.Value)
3✔
118
        }
119

120
        // Add the outputs given, if any.
121
        for _, o := range outputs {
6✔
122
                sweepTx.AddTxOut(o)
3✔
123
                requiredOutput += btcutil.Amount(o.Value)
3✔
124
        }
3✔
125

126
        if requiredOutput+txFee > totalInput {
3✔
127
                return nil, 0, fmt.Errorf("insufficient input to create sweep "+
×
128
                        "tx: input_sum=%v, output_sum=%v", totalInput,
×
129
                        requiredOutput+txFee)
×
130
        }
×
131

132
        // The value remaining after the required output and fees, go to
133
        // change. Not that this fee is what we would have to pay in case the
134
        // sweep tx has a change output.
135
        changeAmt := totalInput - requiredOutput - txFee
3✔
136

3✔
137
        // We'll calculate the dust limit for the given changePkScript since it
3✔
138
        // is variable.
3✔
139
        changeLimit := lnwallet.DustLimitForSize(len(changePkScript))
3✔
140

3✔
141
        // The txn will sweep the amount after fees to the pkscript generated
3✔
142
        // above.
3✔
143
        if changeAmt >= changeLimit {
6✔
144
                sweepTx.AddTxOut(&wire.TxOut{
3✔
145
                        PkScript: changePkScript,
3✔
146
                        Value:    int64(changeAmt),
3✔
147
                })
3✔
148
        } else {
3✔
149
                log.Infof("Change amt %v below dustlimit %v, not adding "+
×
150
                        "change output", changeAmt, changeLimit)
×
151

×
152
                // The dust amount is added to the fee as the miner will
×
153
                // collect it.
×
154
                txFee += changeAmt
×
155
        }
×
156

157
        // We'll default to using the current block height as locktime, if none
158
        // of the inputs commits to a different locktime.
159
        sweepTx.LockTime = currentBlockHeight
3✔
160
        if locktime != -1 {
3✔
161
                sweepTx.LockTime = uint32(locktime)
×
162
        }
×
163

164
        // Before signing the transaction, check to ensure that it meets some
165
        // basic validity requirements.
166
        //
167
        // TODO(conner): add more control to sanity checks, allowing us to
168
        // delay spending "problem" outputs, e.g. possibly batching with other
169
        // classes if fees are too low.
170
        btx := btcutil.NewTx(sweepTx)
3✔
171
        if err := blockchain.CheckTransactionSanity(btx); err != nil {
3✔
172
                return nil, 0, err
×
173
        }
×
174

175
        prevInputFetcher, err := input.MultiPrevOutFetcher(inputs)
3✔
176
        if err != nil {
3✔
177
                return nil, 0, fmt.Errorf("error creating prev input fetcher "+
×
178
                        "for hash cache: %v", err)
×
179
        }
×
180
        hashCache := txscript.NewTxSigHashes(sweepTx, prevInputFetcher)
3✔
181

3✔
182
        // With all the inputs in place, use each output's unique input script
3✔
183
        // function to generate the final witness required for spending.
3✔
184
        addInputScript := func(idx int, tso input.Input) error {
6✔
185
                inputScript, err := tso.CraftInputScript(
3✔
186
                        signer, sweepTx, hashCache, prevInputFetcher, idx,
3✔
187
                )
3✔
188
                if err != nil {
3✔
189
                        return err
×
190
                }
×
191

192
                sweepTx.TxIn[idx].Witness = inputScript.Witness
3✔
193

3✔
194
                if len(inputScript.SigScript) != 0 {
6✔
195
                        sweepTx.TxIn[idx].SignatureScript =
3✔
196
                                inputScript.SigScript
3✔
197
                }
3✔
198

199
                return nil
3✔
200
        }
201

202
        for idx, inp := range idxs {
6✔
203
                if err := addInputScript(idx, inp); err != nil {
3✔
204
                        return nil, 0, err
×
205
                }
×
206
        }
207

208
        log.Debugf("Creating sweep transaction %v for %v inputs (%s) "+
3✔
209
                "using %v, tx_weight=%v, tx_fee=%v, parents_count=%v, "+
3✔
210
                "parents_fee=%v, parents_weight=%v, current_height=%v",
3✔
211
                sweepTx.TxHash(), len(inputs),
3✔
212
                inputTypeSummary(inputs), feeRate,
3✔
213
                estimator.weight(), txFee,
3✔
214
                len(estimator.parents), estimator.parentsFee,
3✔
215
                estimator.parentsWeight, currentBlockHeight)
3✔
216

3✔
217
        return sweepTx, txFee, nil
3✔
218
}
219

220
// getWeightEstimate returns a weight estimate for the given inputs.
221
// Additionally, it returns counts for the number of csv and cltv inputs.
222
func getWeightEstimate(inputs []input.Input, outputs []*wire.TxOut,
223
        feeRate, maxFeeRate chainfee.SatPerKWeight,
224
        outputPkScripts [][]byte) ([]input.Input, *weightEstimator, error) {
3✔
225

3✔
226
        // We initialize a weight estimator so we can accurately asses the
3✔
227
        // amount of fees we need to pay for this sweep transaction.
3✔
228
        //
3✔
229
        // TODO(roasbeef): can be more intelligent about buffering outputs to
3✔
230
        // be more efficient on-chain.
3✔
231
        weightEstimate := newWeightEstimator(feeRate, maxFeeRate)
3✔
232

3✔
233
        // Our sweep transaction will always pay to the given set of outputs.
3✔
234
        for _, o := range outputs {
6✔
235
                weightEstimate.addOutput(o)
3✔
236
        }
3✔
237

238
        // If there is any leftover change after paying to the given outputs
239
        // and required outputs, it will go to a single segwit p2wkh or p2tr
240
        // address. This will be our change address, so ensure it contributes
241
        // to our weight estimate. Note that if we have other outputs, we might
242
        // end up creating a sweep tx without a change output. It is okay to
243
        // add the change output to the weight estimate regardless, since the
244
        // estimated fee will just be subtracted from this already dust output,
245
        // and trimmed.
246
        for _, outputPkScript := range outputPkScripts {
6✔
247
                switch {
3✔
248
                case txscript.IsPayToTaproot(outputPkScript):
3✔
249
                        weightEstimate.addP2TROutput()
3✔
250

251
                case txscript.IsPayToWitnessScriptHash(outputPkScript):
3✔
252
                        weightEstimate.addP2WSHOutput()
3✔
253

254
                case txscript.IsPayToWitnessPubKeyHash(outputPkScript):
3✔
255
                        weightEstimate.addP2WKHOutput()
3✔
256

257
                case txscript.IsPayToPubKeyHash(outputPkScript):
3✔
258
                        weightEstimate.estimator.AddP2PKHOutput()
3✔
259

260
                case txscript.IsPayToScriptHash(outputPkScript):
3✔
261
                        weightEstimate.estimator.AddP2SHOutput()
3✔
262

263
                default:
×
264
                        // Unknown script type.
×
265
                        return nil, nil, fmt.Errorf("unknown script "+
×
266
                                "type: %x", outputPkScript)
×
267
                }
268
        }
269

270
        // For each output, use its witness type to determine the estimate
271
        // weight of its witness, and add it to the proper set of spendable
272
        // outputs.
273
        var sweepInputs []input.Input
3✔
274
        for i := range inputs {
6✔
275
                inp := inputs[i]
3✔
276

3✔
277
                err := weightEstimate.add(inp)
3✔
278
                if err != nil {
3✔
279
                        // TODO(yy): check if this is even possible? If so, we
×
280
                        // should return the error here instead of filtering!
×
281
                        log.Errorf("Failed to get weight estimate for "+
×
282
                                "input=%v, witnessType=%v: %v ", inp.OutPoint(),
×
283
                                inp.WitnessType(), err)
×
284

×
285
                        // Skip inputs for which no weight estimate can be
×
286
                        // given.
×
287
                        continue
×
288
                }
289

290
                // If this input comes with a committed output, add that as
291
                // well.
292
                if inp.RequiredTxOut() != nil {
6✔
293
                        weightEstimate.addOutput(inp.RequiredTxOut())
3✔
294
                }
3✔
295

296
                sweepInputs = append(sweepInputs, inp)
3✔
297
        }
298

299
        return sweepInputs, weightEstimate, nil
3✔
300
}
301

302
// inputSummary returns a string containing a human readable summary about the
303
// witness types of a list of inputs.
304
func inputTypeSummary(inputs []input.Input) string {
3✔
305
        // Sort inputs by witness type.
3✔
306
        sortedInputs := make([]input.Input, len(inputs))
3✔
307
        copy(sortedInputs, inputs)
3✔
308
        sort.Slice(sortedInputs, func(i, j int) bool {
6✔
309
                return sortedInputs[i].WitnessType().String() <
3✔
310
                        sortedInputs[j].WitnessType().String()
3✔
311
        })
3✔
312

313
        var parts []string
3✔
314
        for _, i := range sortedInputs {
6✔
315
                part := fmt.Sprintf("%v (%v)", i.OutPoint(), i.WitnessType())
3✔
316
                parts = append(parts, part)
3✔
317
        }
3✔
318

319
        return strings.Join(parts, "\n")
3✔
320
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc