• 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

77.84
/watchtower/lookout/justice_descriptor.go
1
package lookout
2

3
import (
4
        "errors"
5
        "fmt"
6

7
        "github.com/btcsuite/btcd/blockchain"
8
        "github.com/btcsuite/btcd/btcutil"
9
        "github.com/btcsuite/btcd/btcutil/txsort"
10
        "github.com/btcsuite/btcd/txscript"
11
        "github.com/btcsuite/btcd/wire"
12
        "github.com/davecgh/go-spew/spew"
13
        "github.com/lightningnetwork/lnd/input"
14
        "github.com/lightningnetwork/lnd/lntypes"
15
        "github.com/lightningnetwork/lnd/watchtower/blob"
16
        "github.com/lightningnetwork/lnd/watchtower/wtdb"
17
)
18

19
var (
20
        // ErrOutputNotFound signals that the breached output could not be found
21
        // on the commitment transaction.
22
        ErrOutputNotFound = errors.New("unable to find output on commit tx")
23

24
        // ErrUnknownSweepAddrType signals that client provided an output that
25
        // was not p2wkh or p2wsh.
26
        ErrUnknownSweepAddrType = errors.New("sweep addr is not p2wkh or p2wsh")
27
)
28

29
// JusticeDescriptor contains the information required to sweep a breached
30
// channel on behalf of a victim. It supports the ability to create the justice
31
// transaction that sweeps the commitments and recover a cut of the channel for
32
// the watcher's eternal vigilance.
33
type JusticeDescriptor struct {
34
        // BreachedCommitTx is the commitment transaction that caused the breach
35
        // to be detected.
36
        BreachedCommitTx *wire.MsgTx
37

38
        // SessionInfo contains the contract with the watchtower client and
39
        // the prenegotiated terms they agreed to.
40
        SessionInfo *wtdb.SessionInfo
41

42
        // JusticeKit contains the decrypted blob and information required to
43
        // construct the transaction scripts and witnesses.
44
        JusticeKit blob.JusticeKit
45
}
46

47
// breachedInput contains the required information to construct and spend
48
// breached outputs on a commitment transaction.
49
type breachedInput struct {
50
        txOut    *wire.TxOut
51
        outPoint wire.OutPoint
52
        witness  [][]byte
53
        sequence uint32
54
}
55

56
// commitToLocalInput extracts the information required to spend the commit
57
// to-local output.
58
func (p *JusticeDescriptor) commitToLocalInput() (*breachedInput, error) {
3✔
59
        kit := p.JusticeKit
3✔
60

3✔
61
        // Retrieve the to-local output script and witness from the justice kit.
3✔
62
        toLocalPkScript, witness, err := kit.ToLocalOutputSpendInfo()
3✔
63
        if err != nil {
3✔
64
                return nil, err
×
65
        }
×
66

67
        // Locate the to-local output on the breaching commitment transaction.
68
        toLocalIndex, toLocalTxOut, err := findTxOutByPkScript(
3✔
69
                p.BreachedCommitTx, toLocalPkScript,
3✔
70
        )
3✔
71
        if err != nil {
3✔
72
                return nil, err
×
73
        }
×
74

75
        // Construct the to-local outpoint that will be spent in the justice
76
        // transaction.
77
        toLocalOutPoint := wire.OutPoint{
3✔
78
                Hash:  p.BreachedCommitTx.TxHash(),
3✔
79
                Index: toLocalIndex,
3✔
80
        }
3✔
81

3✔
82
        return &breachedInput{
3✔
83
                txOut:    toLocalTxOut,
3✔
84
                outPoint: toLocalOutPoint,
3✔
85
                witness:  witness,
3✔
86
        }, nil
3✔
87
}
88

89
// commitToRemoteInput extracts the information required to spend the commit
90
// to-remote output.
91
func (p *JusticeDescriptor) commitToRemoteInput() (*breachedInput, error) {
3✔
92
        kit := p.JusticeKit
3✔
93

3✔
94
        // Retrieve the to-remote output script, witness script and sequence
3✔
95
        // from the justice kit.
3✔
96
        toRemotePkScript, witness, seq, err := kit.ToRemoteOutputSpendInfo()
3✔
97
        if err != nil {
3✔
98
                return nil, err
×
99
        }
×
100

101
        // Locate the to-remote output on the breaching commitment transaction.
102
        toRemoteIndex, toRemoteTxOut, err := findTxOutByPkScript(
3✔
103
                p.BreachedCommitTx, toRemotePkScript,
3✔
104
        )
3✔
105
        if err != nil {
3✔
106
                return nil, err
×
107
        }
×
108

109
        // Construct the to-remote outpoint which will be spent in the justice
110
        // transaction.
111
        toRemoteOutPoint := wire.OutPoint{
3✔
112
                Hash:  p.BreachedCommitTx.TxHash(),
3✔
113
                Index: toRemoteIndex,
3✔
114
        }
3✔
115

3✔
116
        return &breachedInput{
3✔
117
                txOut:    toRemoteTxOut,
3✔
118
                outPoint: toRemoteOutPoint,
3✔
119
                witness:  witness,
3✔
120
                sequence: seq,
3✔
121
        }, nil
3✔
122
}
123

