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

lightningnetwork / lnd / 13236757158

10 Feb 2025 08:39AM UTC coverage: 57.649% (-1.2%) from 58.815%
13236757158

Pull #9493

github

ziggie1984
lncli: for some cmds we don't replace the data of the response.

For some cmds it is not very practical to replace the json output
because we might pipe it into other commands. For example when
creating the route we want to pipe it into sendtoRoute.
Pull Request #9493: For some lncli cmds we should not replace the content with other data

0 of 9 new or added lines in 2 files covered. (0.0%)

19535 existing lines in 252 files now uncovered.

103517 of 179563 relevant lines covered (57.65%)

24878.49 hits per line

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

92.09
/watchtower/wtpolicy/policy.go
1
package wtpolicy
2

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

7
        "github.com/btcsuite/btcd/btcutil"
8
        "github.com/btcsuite/btcd/wire"
9
        "github.com/lightningnetwork/lnd/lntypes"
10
        "github.com/lightningnetwork/lnd/lnwallet"
11
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
12
        "github.com/lightningnetwork/lnd/lnwire"
13
        "github.com/lightningnetwork/lnd/watchtower/blob"
14
        "github.com/lightningnetwork/lnd/watchtower/wtwire"
15
)
16

17
const (
18
        // RewardScale is the denominator applied when computing the
19
        // proportional component for a tower's reward output. The current scale
20
        // is in millionths.
21
        RewardScale = 1000000
22

23
        // DefaultMaxUpdates specifies the number of encrypted blobs a client
24
        // can send to the tower in a single session.
25
        DefaultMaxUpdates = 1024
26

27
        // DefaultRewardRate specifies the fraction of the channel that the
28
        // tower takes if it successfully sweeps a breach. The value is
29
        // expressed in millionths of the channel capacity.
30
        DefaultRewardRate = 10000
31

32
        // DefaultSweepFeeRate specifies the fee rate used to construct justice
33
        // transactions. The value is expressed in satoshis per kilo-weight.
34
        DefaultSweepFeeRate = chainfee.SatPerKWeight(2500)
35

36
        // MinSweepFeeRate is the minimum sweep fee rate a client may use in its
37
        // policy, the current value is 4 sat/vbyte.
38
        MinSweepFeeRate = chainfee.SatPerKWeight(1000)
39
)
40

41
var (
42
        // ErrFeeExceedsInputs signals that the total input value of breaching
43
        // commitment txn is insufficient to cover the fees required to sweep
44
        // it.
45
        ErrFeeExceedsInputs = errors.New("sweep fee exceeds input value")
46

47
        // ErrRewardExceedsInputs signals that the reward given to the tower (in
48
        // addition to the transaction fees) is more than the input amount.
49
        ErrRewardExceedsInputs = errors.New("reward amount exceeds input value")
50

51
        // ErrCreatesDust signals that the session's policy would create a dust
52
        // output for the victim.
53
        ErrCreatesDust = errors.New("justice transaction creates dust at fee rate")
54

55
        // ErrAltruistReward signals that the policy is invalid because it
56
        // contains a non-zero RewardBase or RewardRate on an altruist policy.
57
        ErrAltruistReward = errors.New("altruist policy has reward params")
58

59
        // ErrNoMaxUpdates signals that the policy specified zero MaxUpdates.
60
        ErrNoMaxUpdates = errors.New("max updates must be positive")
61

62
        // ErrSweepFeeRateTooLow signals that the policy's fee rate is too low
63
        // to get into the mempool during low congestion.
64
        ErrSweepFeeRateTooLow = errors.New("sweep fee rate too low")
65
)
66

67
// DefaultPolicy returns a Policy containing the default parameters that can be
68
// used by clients or servers.
69
func DefaultPolicy() Policy {
1✔
70
        return Policy{
1✔
71
                TxPolicy: TxPolicy{
1✔
72
                        BlobType:     blob.TypeAltruistCommit,
1✔
73
                        SweepFeeRate: DefaultSweepFeeRate,
1✔
74
                },
1✔
75
                MaxUpdates: DefaultMaxUpdates,
1✔
76
        }
1✔
77
}
1✔
78

