• 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

83.87
/lnwallet/chanfunding/canned_assembler.go
1
package chanfunding
2

3
import (
4
        "fmt"
5

6
        "github.com/btcsuite/btcd/btcec/v2"
7
        "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
8
        "github.com/btcsuite/btcd/btcutil"
9
        "github.com/btcsuite/btcd/chaincfg/chainhash"
10
        "github.com/btcsuite/btcd/wire"
11
        "github.com/lightningnetwork/lnd/fn/v2"
12
        "github.com/lightningnetwork/lnd/input"
13
        "github.com/lightningnetwork/lnd/keychain"
14
)
15

16
// NewShimIntent creates a new ShimIntent. This is only used for testing.
17
func NewShimIntent(localAmt, remoteAmt btcutil.Amount,
18
        localKey *keychain.KeyDescriptor, remoteKey *btcec.PublicKey,
19
        chanPoint *wire.OutPoint, thawHeight uint32, musig2 bool) *ShimIntent {
1✔
20

1✔
21
        return &ShimIntent{
1✔
22
                localFundingAmt:  localAmt,
1✔
23
                remoteFundingAmt: remoteAmt,
1✔
24
                localKey:         localKey,
1✔
25
                remoteKey:        remoteKey,
1✔
26
                chanPoint:        chanPoint,
1✔
27
                thawHeight:       thawHeight,
1✔
28
                musig2:           musig2,
1✔
29
        }
1✔
30
}
1✔
31

32
// ShimIntent is an intent created by the CannedAssembler which represents a
33
// funding output to be created that was constructed outside the wallet. This
34
// might be used when a hardware wallet, or a channel factory is the entity
35
// crafting the funding transaction, and not lnd.
36
type ShimIntent struct {
37
        // localFundingAmt is the final amount we put into the funding output.
38
        localFundingAmt btcutil.Amount
39

40
        // remoteFundingAmt is the final amount the remote party put into the
41
        // funding output.
42
        remoteFundingAmt btcutil.Amount
43

44
        // localKey is our multi-sig key.
45
        localKey *keychain.KeyDescriptor
46

47
        // remoteKey is the remote party's multi-sig key.
48
        remoteKey *btcec.PublicKey
49

50
        // chanPoint is the final channel point for the to be created channel.
51
        chanPoint *wire.OutPoint
52

53
        // thawHeight, if non-zero is the height where this channel will become
54
        // a normal channel. Until this height, it's considered frozen, so it
55
        // can only be cooperatively closed by the responding party.
56
        thawHeight uint32
57

58
        // musig2 determines if the funding output should use musig2 to
59
        // generate an aggregate key to use as the taproot-native multi-sig
60
        // output.
61
        musig2 bool
62

63
        // tapscriptRoot is the root of the tapscript tree that will be used to
64
        // create the funding output. This field will only be utilized if the
65
        // MuSig2 flag above is set to true.
66
        //
67
        // TODO(roasbeef): fold above into new chan type? sum type like thing,
68
        // includes the tapscript root, etc
69
        tapscriptRoot fn.Option[chainhash.Hash]
70
}
71

72
// FundingOutput returns the witness script, and the output that creates the
73
// funding output.
74
//
75
// NOTE: This method satisfies the chanfunding.Intent interface.
76
func (s *ShimIntent) FundingOutput() ([]byte, *wire.TxOut, error) {
211✔
77
        if s.localKey == nil || s.remoteKey == nil {
287✔
78
                return nil, nil, fmt.Errorf("unable to create witness " +
76✔
79
                        "script, no funding keys")
76✔
80
        }
76✔
81

82
        totalAmt := s.localFundingAmt + s.remoteFundingAmt
135✔
83

135✔
84
        // If musig2 is active, then we'll return a single aggregated key
135✔
85
        // rather than using the "existing" funding script.
135✔
86
        if s.musig2 {
147✔
87
                // Similar to the existing p2wsh script, we'll always ensure
12✔
88
                // the keys are sorted before use.
12✔
89
                return input.GenTaprootFundingScript(
12✔
90
                        s.localKey.PubKey, s.remoteKey, int64(totalAmt),
12✔
91
                        s.tapscriptRoot,
12✔
92
                )
12✔
93
        }
12✔
94

95
        return input.GenFundingPkScript(
123✔
96
                s.localKey.PubKey.SerializeCompressed(),
123✔
97
                s.remoteKey.SerializeCompressed(),
123✔
98
                int64(totalAmt),
123✔
99
        )
123✔
100
}
101

