Skip to content

Commit da4bcbe

Browse files
committed
loopdb: refactor the SwapContract to hold all HTLC keys
This commit adds a new struct to hold all HTLC keys and refactors the SwapContract which is used by both loopin and loopout swaps to use this new struct. The newly added internal keys will for now hold the script keys to keep everything equivalent but are already stored and read back if the protocol version is set to MuSig2.
1 parent cc5e26b commit da4bcbe

File tree

11 files changed

+273
-115
lines changed

11 files changed

+273
-115
lines changed

client_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,12 @@ func testLoopOutResume(t *testing.T, confs uint32, expired, preimageRevealed,
240240
Preimage: preimage,
241241
AmountRequested: amt,
242242
CltvExpiry: 744,
243-
ReceiverKey: receiverKey,
244-
SenderKey: senderKey,
243+
HtlcKeys: loopdb.HtlcKeys{
244+
SenderScriptKey: senderKey,
245+
SenderInternalPubKey: senderKey,
246+
ReceiverScriptKey: receiverKey,
247+
ReceiverInternalPubKey: receiverKey,
248+
},
245249
MaxSwapFee: 60000,
246250
MaxMinerFee: 50000,
247251
ProtocolVersion: protocolVersion,

loopdb/loop.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ import (
1010
"github.com/lightningnetwork/lnd/lntypes"
1111
)
1212

13+
// HtlcKeys is a holder of all keys used when constructing the swap HTLC. Since
14+
// it's used for both loop in and loop out swaps it may hold partial information
15+
// about the sender or receiver depending on the swap type.
16+
type HtlcKeys struct {
17+
// SenderScriptKey is the sender's public key that is used in the HTLC,
18+
// specifically when constructing the script spend scripts.
19+
SenderScriptKey [33]byte
20+
21+
// SenderInternalPubKey is the sender's internal pubkey that is used in
22+
// taproot HTLCs as part of the aggregate internal key.
23+
SenderInternalPubKey [33]byte
24+
25+
// ReceiverScriptKey is the receiver's public key that is used in the
26+
// HTLC, specifically when constructing the script spend scripts.
27+
ReceiverScriptKey [33]byte
28+
29+
// ReceiverInternalPubKey is the sender's internal pubkey that is used
30+
// in taproot HTLCs as part of the aggregate internal key.
31+
ReceiverInternalPubKey [33]byte
32+
33+
// ClientScriptKeyLocator is the client's key locator for the key used
34+
// in the HTLC script spend scripts.
35+
ClientScriptKeyLocator keychain.KeyLocator
36+
}
37+
1338
// SwapContract contains the base data that is serialized to persistent storage
1439
// for pending swaps.
1540
type SwapContract struct {
@@ -19,18 +44,8 @@ type SwapContract struct {
1944
// AmountRequested is the total amount of the swap.
2045
AmountRequested btcutil.Amount
2146

22-
// SenderKey is the key of the sender that will be used in the on-chain
23-
// HTLC.
24-
SenderKey [33]byte
25-
26-
// ReceiverKey is the of the receiver that will be used in the on-chain
27-
// HTLC.
28-
ReceiverKey [33]byte
29-
30-
// ClientKeyLocator is the key locator (family and index) for the client
31-
// key. It is for the receiver key if this is a loop out contract, or
32-
// the sender key if this is a loop in contract.
33-
ClientKeyLocator keychain.KeyLocator
47+
// HtlcKeys holds all keys used in the swap HTLC construction.
48+
HtlcKeys HtlcKeys
3449

3550
// CltvExpiry is the total absolute CLTV expiry of the swap.
3651
CltvExpiry int32

loopdb/loopin.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@ func serializeLoopInContract(swap *LoopInContract) (
6363
return nil, err
6464
}
6565

66-
n, err := b.Write(swap.SenderKey[:])
66+
n, err := b.Write(swap.HtlcKeys.SenderScriptKey[:])
6767
if err != nil {
6868
return nil, err
6969
}
7070
if n != keyLength {
7171
return nil, fmt.Errorf("sender key has invalid length")
7272
}
7373

74-
n, err = b.Write(swap.ReceiverKey[:])
74+
n, err = b.Write(swap.HtlcKeys.ReceiverScriptKey[:])
7575
if err != nil {
7676
return nil, err
7777
}
@@ -161,15 +161,15 @@ func deserializeLoopInContract(value []byte) (*LoopInContract, error) {
161161
return nil, err
162162
}
163163

164-
n, err := r.Read(contract.SenderKey[:])
164+
n, err := r.Read(contract.HtlcKeys.SenderScriptKey[:])
165165
if err != nil {
166166
return nil, err
167167
}
168168
if n != keyLength {
169169
return nil, fmt.Errorf("sender key has invalid length")
170170
}
171171

172-
n, err = r.Read(contract.ReceiverKey[:])
172+
n, err = r.Read(contract.HtlcKeys.ReceiverScriptKey[:])
173173
if err != nil {
174174
return nil, err
175175
}

loopdb/loopout.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,15 @@ func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) (
134134
return nil, err
135135
}
136136

137-
n, err := r.Read(contract.SenderKey[:])
137+
n, err := r.Read(contract.HtlcKeys.SenderScriptKey[:])
138138
if err != nil {
139139
return nil, err
140140
}
141141
if n != keyLength {
142142
return nil, fmt.Errorf("sender key has invalid length")
143143
}
144144

145-
n, err = r.Read(contract.ReceiverKey[:])
145+
n, err = r.Read(contract.HtlcKeys.ReceiverScriptKey[:])
146146
if err != nil {
147147
return nil, err
148148
}
@@ -229,15 +229,15 @@ func serializeLoopOutContract(swap *LoopOutContract) (
229229
return nil, err
230230
}
231231

232-
n, err := b.Write(swap.SenderKey[:])
232+
n, err := b.Write(swap.HtlcKeys.SenderScriptKey[:])
233233
if err != nil {
234234
return nil, err
235235
}
236236
if n != keyLength {
237237
return nil, fmt.Errorf("sender key has invalid length")
238238
}
239239

240-
n, err = b.Write(swap.ReceiverKey[:])
240+
n, err = b.Write(swap.HtlcKeys.ReceiverScriptKey[:])
241241
if err != nil {
242242
return nil, err
243243
}

loopdb/protocol_version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const (
6868

6969
// experimentalRPCProtocolVersion defines the RPC protocol version that
7070
// includes all currently experimentally released features.
71-
experimentalRPCProtocolVersion = looprpc.ProtocolVersion_HTLC_V3
71+
experimentalRPCProtocolVersion = looprpc.ProtocolVersion_MUSIG2
7272
)
7373

7474
var (

loopdb/store.go

Lines changed: 130 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,30 @@ var (
112112
// value: concatenation of uint32 values [family, index].
113113
keyLocatorKey = []byte("keylocator")
114114

115+
// senderInternalPubKeyKey is the key that stores the sender's internal
116+
// public key which used when constructing the swap HTLC.
117+
//
118+
// path: loopInBucket/loopOutBucket -> swapBucket[hash]
119+
// -> senderInternalPubKeyKey
120+
// value: serialized public key.
121+
senderInternalPubKeyKey = []byte("sender-internal-pubkey")
122+
123+
// receiverInternalPubKeyKey is the key that stores the receiver's
124+
// internal public key which is used when constructing the swap HTLC.
125+
//
126+
// path: loopInBucket/loopOutBucket -> swapBucket[hash]
127+
// -> receiverInternalPubKeyKey
128+
// value: serialized public key.
129+
receiverInternalPubKeyKey = []byte("receiver-internal-pubkey")
130+
115131
byteOrder = binary.BigEndian
116132

133+
// keyLength is the length of a serialized public key.
117134
keyLength = 33
135+
136+
// errInvalidKey is returned when a serialized key is not the expected
137+
// length.
138+
errInvalidKey = fmt.Errorf("invalid serialized key")
118139
)
119140

120141
const (
@@ -233,6 +254,95 @@ func NewBoltSwapStore(dbPath string, chainParams *chaincfg.Params) (
233254
}, nil
234255
}
235256

257+
// marshalHtlcKeys marshals the HTLC keys of the swap contract into the swap
258+
// bucket.
259+
func marshalHtlcKeys(swapBucket *bbolt.Bucket, contract *SwapContract) error {
260+
var err error
261+
262+
// Store the key locator for swaps that use taproot HTLCs.
263+
if contract.ProtocolVersion >= ProtocolVersionHtlcV3 {
264+
keyLocator, err := MarshalKeyLocator(
265+
contract.HtlcKeys.ClientScriptKeyLocator,
266+
)
267+
if err != nil {
268+
return err
269+
}
270+
271+
err = swapBucket.Put(keyLocatorKey, keyLocator)
272+
if err != nil {
273+
return err
274+
}
275+
}
276+
277+
// Store the internal keys for MuSig2 swaps.
278+
if contract.ProtocolVersion >= ProtocolVersionMuSig2 {
279+
// Internal pubkeys are always filled.
280+
err = swapBucket.Put(
281+
senderInternalPubKeyKey,
282+
contract.HtlcKeys.SenderInternalPubKey[:],
283+
)
284+
if err != nil {
285+
return err
286+
}
287+
288+
err = swapBucket.Put(
289+
receiverInternalPubKeyKey,
290+
contract.HtlcKeys.ReceiverInternalPubKey[:],
291+
)
292+
if err != nil {
293+
return err
294+
}
295+
}
296+
297+
return nil
298+
}
299+
300+
// unmarshalHtlcKeys deserializes the htlc keys from the swap bucket.
301+
func unmarshalHtlcKeys(swapBucket *bbolt.Bucket, contract *SwapContract) error {
302+
var err error
303+
304+
// HTLC V3 contracts have the client script key locator stored.
305+
if contract.ProtocolVersion >= ProtocolVersionHtlcV3 &&
306+
ProtocolVersionUnrecorded > contract.ProtocolVersion {
307+
308+
contract.HtlcKeys.ClientScriptKeyLocator, err =
309+
UnmarshalKeyLocator(
310+
swapBucket.Get(keyLocatorKey),
311+
)
312+
if err != nil {
313+
return err
314+
}
315+
316+
// Default the internal scriptkeys to the sender and receiver
317+
// keys.
318+
contract.HtlcKeys.SenderInternalPubKey =
319+
contract.HtlcKeys.SenderScriptKey
320+
contract.HtlcKeys.ReceiverInternalPubKey =
321+
contract.HtlcKeys.ReceiverScriptKey
322+
}
323+
324+
// MuSig2 contracts have the internal keys stored too.
325+
if contract.ProtocolVersion >= ProtocolVersionMuSig2 &&
326+
ProtocolVersionUnrecorded > contract.ProtocolVersion {
327+
328+
// The pubkeys used for the joint HTLC internal key are always
329+
// present.
330+
key := swapBucket.Get(senderInternalPubKeyKey)
331+
if len(key) != keyLength {
332+
return errInvalidKey
333+
}
334+
copy(contract.HtlcKeys.SenderInternalPubKey[:], key)
335+
336+
key = swapBucket.Get(receiverInternalPubKeyKey)
337+
if len(key) != keyLength {
338+
return errInvalidKey
339+
}
340+
copy(contract.HtlcKeys.ReceiverInternalPubKey[:], key)
341+
}
342+
343+
return nil
344+
}
345+
236346
// FetchLoopOutSwaps returns all loop out swaps currently in the store.
237347
//
238348
// NOTE: Part of the loopdb.SwapStore interface.
@@ -435,19 +545,10 @@ func (s *boltSwapStore) CreateLoopOut(hash lntypes.Hash,
435545
return err
436546
}
437547

438-
// Store the key locator for swaps with taproot htlc.
439-
if swap.ProtocolVersion >= ProtocolVersionHtlcV3 {
440-
keyLocator, err := MarshalKeyLocator(
441-
swap.ClientKeyLocator,
442-
)
443-
if err != nil {
444-
return err
445-
}
446-
447-
err = swapBucket.Put(keyLocatorKey, keyLocator)
448-
if err != nil {
449-
return err
450-
}
548+
// Store the htlc keys and server key locator.
549+
err = marshalHtlcKeys(swapBucket, &swap.SwapContract)
550+
if err != nil {
551+
return err
451552
}
452553

453554
// Finally, we'll create an empty updates bucket for this swap
@@ -501,19 +602,10 @@ func (s *boltSwapStore) CreateLoopIn(hash lntypes.Hash,
501602
return err
502603
}
503604

504-
// Store the key locator for swaps with taproot htlc.
505-
if swap.ProtocolVersion >= ProtocolVersionHtlcV3 {
506-
keyLocator, err := MarshalKeyLocator(
507-
swap.ClientKeyLocator,
508-
)
509-
if err != nil {
510-
return err
511-
}
512-
513-
err = swapBucket.Put(keyLocatorKey, keyLocator)
514-
if err != nil {
515-
return err
516-
}
605+
// Store the htlc keys and server key locator.
606+
err = marshalHtlcKeys(swapBucket, &swap.SwapContract)
607+
if err != nil {
608+
return err
517609
}
518610

519611
// Finally, we'll create an empty updates bucket for this swap
@@ -788,14 +880,12 @@ func (s *boltSwapStore) fetchLoopOutSwap(rootBucket *bbolt.Bucket,
788880
return nil, err
789881
}
790882

791-
// Try to unmarshal the key locator.
792-
if contract.ProtocolVersion >= ProtocolVersionHtlcV3 {
793-
contract.ClientKeyLocator, err = UnmarshalKeyLocator(
794-
swapBucket.Get(keyLocatorKey),
795-
)
796-
if err != nil {
797-
return nil, err
798-
}
883+
// Unmarshal HTLC keys if the contract is recent.
884+
err = unmarshalHtlcKeys(
885+
swapBucket, &contract.SwapContract,
886+
)
887+
if err != nil {
888+
return nil, err
799889
}
800890

801891
loop := LoopOut{
@@ -865,14 +955,12 @@ func (s *boltSwapStore) fetchLoopInSwap(rootBucket *bbolt.Bucket,
865955
return nil, err
866956
}
867957

868-
// Try to unmarshal the key locator.
869-
if contract.ProtocolVersion >= ProtocolVersionHtlcV3 {
870-
contract.ClientKeyLocator, err = UnmarshalKeyLocator(
871-
swapBucket.Get(keyLocatorKey),
872-
)
873-
if err != nil {
874-
return nil, err
875-
}
958+
// Unmarshal HTLC keys if the contract is recent.
959+
err = unmarshalHtlcKeys(
960+
swapBucket, &contract.SwapContract,
961+
)
962+
if err != nil {
963+
return nil, err
876964
}
877965

878966
loop := LoopIn{

0 commit comments

Comments
 (0)