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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 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"
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 {
280✔
136
        return s.localFundingAmt
280✔
137
}
280✔
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 {
139✔
143
        return s.remoteFundingAmt
139✔
144
}
139✔
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