102
// TaprootInternalKey may return the internal key for a MuSig2 funding output,
103
// but only if this is actually a MuSig2 channel.
104
func (s *ShimIntent) TaprootInternalKey() fn.Option[*btcec.PublicKey] {
21✔
105
        if !s.musig2 {
42✔
106
                return fn.None[*btcec.PublicKey]()
21✔
107
        }
21✔
108

109
        // Similar to the existing p2wsh script, we'll always ensure the keys
110
        // are sorted before use. Since we're only interested in the internal
111
        // key, we don't need to take into account any tapscript root.
112
        //
113
        // We ignore the error here as this is only called after FundingOutput
114
        // is called.
UNCOV
115
        combinedKey, _, _, _ := musig2.AggregateKeys(
×
UNCOV
116
                []*btcec.PublicKey{s.localKey.PubKey, s.remoteKey}, true,
×
UNCOV
117
        )
×
UNCOV
118

×
UNCOV
119
        return fn.Some(combinedKey.PreTweakedKey)
×
120
}
121

122
// Cancel allows the caller to cancel a funding Intent at any time.  This will
123
// return any resources such as coins back to the eligible pool to be used in
124
// order channel fundings.
125
//
126
// NOTE: This method satisfies the chanfunding.Intent interface.
127
func (s *ShimIntent) Cancel() {
22✔
128
}
22✔
129

130
// LocalFundingAmt is the amount we put into the channel. This may differ from
131
// the local amount requested, as depending on coin selection, we may bleed
132
// from of that LocalAmt into fees to minimize change.
133
//
134
// NOTE: This method satisfies the chanfunding.Intent interface.
135
func (s *ShimIntent) LocalFundingAmt() btcutil.Amount {
282✔
136
        return s.localFundingAmt
282✔
137
}
282✔
138

139
// RemoteFundingAmt is the amount the remote party put into the channel.
140
//
141
// NOTE: This method satisfies the chanfunding.Intent interface.
142
func (s *ShimIntent) RemoteFundingAmt() btcutil.Amount {
140✔
143
        return s.remoteFundingAmt
140✔
144
}
140✔
145

146
// ChanPoint returns the final outpoint that will create the funding output
147
// described above.
148
//
149
// NOTE: This method satisfies the chanfunding.Intent interface.
150
func (s *ShimIntent) ChanPoint() (*wire.OutPoint, error) {
49✔
151
        if s.chanPoint == nil {
49✔
152
                return nil, fmt.Errorf("chan point unknown, funding output " +
×
153
                        "not constructed")
×
154
        }
×
155

156
        return s.chanPoint, nil
49✔
157
}
158

159
// ThawHeight returns the height where this channel goes back to being a normal
160
// channel.
161
func (s *ShimIntent) ThawHeight() uint32 {
9✔
162
        return s.thawHeight
9✔
163
}
9✔
164

165
// Inputs returns all inputs to the final funding transaction that we
166
// know about. For the ShimIntent this will always be none, since it is funded
167
// externally.
168
func (s *ShimIntent) Inputs() []wire.OutPoint {
5✔
169
        return nil
5✔
170
}
5✔
171

172
// Outputs returns all outputs of the final funding transaction that we
173
// know about. Since this is an externally funded channel, the channel output
174
// is the only known one.
175
func (s *ShimIntent) Outputs() []*wire.TxOut {
82✔
176
        _, txOut, err := s.FundingOutput()
82✔
177
        if err != nil {
158✔
178
                log.Warnf("Unable to find funding output for shim intent: %v",
76✔
179
                        err)
76✔
180

76✔
181
                // Failed finding funding output, return empty list of known
76✔
182
                // outputs.
76✔
183
                return nil
76✔
184
        }
76✔
185

186
        return []*wire.TxOut{txOut}
6✔
187
}
188

189
// FundingKeys couples our multi-sig key along with the remote party's key.
190
type FundingKeys struct {
191
        // LocalKey is our multi-sig key.
192
        LocalKey *keychain.KeyDescriptor
193

194
        // RemoteKey is the multi-sig key of the remote party.
195
        RemoteKey *btcec.PublicKey
196
}
197

198
// MultiSigKeys returns the committed multi-sig keys, but only if they've been
199
// specified/provided.
200
func (s *ShimIntent) MultiSigKeys() (*FundingKeys, error) {
9✔
201
        if s.localKey == nil || s.remoteKey == nil {
9✔
202
                return nil, fmt.Errorf("unknown funding keys")
×
203
        }
×
204

205
        return &FundingKeys{
9✔
206
                LocalKey:  s.localKey,
9✔
207
                RemoteKey: s.remoteKey,
9✔
208
        }, nil
9✔
209
}
210

