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

lightningnetwork / lnd / 15843723163

24 Jun 2025 07:07AM UTC coverage: 56.04%. First build
15843723163

Pull #9985

github

web-flow
Merge 7f2e35788 into 1b95798fc
Pull Request #9985: multi: implement awareness of the final/production taproot channel variant

145 of 393 new or added lines in 18 files covered. (36.9%)

108424 of 193477 relevant lines covered (56.04%)

22573.28 hits per line

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

0.0
/lntest/utils.go
1
package lntest
2

3
import (
4
        "fmt"
5
        "io"
6
        "math"
7
        "os"
8
        "strconv"
9
        "strings"
10

11
        "github.com/btcsuite/btcd/btcutil"
12
        "github.com/btcsuite/btcd/wire"
13
        "github.com/lightningnetwork/lnd/input"
14
        "github.com/lightningnetwork/lnd/lnrpc"
15
        "github.com/lightningnetwork/lnd/lntest/wait"
16
        "github.com/lightningnetwork/lnd/lntypes"
17
        "github.com/lightningnetwork/lnd/lnwallet"
18
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
19
        "github.com/lightningnetwork/lnd/lnwire"
20
)
21

22
const (
23
        // NeutrinoBackendName is the name of the neutrino backend.
24
        NeutrinoBackendName = "neutrino"
25

26
        DefaultTimeout = wait.DefaultTimeout
27

28
        // noFeeLimitMsat is used to specify we will put no requirements on fee
29
        // charged when choosing a route path.
30
        noFeeLimitMsat = math.MaxInt64
31
)
32

33
// CopyFile copies the file src to dest.
34
func CopyFile(dest, src string) error {
×
35
        s, err := os.Open(src)
×
36
        if err != nil {
×
37
                return err
×
38
        }
×
39
        defer s.Close()
×
40

×
41
        d, err := os.Create(dest)
×
42
        if err != nil {
×
43
                return err
×
44
        }
×
45

46
        if _, err := io.Copy(d, s); err != nil {
×
47
                d.Close()
×
48
                return err
×
49
        }
×
50

51
        return d.Close()
×
52
}
53

54
// errNumNotMatched is a helper method to return a nicely formatted error.
55
func errNumNotMatched(name string, subject string,
56
        want, got, total, old int, desc ...any) error {
×
57

×
58
        err := fmt.Errorf("%s: assert %s failed: want %d, got: %d, total: "+
×
59
                "%d, previously had: %d", name, subject, want, got, total, old)
×
60

×
61
        if len(desc) > 0 {
×
62
                err = fmt.Errorf("%w, desc: %v", err, desc)
×
63
        }
×
64

65
        return err
×
66
}
67

68
// parseDerivationPath parses a path in the form of m/x'/y'/z'/a/b into a slice
69
// of [x, y, z, a, b], meaning that the apostrophe is ignored and 2^31 is _not_
70
// added to the numbers.
71
func ParseDerivationPath(path string) ([]uint32, error) {
×
72
        path = strings.TrimSpace(path)
×
73
        if len(path) == 0 {
×
74
                return nil, fmt.Errorf("path cannot be empty")
×
75
        }
×
76
        if !strings.HasPrefix(path, "m/") {
×
77
                return nil, fmt.Errorf("path must start with m/")
×
78
        }
×
79

80
        // Just the root key, no path was provided. This is valid but not useful
81
        // in most cases.
82
        rest := strings.ReplaceAll(path, "m/", "")
×
83
        if rest == "" {
×
84
                return []uint32{}, nil
×
85
        }
×
86

87
        parts := strings.Split(rest, "/")
×
88
        indices := make([]uint32, len(parts))
×
89
        for i := 0; i < len(parts); i++ {
×
90
                part := parts[i]
×
91
                if strings.Contains(parts[i], "'") {
×
92
                        part = strings.TrimRight(parts[i], "'")
×
93
                }
×
94
                parsed, err := strconv.ParseInt(part, 10, 32)
×
95
                if err != nil {
×
96
                        return nil, fmt.Errorf("could not parse part \"%s\": "+
×
97
                                "%v", part, err)
×
98
                }
×
99
                indices[i] = uint32(parsed)
×
100
        }
101

102
        return indices, nil
×
103
}
104