79
// TxPolicy defines the negotiate parameters that determine the form of the
80
// justice transaction for a given breached state. Thus, for any given revoked
81
// state, an identical key will result in an identical justice transaction
82
// (barring signatures). The parameters specify the format of encrypted blobs
83
// sent to the tower, the reward schedule for the tower, and the number of
84
// encrypted blobs a client can send in one session.
85
type TxPolicy struct {
86
        // BlobType specifies the blob format that must be used by all updates sent
87
        // under the session key used to negotiate this session.
88
        BlobType blob.Type
89

90
        // RewardBase is the fixed amount allocated to the tower when the
91
        // policy's blob type specifies a reward for the tower. This is taken
92
        // before adding the proportional reward.
93
        RewardBase uint32
94

95
        // RewardRate is the fraction of the total balance of the revoked
96
        // commitment that the watchtower is entitled to. This value is
97
        // expressed in millionths of the total balance.
98
        RewardRate uint32
99

100
        // SweepFeeRate expresses the intended fee rate to be used when
101
        // constructing the justice transaction. All sweep transactions created
102
        // for this session must use this value during construction, and the
103
        // signatures must implicitly commit to the resulting output values.
104
        SweepFeeRate chainfee.SatPerKWeight
105
}
106

107
// Policy defines the negotiated parameters for a session between a client and
108
// server. In addition to the TxPolicy that governs the shape of the justice
109
// transaction, the Policy also includes features which only affect the
110
// operation of the session.
111
type Policy struct {
112
        TxPolicy
113

114
        // MaxUpdates is the maximum number of updates the watchtower will honor
115
        // for this session.
116
        MaxUpdates uint16
117
}
118

119
// String returns a human-readable description of the current policy.
UNCOV
120
func (p Policy) String() string {
×
UNCOV
121
        return fmt.Sprintf("(blob-type=%b max-updates=%d reward-rate=%d "+
×
UNCOV
122
                "sweep-fee-rate=%d)", p.BlobType, p.MaxUpdates, p.RewardRate,
×
UNCOV
123
                p.SweepFeeRate)
×
UNCOV
124
}
×
125

126
// FeatureBits returns the watchtower feature bits required for the given
127
// policy.
128
func (p *Policy) FeatureBits() []lnwire.FeatureBit {
36✔
129
        features := []lnwire.FeatureBit{
36✔
130
                wtwire.AltruistSessionsRequired,
36✔
131
        }
36✔
132

36✔
133
        t := p.TxPolicy.BlobType
36✔
134
        switch {
36✔
135
        case t.IsTaprootChannel():
36✔
136
                features = append(features, wtwire.TaprootCommitRequired)
36✔
UNCOV
137
        case t.IsAnchorChannel():
×
UNCOV
138
                features = append(features, wtwire.AnchorCommitRequired)
×
139
        }
140

141
        return features
36✔
142
}
143

144
// IsAnchorChannel returns true if the session policy requires anchor channels.
145
func (p *Policy) IsAnchorChannel() bool {
3✔
146
        return p.TxPolicy.BlobType.IsAnchorChannel()
3✔
147
}
3✔
148

149
// IsTaprootChannel returns true if the session policy requires taproot
150
// channels.
151
func (p *Policy) IsTaprootChannel() bool {
3✔
152
        return p.TxPolicy.BlobType.IsTaprootChannel()
3✔
153
}
3✔
154