124
// assembleJusticeTxn accepts the breached inputs recovered from state update
125
// and attempts to construct the justice transaction that sweeps the victims
126
// funds to their wallet and claims the watchtower's reward.
127
func (p *JusticeDescriptor) assembleJusticeTxn(txWeight lntypes.WeightUnit,
128
        inputs ...*breachedInput) (*wire.MsgTx, error) {
3✔
129

3✔
130
        justiceTxn := wire.NewMsgTx(2)
3✔
131

3✔
132
        // First, construct add the breached inputs to our justice transaction
3✔
133
        // and compute the total amount that will be swept.
3✔
134
        var totalAmt btcutil.Amount
3✔
135
        for _, inp := range inputs {
6✔
136
                totalAmt += btcutil.Amount(inp.txOut.Value)
3✔
137
                justiceTxn.AddTxIn(&wire.TxIn{
3✔
138
                        PreviousOutPoint: inp.outPoint,
3✔
139
                        Sequence:         inp.sequence,
3✔
140
                })
3✔
141
        }
3✔
142

143
        // Using the session's policy, compute the outputs that should be added
144
        // to the justice transaction. In the case of an altruist sweep, there
145
        // will be a single output paying back to the victim. Otherwise for a
146
        // reward sweep, there will be two outputs, one of which pays back to
147
        // the victim while the other gives a cut to the tower.
148
        outputs, err := p.SessionInfo.Policy.ComputeJusticeTxOuts(
3✔
149
                totalAmt, txWeight, p.JusticeKit.SweepAddress(),
3✔
150
                p.SessionInfo.RewardAddress,
3✔
151
        )
3✔
152
        if err != nil {
3✔
153
                return nil, err
×
154
        }
×
155

156
        // Attach the computed txouts to the justice transaction.
157
        justiceTxn.TxOut = outputs
3✔
158

3✔
159
        // Apply a BIP69 sort to the resulting transaction.
3✔
160
        txsort.InPlaceSort(justiceTxn)
3✔
161

3✔
162
        btx := btcutil.NewTx(justiceTxn)
3✔
163
        if err := blockchain.CheckTransactionSanity(btx); err != nil {
3✔
164
                return nil, err
×
165
        }
×
166

167
        // Since the transaction inputs could have been reordered as a result of the
168
        // BIP69 sort, create an index mapping each prevout to it's new index.
169
        inputIndex := make(map[wire.OutPoint]int)
3✔
170
        for i, txIn := range justiceTxn.TxIn {
6✔
171
                inputIndex[txIn.PreviousOutPoint] = i
3✔
172
        }
3✔
173

174
        // Attach each of the provided witnesses to the transaction.
175
        prevOutFetcher, err := prevOutFetcher(inputs)
3✔
176
        if err != nil {
3✔
177
                return nil, fmt.Errorf("error creating previous output "+
×
178
                        "fetcher: %v", err)
×
179
        }
×
180

181
        hashes := txscript.NewTxSigHashes(justiceTxn, prevOutFetcher)
3✔
182
        for _, inp := range inputs {
6✔
183
                // Lookup the input's new post-sort position.
3✔
184
                i := inputIndex[inp.outPoint]
3✔
185
                justiceTxn.TxIn[i].Witness = inp.witness
3✔
186

3✔
187
                // Validate the reconstructed witnesses to ensure they are
3✔
188
                // valid for the breached inputs.
3✔
189
                vm, err := txscript.NewEngine(
3✔
190
                        inp.txOut.PkScript, justiceTxn, i,
3✔
191
                        txscript.StandardVerifyFlags,
3✔
192
                        nil, hashes, inp.txOut.Value, prevOutFetcher,
3✔
193
                )
3✔
194
                if err != nil {
3✔
195
                        return nil, err
×
196
                }
×
197
                if err := vm.Execute(); err != nil {
3✔
198
                        log.Debugf("Failed to validate justice transaction: %s",
×
199
                                spew.Sdump(justiceTxn))
×
200
                        return nil, fmt.Errorf("error validating TX: %w", err)
×
201
                }
×
202
        }
203

204
        return justiceTxn, nil
3✔
205
}
206