105
// ChanPointFromPendingUpdate constructs a channel point from a lnrpc pending
106
// update.
107
func ChanPointFromPendingUpdate(pu *lnrpc.PendingUpdate) *lnrpc.ChannelPoint {
×
108
        chanPoint := &lnrpc.ChannelPoint{
×
109
                FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
×
110
                        FundingTxidBytes: pu.Txid,
×
111
                },
×
112
                OutputIndex: pu.OutputIndex,
×
113
        }
×
114

×
115
        return chanPoint
×
116
}
×
117

118
// channelPointStr returns the string representation of the channel's
119
// funding transaction.
120
func channelPointStr(chanPoint *lnrpc.ChannelPoint) string {
×
121
        fundingTxID, err := lnrpc.GetChanPointFundingTxid(chanPoint)
×
122
        if err != nil {
×
123
                return ""
×
124
        }
×
125
        cp := wire.OutPoint{
×
126
                Hash:  *fundingTxID,
×
127
                Index: chanPoint.OutputIndex,
×
128
        }
×
129

×
130
        return cp.String()
×
131
}
132

133
// CommitTypeHasTaproot returns whether commitType is a taproot commitment.
134
func CommitTypeHasTaproot(commitType lnrpc.CommitmentType) bool {
×
135
        switch commitType {
×
136
        case lnrpc.CommitmentType_SIMPLE_TAPROOT,
NEW
137
                lnrpc.CommitmentType_SIMPLE_TAPROOT_FINAL:
×
138
                return true
×
139
        default:
×
140
                return false
×
141
        }
142
}
143

144
// CommitTypeHasAnchors returns whether commitType uses anchor outputs.
145
func CommitTypeHasAnchors(commitType lnrpc.CommitmentType) bool {
×
146
        switch commitType {
×
147
        case lnrpc.CommitmentType_ANCHORS,
148
                lnrpc.CommitmentType_SIMPLE_TAPROOT,
149
                lnrpc.CommitmentType_SIMPLE_TAPROOT_FINAL,
150
                lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE:
×
151
                return true
×
152
        default:
×
153
                return false
×
154
        }
155
}
156

157
// NodeArgsForCommitType returns the command line flag to supply to enable this
158
// commitment type.
159
func NodeArgsForCommitType(commitType lnrpc.CommitmentType) []string {
×
160
        switch commitType {
×
161
        case lnrpc.CommitmentType_LEGACY:
×
162
                return []string{"--protocol.legacy.committweak"}
×
163
        case lnrpc.CommitmentType_STATIC_REMOTE_KEY:
×
164
                return []string{}
×
165
        case lnrpc.CommitmentType_ANCHORS:
×
166
                return []string{"--protocol.anchors"}
×
167
        case lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE:
×
168
                return []string{
×
169
                        "--protocol.anchors",
×
170
                        "--protocol.script-enforced-lease",
×
171
                }
×
172
        case lnrpc.CommitmentType_SIMPLE_TAPROOT,
NEW
173
                lnrpc.CommitmentType_SIMPLE_TAPROOT_FINAL:
×
174
                return []string{
×
175
                        "--protocol.anchors",
×
176
                        "--protocol.simple-taproot-chans",
×
177
                }
×
178
        }
179

180
        return nil
×
181
}
182

183
// CalcStaticFee calculates appropriate fees for commitment transactions. This
184
// function provides a simple way to allow test balance assertions to take fee
185
// calculations into account.
186
func CalcStaticFee(c lnrpc.CommitmentType, numHTLCs int) btcutil.Amount {
×
187
        //nolint:ll
×
188
        const (
×
189
                htlcWeight         = input.HTLCWeight
×
190
                anchorSize         = 330 * 2
×
191
                defaultSatPerVByte = lnwallet.DefaultAnchorsCommitMaxFeeRateSatPerVByte
×
192
                scale              = 1000
×
193
        )
×
194

×
195
        var (
×
196
                anchors      = btcutil.Amount(0)
×
197
                commitWeight = input.CommitWeight
×
198
                feePerKw     = chainfee.SatPerKWeight(DefaultFeeRateSatPerKw)
×
199
        )
×
200

×
201
        switch {
×
202
        // The taproot commitment type has the extra anchor outputs, but also a
203
        // smaller witness field (will just be a normal key spend), so we need
204
        // to account for that here as well.
205
        case CommitTypeHasTaproot(c):
×
206
                feePerKw = chainfee.SatPerKVByte(
×
207
                        defaultSatPerVByte * scale,
×
208
                ).FeePerKWeight()
×
209

×
210
                commitWeight = input.TaprootCommitWeight
×
211
                anchors = anchorSize
×
212

213
        // The anchor commitment type is slightly heavier, and we must also add
214
        // the value of the two anchors to the resulting fee the initiator
215
        // pays. In addition the fee rate is capped at 10 sat/vbyte for anchor
216
        // channels.
217
        case CommitTypeHasAnchors(c):
×
218
                feePerKw = chainfee.SatPerKVByte(
×
219
                        defaultSatPerVByte * scale,
×
220
                ).FeePerKWeight()
×
221
                commitWeight = input.AnchorCommitWeight
×
222
                anchors = anchorSize
×
223
        }
224

225
        totalWeight := commitWeight + htlcWeight*numHTLCs
×
226

×
227
        return feePerKw.FeeForWeight(lntypes.WeightUnit(totalWeight)) + anchors
×
228
}
229