211
// A compile-time check to ensure ShimIntent adheres to the Intent interface.
212
var _ Intent = (*ShimIntent)(nil)
213

214
// CannedAssembler is a type of chanfunding.Assembler wherein the funding
215
// transaction is constructed outside of lnd, and may already exist. This
216
// Assembler serves as a shim which gives the funding flow the only thing it
217
// actually needs to proceed: the channel point.
218
type CannedAssembler struct {
219
        // fundingAmt is the total amount of coins in the funding output.
220
        fundingAmt btcutil.Amount
221

222
        // localKey is our multi-sig key.
223
        localKey *keychain.KeyDescriptor
224

225
        // remoteKey is the remote party's multi-sig key.
226
        remoteKey *btcec.PublicKey
227

228
        // chanPoint is the final channel point for the to be created channel.
229
        chanPoint wire.OutPoint
230

231
        // initiator indicates if we're the initiator or the channel or not.
232
        initiator bool
233

234
        // thawHeight, if non-zero is the height where this channel will become
235
        // a normal channel. Until this height, it's considered frozen, so it
236
        // can only be cooperatively closed by the responding party.
237
        thawHeight uint32
238

239
        // musig2 determines if the funding output should use musig2 to
240
        // generate an aggregate key to use as the taproot-native multi-sig
241
        // output.
242
        musig2 bool
243
}
244

245
// NewCannedAssembler creates a new CannedAssembler from the material required
246
// to construct a funding output and channel point.
247
//
248
// TODO(roasbeef): pass in chan type instead?
249
func NewCannedAssembler(thawHeight uint32, chanPoint wire.OutPoint,
250
        fundingAmt btcutil.Amount, localKey *keychain.KeyDescriptor,
251
        remoteKey *btcec.PublicKey, initiator, musig2 bool) *CannedAssembler {
8✔
252

8✔
253
        return &CannedAssembler{
8✔
254
                initiator:  initiator,
8✔
255
                localKey:   localKey,
8✔
256
                remoteKey:  remoteKey,
8✔
257
                fundingAmt: fundingAmt,
8✔
258
                chanPoint:  chanPoint,
8✔
259
                thawHeight: thawHeight,
8✔
260
                musig2:     musig2,
8✔
261
        }
8✔
262
}
8✔
263

264
// ProvisionChannel creates a new ShimIntent given the passed funding Request.
265
// The returned intent is immediately able to provide the channel point and
266
// funding output as they've already been created outside lnd.
267
//
268
// NOTE: This method satisfies the chanfunding.Assembler interface.
269
func (c *CannedAssembler) ProvisionChannel(req *Request) (Intent, error) {
8✔
270
        // We'll exit out if SubtractFees is set as the funding transaction has
8✔
271
        // already been assembled, so we don't influence coin selection.
8✔
272
        if req.SubtractFees {
8✔
273
                return nil, fmt.Errorf("SubtractFees ignored, funding " +
×
274
                        "transaction is frozen")
×
275
        }
×
276

277
        // We'll exit out if FundUpToMaxAmt or MinFundAmt is set as the funding
278
        // transaction has already been assembled, so we don't influence coin
279
        // selection.
280
        if req.FundUpToMaxAmt != 0 || req.MinFundAmt != 0 {
8✔
281
                return nil, fmt.Errorf("FundUpToMaxAmt and MinFundAmt " +
×
282
                        "ignored, funding transaction is frozen")
×
283
        }
×
284

285
        intent := &ShimIntent{
8✔
286
                localKey:   c.localKey,
8✔
287
                remoteKey:  c.remoteKey,
8✔
288
                chanPoint:  &c.chanPoint,
8✔
289
                thawHeight: c.thawHeight,
8✔
290
                musig2:     c.musig2,
8✔
291
        }
8✔
292

8✔
293
        if c.initiator {
12✔
294
                intent.localFundingAmt = c.fundingAmt
4✔
295
        } else {
8✔
296
                intent.remoteFundingAmt = c.fundingAmt
4✔
297
        }
4✔
298

299
        // A simple sanity check to ensure the provisioned request matches the
300
        // re-made shim intent.
301
        if req.LocalAmt+req.RemoteAmt != c.fundingAmt {
8✔
302
                return nil, fmt.Errorf("intent doesn't match canned "+
×
303
                        "assembler: local_amt=%v, remote_amt=%v, funding_amt=%v",
×
304
                        req.LocalAmt, req.RemoteAmt, c.fundingAmt)
×
305
        }
×
306

307
        return intent, nil
8✔
308
}
309

310
// A compile-time assertion to ensure CannedAssembler meets the Assembler
311
// interface.
312
var _ Assembler = (*CannedAssembler)(nil)
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