Skip to content

Commit b47f67a

Browse files
committed
loopin: push the htlc internal key if the invoice is swept
This commit adds key reveal to MuSig2 loopin swaps' success path. In this case the client reveals their internal HTLC key to the server when the swap invoice is settled. With this key the server can sweep the swap HTLC without any more interaction from the client. We'll do this every block (after the invoice has been settled).
1 parent 90ae922 commit b47f67a

File tree

6 files changed

+458
-190
lines changed

6 files changed

+458
-190
lines changed

loopin.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
839839

840840
htlcSpend := false
841841
invoiceFinalized := false
842+
htlcKeyRevealed := false
842843
for !htlcSpend || !invoiceFinalized {
843844
select {
844845
// Spend notification error.
@@ -855,6 +856,10 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
855856
return err
856857
}
857858

859+
if invoiceFinalized && !htlcKeyRevealed {
860+
htlcKeyRevealed = s.tryPushHtlcKey(ctx)
861+
}
862+
858863
// The htlc spend is confirmed. Inspect the spending tx to
859864
// determine the final swap state.
860865
case spendDetails := <-spendChan:
@@ -918,6 +923,7 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
918923
}
919924

920925
invoiceFinalized = true
926+
htlcKeyRevealed = s.tryPushHtlcKey(ctx)
921927

922928
// Canceled invoice has no effect on server cost
923929
// balance.
@@ -933,6 +939,36 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
933939
return nil
934940
}
935941

942+
// tryPushHtlcKey attempts to push the htlc key to the server. If the server
943+
// returns an error of any kind we'll log it as a warning but won't act as
944+
// the swap execution can just go on without the server gaining knowledge of
945+
// our internal key.
946+
func (s *loopInSwap) tryPushHtlcKey(ctx context.Context) bool {
947+
if s.ProtocolVersion < loopdb.ProtocolVersionMuSig2 {
948+
return false
949+
}
950+
951+
log.Infof("Attempting to reveal internal HTLC key to the server")
952+
953+
internalPrivKey, err := sharedSecretFromHash(
954+
ctx, s.swapConfig.lnd.Signer, s.hash,
955+
)
956+
if err != nil {
957+
s.log.Warnf("Unable to derive HTLC internal private key: %v",
958+
err)
959+
960+
return false
961+
}
962+
963+
err = s.server.PushKey(ctx, s.ProtocolVersion, s.hash, internalPrivKey)
964+
if err != nil {
965+
s.log.Warnf("Internal HTLC key reveal failed: %v", err)
966+
return false
967+
}
968+
969+
return true
970+
}
971+
936972
func (s *loopInSwap) processHtlcSpend(ctx context.Context,
937973
spend *chainntnfs.SpendDetail, htlcValue,
938974
sweepFee btcutil.Amount) error {

server_mock_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,9 @@ func (s *serverMock) MuSig2SignSweep(_ context.Context, _ loopdb.ProtocolVersion
267267

268268
return nil, nil, nil
269269
}
270+
271+
func (s *serverMock) PushKey(_ context.Context, _ loopdb.ProtocolVersion,
272+
_ lntypes.Hash, _ [32]byte) error {
273+
274+
return nil
275+
}

swap_server_client.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ type swapServerClient interface {
128128
protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash,
129129
paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte) (
130130
[]byte, []byte, error)
131+
132+
// PushKey sends the client's HTLC internal key associated with the
133+
// swap to the server.
134+
PushKey(ctx context.Context,
135+
protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash,
136+
clientInternalPrivateKey [32]byte) error
131137
}
132138

133139
type grpcSwapServerClient struct {
@@ -760,6 +766,25 @@ func (s *grpcSwapServerClient) MuSig2SignSweep(ctx context.Context,
760766
return res.Nonce, res.PartialSignature, nil
761767
}
762768

769+
// PushKey sends the client's HTLC internal key associated with the swap to
770+
// the server.
771+
func (s *grpcSwapServerClient) PushKey(ctx context.Context,
772+
protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash,
773+
clientInternalPrivateKey [32]byte) error {
774+
775+
req := &looprpc.ServerPushKeyReq{
776+
ProtocolVersion: looprpc.ProtocolVersion(protocolVersion),
777+
SwapHash: swapHash[:],
778+
InternalPrivkey: clientInternalPrivateKey[:],
779+
}
780+
781+
rpcCtx, rpcCancel := context.WithTimeout(ctx, globalCallTimeout)
782+
defer rpcCancel()
783+
784+
_, err := s.server.PushKey(rpcCtx, req)
785+
return err
786+
}
787+
763788
func rpcRouteCancel(details *outCancelDetails) (
764789
*looprpc.CancelLoopOutSwapRequest_RouteCancel, error) {
765790

0 commit comments

Comments
 (0)