230
// CalculateMaxHtlc re-implements the RequiredRemoteChannelReserve of the
231
// funding manager's config, which corresponds to the maximum MaxHTLC value we
232
// allow users to set when updating a channel policy.
233
func CalculateMaxHtlc(chanCap btcutil.Amount) uint64 {
×
234
        const ratio = 100
×
235
        reserve := lnwire.NewMSatFromSatoshis(chanCap / ratio)
×
236
        max := lnwire.NewMSatFromSatoshis(chanCap) - reserve
×
237

×
238
        return uint64(max)
×
239
}
×
240

241
// CalcStaticFeeBuffer calculates appropriate fee buffer which must be taken
242
// into account when sending htlcs.
243
func CalcStaticFeeBuffer(c lnrpc.CommitmentType, numHTLCs int) btcutil.Amount {
×
244
        //nolint:ll
×
245
        const (
×
246
                htlcWeight         = input.HTLCWeight
×
247
                defaultSatPerVByte = lnwallet.DefaultAnchorsCommitMaxFeeRateSatPerVByte
×
248
                scale              = 1000
×
249
        )
×
250

×
251
        var (
×
252
                commitWeight = input.CommitWeight
×
253
                feePerKw     = chainfee.SatPerKWeight(DefaultFeeRateSatPerKw)
×
254
        )
×
255

×
256
        switch {
×
257
        // The taproot commitment type has the extra anchor outputs, but also a
258
        // smaller witness field (will just be a normal key spend), so we need
259
        // to account for that here as well.
260
        case CommitTypeHasTaproot(c):
×
261
                feePerKw = chainfee.SatPerKVByte(
×
262
                        defaultSatPerVByte * scale,
×
263
                ).FeePerKWeight()
×
264

×
265
                commitWeight = input.TaprootCommitWeight
×
266

267
        // The anchor commitment type is slightly heavier, and we must also add
268
        // the value of the two anchors to the resulting fee the initiator
269
        // pays. In addition the fee rate is capped at 10 sat/vbyte for anchor
270
        // channels.
271
        case CommitTypeHasAnchors(c):
×
272
                feePerKw = chainfee.SatPerKVByte(
×
273
                        defaultSatPerVByte * scale,
×
274
                ).FeePerKWeight()
×
275
                commitWeight = input.AnchorCommitWeight
×
276
        }
277

278
        // Account for the HTLC which will be required when sending an htlc.
279
        numHTLCs++
×
280

×
281
        totalWeight := commitWeight + numHTLCs*htlcWeight
×
282
        feeBuffer := lnwallet.CalcFeeBuffer(
×
283
                feePerKw, lntypes.WeightUnit(totalWeight),
×
284
        )
×
285

×
286
        return feeBuffer.ToSatoshis()
×
287
}
288

289
// CustomRecordsWithUnendorsed copies the map of custom records and adds an
290
// endorsed signal (replacing in the case of conflict) for assertion in tests.
291
func CustomRecordsWithUnendorsed(
292
        originalRecords lnwire.CustomRecords) map[uint64][]byte {
×
293

×
294
        return originalRecords.MergedCopy(map[uint64][]byte{
×
295
                uint64(lnwire.ExperimentalEndorsementType): {
×
296
                        lnwire.ExperimentalUnendorsed,
×
297
                }},
×
298
        )
×
299
}
×
300

301
// LnrpcOutpointToStr returns a string representation of an lnrpc.OutPoint.
302
func LnrpcOutpointToStr(outpoint *lnrpc.OutPoint) string {
×
303
        return fmt.Sprintf("%s:%d", outpoint.TxidStr, outpoint.OutputIndex)
×
304
}
×
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