155
// Validate ensures that the policy satisfies some minimal correctness
156
// constraints.
157
func (p *Policy) Validate() error {
217✔
158
        // RewardBase and RewardRate should not be set if the policy doesn't
217✔
159
        // have a reward.
217✔
160
        if !p.BlobType.Has(blob.FlagReward) &&
217✔
161
                (p.RewardBase != 0 || p.RewardRate != 0) {
219✔
162

2✔
163
                return ErrAltruistReward
2✔
164
        }
2✔
165

166
        // MaxUpdates must be positive.
167
        if p.MaxUpdates == 0 {
216✔
168
                return ErrNoMaxUpdates
1✔
169
        }
1✔
170

171
        // SweepFeeRate must be sane enough to get in the mempool during low
172
        // congestion.
173
        if p.SweepFeeRate < MinSweepFeeRate {
218✔
174
                return ErrSweepFeeRateTooLow
4✔
175
        }
4✔
176

177
        return nil
210✔
178
}
179

180
// ComputeAltruistOutput computes the lone output value of a justice transaction
181
// that pays no reward to the tower. The value is computed using the weight of
182
// of the justice transaction and subtracting an amount that satisfies the
183
// policy's fee rate.
184
func (p *Policy) ComputeAltruistOutput(
185
        totalAmt btcutil.Amount, txWeight lntypes.WeightUnit,
186
        sweepScript []byte) (btcutil.Amount, error) {
510✔
187

510✔
188
        txFee := p.SweepFeeRate.FeeForWeight(txWeight)
510✔
189
        if txFee > totalAmt {
519✔
190
                return 0, ErrFeeExceedsInputs
9✔
191
        }
9✔
192

193
        sweepAmt := totalAmt - txFee
501✔
194

501✔
195
        // Check that the created outputs won't be dusty. We'll base the dust
501✔
196
        // computation on the type of the script itself.
501✔
197
        if sweepAmt < lnwallet.DustLimitForSize(len(sweepScript)) {
509✔
198
                return 0, ErrCreatesDust
8✔
199
        }
8✔
200

201
        return sweepAmt, nil
493✔
202
}
203

204
// ComputeRewardOutputs splits the total funds in a breaching commitment
205
// transaction between the victim and the tower, according to the sweep fee rate
206
// and reward rate. The reward to he tower is subtracted first, before
207
// splitting the remaining balance amongst the victim and fees.
208
func (p *Policy) ComputeRewardOutputs(totalAmt btcutil.Amount,
209
        txWeight lntypes.WeightUnit,
210
        rewardScript []byte) (btcutil.Amount, btcutil.Amount, error) {
26✔
211

26✔
212
        txFee := p.SweepFeeRate.FeeForWeight(txWeight)
26✔
213
        if txFee > totalAmt {
30✔
214
                return 0, 0, ErrFeeExceedsInputs
4✔
215
        }
4✔
216

217
        // Apply the reward rate to the remaining total, specified in millionths
218
        // of the available balance.
219
        rewardAmt := ComputeRewardAmount(totalAmt, p.RewardBase, p.RewardRate)
22✔
220
        if rewardAmt+txFee > totalAmt {
22✔
221
                return 0, 0, ErrRewardExceedsInputs
×
222
        }
×
223

224
        // The sweep amount for the victim constitutes the remainder of the
225
        // input value.
226
        sweepAmt := totalAmt - rewardAmt - txFee
22✔
227

22✔
228
        // Check that the created outputs won't be dusty. We'll base the dust
22✔
229
        // computation on the type of the script itself.
22✔
230
        if sweepAmt < lnwallet.DustLimitForSize(len(rewardScript)) {
30✔
231
                return 0, 0, ErrCreatesDust
8✔
232
        }
8✔
233

234
        return sweepAmt, rewardAmt, nil
14✔
235
}
236

237
// ComputeRewardAmount computes the amount rewarded to the tower using the
238
// proportional rate expressed in millionths, e.g. one million is equivalent to
239
// one hundred percent of the total amount. The amount is rounded up to the
240
// nearest whole satoshi.
241
func ComputeRewardAmount(total btcutil.Amount, base, rate uint32) btcutil.Amount {
22✔
242
        rewardBase := btcutil.Amount(base)
22✔
243
        rewardRate := btcutil.Amount(rate)
22✔
244

22✔
245
        // If the base reward exceeds the total, there is no more funds left
22✔
246
        // from which to derive the proportional fee. We simply return the base,
22✔
247
        // the caller should detect that this exceeds the total amount input.
22✔
248
        if rewardBase > total {
22✔
249
                return rewardBase
×
250
        }
×
251

252
        // Otherwise, subtract the base from the total and compute the
253
        // proportional reward from the remaining total.
254
        afterBase := total - rewardBase
22✔
255
        proportional := (afterBase*rewardRate + RewardScale - 1) / RewardScale
22✔
256

22✔
257
        return rewardBase + proportional
22✔
258
}
259

