Skip to content

Commit 90ae922

Browse files
committed
loopin: generate and send internal key for MuSig2 swaps
This commit changes how we create loopin swaps if the client activates the experimental MuSig2 features. When creating a new loopin swap the client will create (and store) a new key that will be used as the sender's internal key when constructing the HTLC. The client will send the public part to the server and will also receive (and store) the server's (receiver) internal public key.
1 parent da4bcbe commit 90ae922

File tree

3 files changed

+76
-21
lines changed

3 files changed

+76
-21
lines changed

loopin.go

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"sync"
1010
"time"
1111

12+
"github.com/btcsuite/btcd/btcec/v2"
1213
"github.com/btcsuite/btcd/btcutil"
1314
"github.com/btcsuite/btcd/chaincfg/chainhash"
1415
"github.com/btcsuite/btcd/mempool"
@@ -19,6 +20,7 @@ import (
1920
"github.com/lightninglabs/loop/swap"
2021
"github.com/lightningnetwork/lnd/chainntnfs"
2122
invpkg "github.com/lightningnetwork/lnd/invoices"
23+
"github.com/lightningnetwork/lnd/keychain"
2224
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
2325
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
2426
"github.com/lightningnetwork/lnd/lntypes"
@@ -189,6 +191,23 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
189191
return nil, err
190192
}
191193

194+
// Default the HTLC internal key to our sender key.
195+
senderInternalPubKey := senderKey
196+
197+
// If this is a MuSig2 swap then we'll generate a brand new key pair
198+
// and will use that as the internal key for the HTLC.
199+
if loopdb.CurrentProtocolVersion() >= loopdb.ProtocolVersionMuSig2 {
200+
secret, err := sharedSecretFromHash(
201+
globalCtx, cfg.lnd.Signer, swapHash,
202+
)
203+
if err != nil {
204+
return nil, err
205+
}
206+
207+
_, pubKey := btcec.PrivKeyFromBytes(secret[:])
208+
copy(senderInternalPubKey[:], pubKey.SerializeCompressed())
209+
}
210+
192211
// Create a cancellable context that is used for monitoring the probe.
193212
probeWaitCtx, probeWaitCancel := context.WithCancel(globalCtx)
194213

@@ -204,8 +223,8 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
204223
// htlc.
205224
log.Infof("Initiating swap request at height %v", currentHeight)
206225
swapResp, err := cfg.server.NewLoopInSwap(globalCtx, swapHash,
207-
request.Amount, senderKey, swapInvoice, probeInvoice,
208-
request.LastHop, request.Initiator,
226+
request.Amount, senderKey, senderInternalPubKey, swapInvoice,
227+
probeInvoice, request.LastHop, request.Initiator,
209228
)
210229
probeWaitCancel()
211230
if err != nil {
@@ -254,6 +273,13 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
254273
},
255274
}
256275