207
// CreateJusticeTxn computes the justice transaction that sweeps a breaching
208
// commitment transaction. The justice transaction is constructed by assembling
209
// the witnesses using data provided by the client in a prior state update.
210
//
211
// NOTE: An older version of ToLocalPenaltyWitnessSize underestimated the size
212
// of the witness by one byte, which could cause the signature(s) to break if
213
// the tower is reconstructing with the newer constant because the output values
214
// might differ. This method retains that original behavior to not invalidate
215
// historical signatures.
216
func (p *JusticeDescriptor) CreateJusticeTxn() (*wire.MsgTx, error) {
3✔
217
        var (
3✔
218
                sweepInputs    = make([]*breachedInput, 0, 2)
3✔
219
                weightEstimate input.TxWeightEstimator
3✔
220
        )
3✔
221

3✔
222
        commitmentType, err := p.SessionInfo.Policy.BlobType.CommitmentType(nil)
3✔
223
        if err != nil {
3✔
224
                return nil, err
×
225
        }
×
226

227
        // Add the sweep address's contribution, depending on whether it is a
228
        // p2wkh or p2wsh output.
229
        switch len(p.JusticeKit.SweepAddress()) {
3✔
230
        case input.P2WPKHSize:
×
231
                weightEstimate.AddP2WKHOutput()
×
232

233
        case input.P2WSHSize:
3✔
234
                weightEstimate.AddP2WSHOutput()
3✔
235

236
        default:
×
237
                return nil, ErrUnknownSweepAddrType
×
238
        }
239

240
        // Add our reward address to the weight estimate if the policy's blob
241
        // type specifies a reward output.
242
        if p.SessionInfo.Policy.BlobType.Has(blob.FlagReward) {
3✔
243
                weightEstimate.AddP2WKHOutput()
×
244
        }
×
245

246
        // Assemble the breached to-local output from the justice descriptor and
247
        // add it to our weight estimate.
248
        toLocalInput, err := p.commitToLocalInput()
3✔
249
        if err != nil {
3✔
250
                return nil, err
×
251
        }
×
252

253
        // Get the weight for the to-local witness and add that to the
254
        // estimator.
255
        toLocalWitnessSize, err := commitmentType.ToLocalWitnessSize()
3✔
256
        if err != nil {
3✔
257
                return nil, err
×
258
        }
×
259
        weightEstimate.AddWitnessInput(toLocalWitnessSize)
3✔
260

3✔
261
        sweepInputs = append(sweepInputs, toLocalInput)
3✔
262

3✔
263
        log.Debugf("Found to local witness output=%#v, stack=%v",
3✔
264
                toLocalInput.txOut, toLocalInput.witness)
3✔
265

3✔
266
        // If the justice kit specifies that we have to sweep the to-remote
3✔
267
        // output, we'll also try to assemble the output and add it to weight
3✔
268
        // estimate if successful.
3✔
269
        if p.JusticeKit.HasCommitToRemoteOutput() {
6✔
270
                toRemoteInput, err := p.commitToRemoteInput()
3✔
271
                if err != nil {
3✔
272
                        return nil, err
×
273
                }
×
274
                sweepInputs = append(sweepInputs, toRemoteInput)
3✔
275

3✔
276
                log.Debugf("Found to remote witness output=%#v, stack=%v",
3✔
277
                        toRemoteInput.txOut, toRemoteInput.witness)
3✔
278

3✔
279
                // Get the weight for the to-remote witness and add that to the
3✔
280
                // estimator.
3✔
281
                toRemoteWitnessSize, err := commitmentType.ToRemoteWitnessSize()
3✔
282
                if err != nil {
3✔
283
                        return nil, err
×
284
                }
×
285

286
                weightEstimate.AddWitnessInput(toRemoteWitnessSize)
3✔
287
        }
288

289
        // TODO(conner): sweep htlc outputs
290

291
        txWeight := weightEstimate.Weight()
3✔
292

3✔
293
        return p.assembleJusticeTxn(txWeight, sweepInputs...)
3✔
294
}
295

296
// findTxOutByPkScript searches the given transaction for an output whose
297
// pkscript matches the query. If one is found, the TxOut is returned along with
298
// the index.
299
//
300
// NOTE: The search stops after the first match is found.
301
func findTxOutByPkScript(txn *wire.MsgTx,
302
        pkScript *txscript.PkScript) (uint32, *wire.TxOut, error) {
3✔
303

3✔
304
        found, index := input.FindScriptOutputIndex(txn, pkScript.Script())
3✔
305
        if !found {
3✔
306
                return 0, nil, ErrOutputNotFound
×
307
        }
×
308

309
        return index, txn.TxOut[index], nil
3✔
310
}
311

312
// prevOutFetcher returns a txscript.MultiPrevOutFetcher for the given set
313
// of inputs.
314
func prevOutFetcher(inputs []*breachedInput) (*txscript.MultiPrevOutFetcher,
315
        error) {
3✔
316

3✔
317
        fetcher := txscript.NewMultiPrevOutFetcher(nil)
3✔
318
        for _, inp := range inputs {
6✔
319
                if inp.txOut == nil {
3✔
320
                        return nil, fmt.Errorf("missing input utxo information")
×
321
                }
×
322

323
                fetcher.AddPrevOut(inp.outPoint, inp.txOut)
3✔
324
        }
325

326
        return fetcher, nil
3✔
327
}
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