260
// ComputeJusticeTxOuts constructs the justice transaction outputs for the
261
// given policy. If the policy specifies a reward for the tower, there will be
262
// two outputs paying to the victim and the tower. Otherwise there will be a
263
// single output sweeping funds back to the victim. The totalAmt should be the
264
// sum of any inputs used in the transaction. The passed txWeight should
265
// include the weight of the outputs for the justice transaction, which is
266
// dependent on whether the justice transaction has a reward. The sweepPkScript
267
// should be the pkScript of the victim to which funds will be recovered. The
268
// rewardPkScript is the pkScript of the tower where its reward will be
269
// deposited, and will be
270
// ignored if the blob type does not specify a reward.
271
func (p *Policy) ComputeJusticeTxOuts(
272
        totalAmt btcutil.Amount, txWeight lntypes.WeightUnit,
273
        sweepPkScript, rewardPkScript []byte) ([]*wire.TxOut, error) {
536✔
274

536✔
275
        var outputs []*wire.TxOut
536✔
276

536✔
277
        // If the policy specifies a reward for the tower, compute a split of
536✔
278
        // the funds based on the policy's parameters. Otherwise, we will use
536✔
279
        // the altruist output computation and sweep as much of the funds
536✔
280
        // back to the victim as possible.
536✔
281
        if p.BlobType.Has(blob.FlagReward) {
562✔
282
                // Using the total input amount and the transaction's weight,
26✔
283
                // compute the sweep and reward amounts. This corresponds to
26✔
284
                // the amount returned to the victim and the amount paid to the
26✔
285
                // tower, respectively. To do so, the required transaction fee
26✔
286
                // is subtracted from the total, and the remaining amount is
26✔
287
                // divided according to the pre negotiated reward rate from the
26✔
288
                // client's session info.
26✔
289
                sweepAmt, rewardAmt, err := p.ComputeRewardOutputs(
26✔
290
                        totalAmt, txWeight, rewardPkScript,
26✔
291
                )
26✔
292
                if err != nil {
38✔
293
                        return nil, err
12✔
294
                }
12✔
295

296
                // Add the sweep and reward outputs to the list of txouts.
297
                outputs = append(outputs, &wire.TxOut{
14✔
298
                        PkScript: sweepPkScript,
14✔
299
                        Value:    int64(sweepAmt),
14✔
300
                })
14✔
301
                outputs = append(outputs, &wire.TxOut{
14✔
302
                        PkScript: rewardPkScript,
14✔
303
                        Value:    int64(rewardAmt),
14✔
304
                })
14✔
305
        } else {
510✔
306
                // Using the total input amount and the transaction's weight,
510✔
307
                // compute the sweep amount, which corresponds to the amount
510✔
308
                // returned to the victim. To do so, the required transaction
510✔
309
                // fee is subtracted from the total input amount.
510✔
310
                sweepAmt, err := p.ComputeAltruistOutput(
510✔
311
                        totalAmt, txWeight, sweepPkScript,
510✔
312
                )
510✔
313
                if err != nil {
527✔
314
                        return nil, err
17✔
315
                }
17✔
316

317
                // Add the sweep output to the list of txouts.
318
                outputs = append(outputs, &wire.TxOut{
493✔
319
                        PkScript: sweepPkScript,
493✔
320
                        Value:    int64(sweepAmt),
493✔
321
                })
493✔
322
        }
323

324
        return outputs, nil
507✔
325
}
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