276+
// For MuSig2 swaps we store the proper internal keys that we generated
277+
// and received from the server.
278+
if loopdb.CurrentProtocolVersion() >= loopdb.ProtocolVersionMuSig2 {
279+
contract.HtlcKeys.SenderInternalPubKey = senderInternalPubKey
280+
contract.HtlcKeys.ReceiverInternalPubKey = swapResp.receiverInternalKey
281+
}
282+
257283
swapKit := newSwapKit(
258284
swapHash, swap.TypeIn,
259285
cfg, &contract.SwapContract,
@@ -1031,3 +1057,18 @@ func (s *loopInSwap) setState(state loopdb.SwapState) {
10311057
s.lastUpdateTime = time.Now()
10321058
s.state = state
10331059
}
1060+
1061+
// sharedSecretFromHash derives the shared secret from the swap hash using the
1062+
// swap.KeyFamily family and zero as index.
1063+
func sharedSecretFromHash(ctx context.Context, signer lndclient.SignerClient,
1064+
hash lntypes.Hash) ([32]byte, error) {
1065+
1066+
_, hashPubKey := btcec.PrivKeyFromBytes(hash[:])
1067+
1068+
return signer.DeriveSharedKey(
1069+
ctx, hashPubKey, &keychain.KeyLocator{
1070+
Family: keychain.KeyFamily(swap.KeyFamily),
1071+
Index: 0,
1072+
},
1073+
)
1074+
}

server_mock_test.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,17 +151,22 @@ func getInvoice(hash lntypes.Hash, amt btcutil.Amount, memo string) (string, err
151151
}
152152

153153
func (s *serverMock) NewLoopInSwap(_ context.Context, swapHash lntypes.Hash,
154-
amount btcutil.Amount, _ [33]byte, swapInvoice, _ string,
154+
amount btcutil.Amount, _, _ [33]byte, swapInvoice, _ string,
155155
_ *route.Vertex, _ string) (*newLoopInResponse, error) {
156156

157157
_, receiverKey := test.CreateKey(101)
158+
_, receiverInternalKey := test.CreateKey(102)
158159

159160
if amount != s.expectedSwapAmt {
160161
return nil, errors.New("unexpected test swap amount")
161162
}
162163

163-
var receiverKeyArray [33]byte
164+
var receiverKeyArray, receiverInternalKeyArray [33]byte
164165
copy(receiverKeyArray[:], receiverKey.SerializeCompressed())
166+
copy(
167+
receiverInternalKeyArray[:],
168+
receiverInternalKey.SerializeCompressed(),
169+
)
165170

166171
s.swapInvoice = swapInvoice
167172
s.swapHash = swapHash
@@ -175,8 +180,9 @@ func (s *serverMock) NewLoopInSwap(_ context.Context, swapHash lntypes.Hash,
175180
<-s.lnd.FailInvoiceChannel
176181

177182
resp := &newLoopInResponse{
178-
expiry: s.height + testChargeOnChainCltvDelta,
179-
receiverKey: receiverKeyArray,
183+
expiry: s.height + testChargeOnChainCltvDelta,
184+
receiverKey: receiverKeyArray,
185+
receiverInternalKey: receiverInternalKeyArray,
180186
}
181187

182188
return resp, nil

swap_server_client.go

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ type swapServerClient interface {
9494
preimage lntypes.Preimage) error
9595

9696
NewLoopInSwap(ctx context.Context,
97-
swapHash lntypes.Hash, amount btcutil.Amount,
98-
senderKey [33]byte, swapInvoice, probeInvoice string,
99-
lastHop *route.Vertex, initiator string) (*newLoopInResponse,
100-
error)
97+
swapHash lntypes.Hash, amount btcutil.Amount, senderScriptKey,
98+
senderInternalKey [33]byte, swapInvoice, probeInvoice string,
99+
lastHop *route.Vertex, initiator string) (
100+
*newLoopInResponse, error)
101101

102102
// SubscribeLoopOutUpdates subscribes to loop out server state.
103103
SubscribeLoopOutUpdates(ctx context.Context,
@@ -419,17 +419,17 @@ func (s *grpcSwapServerClient) PushLoopOutPreimage(ctx context.Context,
419419
}
420420

421421
func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
422-
swapHash lntypes.Hash, amount btcutil.Amount, senderKey [33]byte,
423-
swapInvoice, probeInvoice string, lastHop *route.Vertex,
424-
initiator string) (*newLoopInResponse, error) {
422+
swapHash lntypes.Hash, amount btcutil.Amount, senderScriptKey,
423+
senderInternalKey [33]byte, swapInvoice, probeInvoice string,
424+
lastHop *route.Vertex, initiator string) (*newLoopInResponse, error) {
425425

426426
rpcCtx, rpcCancel := context.WithTimeout(ctx, globalCallTimeout)
427427
defer rpcCancel()
428428

429429
req := &looprpc.ServerLoopInRequest{
430430
SwapHash: swapHash[:],
431431
Amt: uint64(amount),
432-
SenderKey: senderKey[:],
432+
SenderKey: senderScriptKey[:],
433433
SwapInvoice: swapInvoice,
434434
ProtocolVersion: loopdb.CurrentRPCProtocolVersion(),
435435
ProbeInvoice: probeInvoice,
@@ -439,13 +439,19 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
439439
req.LastHop = lastHop[:]
440440
}
441441

442+
// Set the client's internal key if this is a MuSig2 swap.
443+
if loopdb.CurrentProtocolVersion() >= loopdb.ProtocolVersionMuSig2 {
444+
req.SenderInternalPubkey = senderInternalKey[:]
445+
}
446+
442447
swapResp, err := s.server.NewLoopInSwap(rpcCtx, req)
443448
if err != nil {
444449
return nil, err
445450
}
446451

447-
var receiverKey [33]byte
452+
var receiverKey, receiverInternalKey [33]byte
448453
copy(receiverKey[:], swapResp.ReceiverKey)
454+
copy(receiverInternalKey[:], swapResp.ReceiverInternalPubkey)
449455

450456
// Validate receiver key.
451457
_, err = btcec.ParsePubKey(receiverKey[:])
@@ -454,9 +460,10 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context,
454460
}
455461

456462
return &newLoopInResponse{
457-
receiverKey: receiverKey,
458-
expiry: swapResp.Expiry,
459-
serverMessage: swapResp.ServerMessage,
463+
receiverKey: receiverKey,
464+
receiverInternalKey: receiverInternalKey,
465+
expiry: swapResp.Expiry,
466+
serverMessage: swapResp.ServerMessage,
460467
}, nil
461468
}
462469

@@ -873,7 +880,8 @@ type newLoopOutResponse struct {
873880
}
874881

875882
type newLoopInResponse struct {
876-
receiverKey [33]byte
877-
expiry int32
878-
serverMessage string
883+
receiverKey [33]byte
884+
receiverInternalKey [33]byte
885+
expiry int32
886+
serverMessage string
879887
}

0 commit comments

Comments
 (0)