@@ -24,6 +24,11 @@ import (
2424 "github.com/lightningnetwork/lnd/routing/route"
2525)
2626
27+ var (
28+ // errSwapNotFinished is returned when a swap is not finished.
29+ errSwapNotFinished = fmt .Errorf ("swap not finished" )
30+ )
31+
2732// Config contains the services required for the loop-in manager.
2833type Config struct {
2934 // Server is the client that is used to communicate with the static
@@ -219,11 +224,23 @@ func (m *Manager) Run(ctx context.Context, currentHeight uint32) error {
219224 }
220225}
221226
227+ // notifyNotFinished notifies the server that a swap is not finished by
228+ // sending an empty signature map.
229+ func (m * Manager ) notifyNotFinished (ctx context.Context , swapHash lntypes.Hash ,
230+ txId chainhash.Hash ) error {
231+ _ , err := m .cfg .Server .PushStaticAddressSweeplessSigs (
232+ ctx , & looprpc.PushStaticAddressSweeplessSigsRequest {
233+ SwapHash : swapHash [:],
234+ Txid : txId [:],
235+ })
236+ return err
237+ }
238+
222239// handleLoopInSweepReq handles a loop-in sweep request from the server.
223240// It first checks if the requested loop-in is finished as expected and if
224241// yes will send signature to the server for the provided psbt.
225242func (m * Manager ) handleLoopInSweepReq (ctx context.Context ,
226- req * swapserverrpc.ServerStaticLoopInSweepRequest ) error {
243+ req * swapserverrpc.ServerStaticLoopInSweepNotification ) error {
227244
228245 // First we'll check if the loop-ins are known to us and in
229246 // the expected state.
@@ -238,11 +255,18 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context,
238255 return err
239256 }
240257
241- // If the loop-in is not in the Succeeded state we return an
242- // error.
243- if loopIn .state != Succeeded {
244- return fmt .Errorf ("loop-in %v not in Succeeded state" ,
245- swapHash )
258+ loopIn .AddressParams , err =
259+ m .cfg .AddressManager .GetStaticAddressParameters (ctx )
260+
261+ if err != nil {
262+ return err
263+ }
264+
265+ loopIn .Address , err = m .cfg .AddressManager .GetStaticAddress (
266+ ctx ,
267+ )
268+ if err != nil {
269+ return err
246270 }
247271
248272 reader := bytes .NewReader (req .SweepTxPsbt )
@@ -253,16 +277,58 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context,
253277
254278 sweepTx := sweepPacket .UnsignedTx
255279
280+ // If the loop-in is not in the Succeeded state we return an
281+ // error.
282+ if loopIn .state != Succeeded {
283+ // We'll notify the server that we don't consider the swap
284+ // finished yet, so it can retry later.
285+ _ = m .notifyNotFinished (ctx , swapHash , sweepTx .TxHash ())
286+ return fmt .Errorf ("loop-in %v not in Succeeded state" ,
287+ swapHash )
288+ }
289+
256290 // Perform a sanity check on the number of unsigned tx inputs and
257291 // prevout info.
258292 if len (sweepTx .TxIn ) != len (req .PrevoutInfo ) {
259293 return fmt .Errorf ("expected %v inputs, got %v" ,
260294 len (req .PrevoutInfo ), len (sweepTx .TxIn ))
261295 }
262296
297+ // Check if all the deposits requested are part of the loop-in and
298+ // find them in the requested sweep.
299+ depositToIdxMap := make (map [string ]int )
300+ for reqOutpoint := range req .DepositToNonces {
301+ hasDeposit := false
302+ for _ , depositOutpoint := range loopIn .DepositOutpoints {
303+ if depositOutpoint == reqOutpoint {
304+ hasDeposit = true
305+ break
306+ }
307+ }
308+ if ! hasDeposit {
309+ return fmt .Errorf ("deposit outpoint not part of " +
310+ "loop-in" )
311+ }
312+
313+ foundDepositInTx := false
314+
315+ for i , txIn := range sweepTx .TxIn {
316+ if txIn .PreviousOutPoint .String () == reqOutpoint {
317+ depositToIdxMap [reqOutpoint ] = i
318+ foundDepositInTx = true
319+ break
320+ }
321+ }
322+
323+ if ! foundDepositInTx {
324+ return fmt .Errorf ("deposit outpoint not part of " +
325+ "sweep tx" )
326+ }
327+ }
328+
263329 prevoutMap := make (map [wire.OutPoint ]* wire.TxOut )
264- var depositOutpoint * wire.OutPoint
265330
331+ // Set all the prevouts in the prevout map.
266332 for i := range req .PrevoutInfo {
267333 prevout := req .PrevoutInfo [i ]
268334
@@ -271,13 +337,6 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context,
271337 return err
272338 }
273339
274- if i == int (req .OutputIndex ) {
275- depositOutpoint = & wire.OutPoint {
276- Hash : * txid ,
277- Index : prevout .OutputIndex ,
278- }
279- }
280-
281340 prevoutMap [wire.OutPoint {
282341 Hash : * txid ,
283342 Index : prevout .OutputIndex ,
@@ -287,24 +346,6 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context,
287346 }
288347 }
289348
290- // Check if the deposit outpoint is part of the loop-in.
291- if depositOutpoint == nil {
292- return fmt .Errorf ("deposit outpoint not part of loop-in" )
293- }
294-
295- foundDeposit := false
296- for _ , loopInDeposit := range loopIn .DepositOutpoints {
297- if loopInDeposit == fmt .Sprintf ("%v:%v" ,
298- depositOutpoint .Hash , depositOutpoint .Index ) {
299-
300- foundDeposit = true
301- }
302- }
303-
304- if ! foundDeposit {
305- return fmt .Errorf ("deposit outpoint not part of loop-in" )
306- }
307-
308349 prevOutputFetcher := txscript .NewMultiPrevOutFetcher (
309350 prevoutMap ,
310351 )
@@ -313,65 +354,67 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context,
313354 sweepPacket .UnsignedTx , prevOutputFetcher ,
314355 )
315356
316- taprootSigHash , err := txscript .CalcTaprootSignatureHash (
317- sigHashes , txscript .SigHashDefault , sweepPacket .UnsignedTx ,
318- int (req .OutputIndex ), prevOutputFetcher ,
319- )
320- if err != nil {
321- return err
322- }
357+ // We'll now sign for every deposit that is part of the loop-in.
358+ responseMap := make (map [string ]* looprpc.ClientSweeplessSigningInfo )
359+ for depositOutpoint , nonce := range req .DepositToNonces {
360+ taprootSigHash , err := txscript .CalcTaprootSignatureHash (
361+ sigHashes , txscript .SigHashDefault , sweepPacket .UnsignedTx ,
362+ depositToIdxMap [depositOutpoint ], prevOutputFetcher ,
363+ )
364+ if err != nil {
365+ return err
366+ }
323367
324- var (
325- serverNonce [musig2 .PubNonceSize ]byte
326- sigHash [32 ]byte
327- )
368+ var (
369+ serverNonce [musig2 .PubNonceSize ]byte
370+ sigHash [32 ]byte
371+ )
328372
329- copy (serverNonce [:], req .Nonce )
330- musig2Session , err := loopIn .createMusig2Session (ctx , m .cfg .Signer )
331- if err != nil {
332- return err
333- }
373+ copy (serverNonce [:], nonce )
374+ musig2Session , err := loopIn .createMusig2Session (
375+ ctx , m .cfg .Signer ,
376+ )
377+ if err != nil {
378+ return err
379+ }
334380
335- haveAllNonces , err := m .cfg .Signer .MuSig2RegisterNonces (
336- ctx , musig2Session .SessionID ,
337- [][musig2 .PubNonceSize ]byte {serverNonce },
338- )
339- if err != nil {
340- return err
341- }
381+ haveAllNonces , err := m .cfg .Signer .MuSig2RegisterNonces (
382+ ctx , musig2Session .SessionID ,
383+ [][musig2 .PubNonceSize ]byte {serverNonce },
384+ )
385+ if err != nil {
386+ return err
387+ }
342388
343- if ! haveAllNonces {
344- return fmt .Errorf ("expected all nonces to be registered" )
345- }
389+ if ! haveAllNonces {
390+ return fmt .Errorf ("expected all nonces to be " +
391+ "registered" )
392+ }
346393
347- copy (sigHash [:], taprootSigHash )
394+ copy (sigHash [:], taprootSigHash )
348395
349- // Since our MuSig2 session has all nonces, we can now create
350- // the local partial signature by signing the sig hash.
351- sig , err := m .cfg .Signer .MuSig2Sign (
352- ctx , musig2Session .SessionID , sigHash , false ,
353- )
354- if err != nil {
355- return err
396+ // Since our MuSig2 session has all nonces, we can now create
397+ // the local partial signature by signing the sig hash.
398+ sig , err := m .cfg .Signer .MuSig2Sign (
399+ ctx , musig2Session .SessionID , sigHash , true ,
400+ )
401+ if err != nil {
402+ return err
403+ }
404+
405+ responseMap [depositOutpoint ] = & looprpc.ClientSweeplessSigningInfo { //nolint:lll
406+ Nonce : musig2Session .PublicNonce [:],
407+ Sig : sig ,
408+ }
356409 }
357410
358411 txHash := sweepTx .TxHash ()
359412
360- // We'll now push the signature to the server.
361- msg := & looprpc.PushSingleDepositVersion {
362- TxHash : txHash [:],
363- OutputIndex : req .OutputIndex ,
364- Nonce : musig2Session .PublicNonce [:],
365- Sig : sig ,
366- }
367-
368413 _ , err = m .cfg .Server .PushStaticAddressSweeplessSigs (
369414 ctx , & looprpc.PushStaticAddressSweeplessSigsRequest {
370-
371- SwapHash : loopIn .SwapHash [:],
372- Message : & looprpc.PushStaticAddressSweeplessSigsRequest_SingleDepositVersion { //nolint:lll
373- SingleDepositVersion : msg ,
374- },
415+ SwapHash : loopIn .SwapHash [:],
416+ Txid : txHash [:],
417+ SigningInfo : responseMap ,
375418 },
376419 )
377420 return err
0 commit comments