From 246becb10a8cf3de2be271c4416798db504235e4 Mon Sep 17 00:00:00 2001 From: sputn1ck Date: Mon, 21 Oct 2024 21:15:09 +0200 Subject: [PATCH] instantout: update package to use new fsm context This commit updates the instantout package to use the new fsm context instead of the old fsm context. This is only a first step in the process of migrating the instantout package to the new fsm context. The next step should be to remove the stored context in the instantout manager. --- instantout/actions.go | 131 +++++++++++++++++++++++------------------- instantout/fsm.go | 19 +++--- instantout/manager.go | 10 ++-- 3 files changed, 82 insertions(+), 78 deletions(-) diff --git a/instantout/actions.go b/instantout/actions.go index 33489897..eb8a6415 100644 --- a/instantout/actions.go +++ b/instantout/actions.go @@ -65,7 +65,9 @@ type InitInstantOutCtx struct { // InitInstantOutAction is the first action that is executed when the instant // out FSM is started. It will send the instant out request to the server. -func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType { +func (f *FSM) InitInstantOutAction(ctx context.Context, + eventCtx fsm.EventContext) fsm.EventType { + initCtx, ok := eventCtx.(*InitInstantOutCtx) if !ok { return f.HandleError(fsm.ErrInvalidContextType) @@ -86,9 +88,7 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType { // The requested amount needs to be full reservation amounts. for _, reservationId := range initCtx.reservations { resId := reservationId - res, err := f.cfg.ReservationManager.GetReservation( - f.ctx, resId, - ) + res, err := f.cfg.ReservationManager.GetReservation(ctx, resId) if err != nil { return f.HandleError(err) } @@ -120,7 +120,7 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType { } // Create the keys for the swap. - keyRes, err := f.cfg.Wallet.DeriveNextKey(f.ctx, KeyFamily) + keyRes, err := f.cfg.Wallet.DeriveNextKey(ctx, KeyFamily) if err != nil { return f.HandleError(err) } @@ -128,7 +128,7 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType { swapHash := preimage.Hash() // Create a high fee rate so that the htlc will be confirmed quickly. - feeRate, err := f.cfg.Wallet.EstimateFeeRate(f.ctx, urgentConfTarget) + feeRate, err := f.cfg.Wallet.EstimateFeeRate(ctx, urgentConfTarget) if err != nil { f.Infof("error estimating fee rate: %v", err) return f.HandleError(err) @@ -136,7 +136,7 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType { // Send the instantout request to the server. instantOutResponse, err := f.cfg.InstantOutClient.RequestInstantLoopOut( - f.ctx, + ctx, &swapserverrpc.InstantLoopOutRequest{ ReceiverKey: keyRes.PubKey.SerializeCompressed(), SwapHash: swapHash[:], @@ -151,7 +151,7 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType { } // Decode the invoice to check if the hash is valid. payReq, err := f.cfg.LndClient.DecodePaymentRequest( - f.ctx, instantOutResponse.SwapInvoice, + ctx, instantOutResponse.SwapInvoice, ) if err != nil { return f.HandleError(err) @@ -170,7 +170,7 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType { sweepAddress := initCtx.sweepAddress if sweepAddress == nil { sweepAddress, err = f.cfg.Wallet.NextAddr( - f.ctx, lnwallet.DefaultAccountName, + ctx, lnwallet.DefaultAccountName, walletrpc.AddressType_TAPROOT_PUBKEY, false, ) if err != nil { @@ -196,7 +196,7 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType { sweepAddress: sweepAddress, } - err = f.cfg.Store.CreateInstantLoopOut(f.ctx, instantOut) + err = f.cfg.Store.CreateInstantLoopOut(ctx, instantOut) if err != nil { return f.HandleError(err) } @@ -208,21 +208,23 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType { // PollPaymentAcceptedAction locks the reservations, sends the payment to the // server and polls the server for the payment status. -func (f *FSM) PollPaymentAcceptedAction(_ fsm.EventContext) fsm.EventType { +func (f *FSM) PollPaymentAcceptedAction(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + // Now that we're doing the swap, we first lock the reservations // so that they can't be used for other swaps. for _, reservation := range f.InstantOut.Reservations { err := f.cfg.ReservationManager.LockReservation( - f.ctx, reservation.ID, + ctx, reservation.ID, ) if err != nil { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } } // Now we send the payment to the server. payChan, paymentErrChan, err := f.cfg.RouterClient.SendPayment( - f.ctx, + ctx, lndclient.SendPaymentRequest{ Invoice: f.InstantOut.swapInvoice, Timeout: defaultSendpaymentTimeout, @@ -232,7 +234,7 @@ func (f *FSM) PollPaymentAcceptedAction(_ fsm.EventContext) fsm.EventType { ) if err != nil { f.Errorf("error sending payment: %v", err) - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } // We'll continuously poll the server for the payment status. @@ -246,20 +248,20 @@ func (f *FSM) PollPaymentAcceptedAction(_ fsm.EventContext) fsm.EventType { f.Debugf("payment result: %v", payRes) if payRes.State == lnrpc.Payment_FAILED { return f.handleErrorAndUnlockReservations( - fmt.Errorf("payment failed: %v", + ctx, fmt.Errorf("payment failed: %v", payRes.FailureReason), ) } case err := <-paymentErrChan: f.Errorf("error sending payment: %v", err) - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) - case <-f.ctx.Done(): - return f.handleErrorAndUnlockReservations(nil) + case <-ctx.Done(): + return f.handleErrorAndUnlockReservations(ctx, nil) case <-timer.C: res, err := f.cfg.InstantOutClient.PollPaymentAccepted( - f.ctx, + ctx, &swapserverrpc.PollPaymentAcceptedRequest{ SwapHash: f.InstantOut.SwapHash[:], }, @@ -267,7 +269,7 @@ func (f *FSM) PollPaymentAcceptedAction(_ fsm.EventContext) fsm.EventType { if err != nil { pollPaymentTries++ if pollPaymentTries > 20 { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } } if res != nil && res.Accepted { @@ -280,74 +282,76 @@ func (f *FSM) PollPaymentAcceptedAction(_ fsm.EventContext) fsm.EventType { // BuildHTLCAction creates the htlc transaction, exchanges nonces with // the server and sends the htlc signatures to the server. -func (f *FSM) BuildHTLCAction(eventCtx fsm.EventContext) fsm.EventType { +func (f *FSM) BuildHTLCAction(ctx context.Context, + eventCtx fsm.EventContext) fsm.EventType { + htlcSessions, htlcClientNonces, err := f.InstantOut.createMusig2Session( - f.ctx, f.cfg.Signer, + ctx, f.cfg.Signer, ) if err != nil { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } f.htlcMusig2Sessions = htlcSessions // Send the server the client nonces. htlcInitRes, err := f.cfg.InstantOutClient.InitHtlcSig( - f.ctx, + ctx, &swapserverrpc.InitHtlcSigRequest{ SwapHash: f.InstantOut.SwapHash[:], HtlcClientNonces: htlcClientNonces, }, ) if err != nil { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } if len(htlcInitRes.HtlcServerNonces) != len(f.InstantOut.Reservations) { return f.handleErrorAndUnlockReservations( - errors.New("invalid number of server nonces"), + ctx, errors.New("invalid number of server nonces"), ) } htlcServerNonces, err := toNonces(htlcInitRes.HtlcServerNonces) if err != nil { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } // Now that our nonces are set, we can create and sign the htlc // transaction. htlcTx, err := f.InstantOut.createHtlcTransaction(f.cfg.Network) if err != nil { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } // Next we'll get our sweep tx signatures. htlcSigs, err := f.InstantOut.signMusig2Tx( - f.ctx, f.cfg.Signer, htlcTx, f.htlcMusig2Sessions, + ctx, f.cfg.Signer, htlcTx, f.htlcMusig2Sessions, htlcServerNonces, ) if err != nil { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } // Send the server the htlc signatures. htlcRes, err := f.cfg.InstantOutClient.PushHtlcSig( - f.ctx, + ctx, &swapserverrpc.PushHtlcSigRequest{ SwapHash: f.InstantOut.SwapHash[:], ClientSigs: htlcSigs, }, ) if err != nil { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } // We can now finalize the htlc transaction. htlcTx, err = f.InstantOut.finalizeMusig2Transaction( - f.ctx, f.cfg.Signer, f.htlcMusig2Sessions, htlcTx, + ctx, f.cfg.Signer, f.htlcMusig2Sessions, htlcTx, htlcRes.ServerSigs, ) if err != nil { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } f.InstantOut.finalizedHtlcTx = htlcTx @@ -359,25 +363,27 @@ func (f *FSM) BuildHTLCAction(eventCtx fsm.EventContext) fsm.EventType { // sweepless sweep transaction and sends the signatures to the server. Finally, // it publishes the sweepless sweep transaction. If any of the steps after // pushing the preimage fail, the htlc timeout transaction will be published. -func (f *FSM) PushPreimageAction(eventCtx fsm.EventContext) fsm.EventType { +func (f *FSM) PushPreimageAction(ctx context.Context, + eventCtx fsm.EventContext) fsm.EventType { + // First we'll create the musig2 context. coopSessions, coopClientNonces, err := f.InstantOut.createMusig2Session( - f.ctx, f.cfg.Signer, + ctx, f.cfg.Signer, ) if err != nil { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } f.sweeplessSweepSessions = coopSessions // Get the feerate for the coop sweep. - feeRate, err := f.cfg.Wallet.EstimateFeeRate(f.ctx, normalConfTarget) + feeRate, err := f.cfg.Wallet.EstimateFeeRate(ctx, normalConfTarget) if err != nil { - return f.handleErrorAndUnlockReservations(err) + return f.handleErrorAndUnlockReservations(ctx, err) } pushPreImageRes, err := f.cfg.InstantOutClient.PushPreimage( - f.ctx, + ctx, &swapserverrpc.PushPreimageRequest{ Preimage: f.InstantOut.swapPreimage[:], ClientNonces: coopClientNonces, @@ -408,7 +414,7 @@ func (f *FSM) PushPreimageAction(eventCtx fsm.EventContext) fsm.EventType { // Next we'll get our sweep tx signatures. _, err = f.InstantOut.signMusig2Tx( - f.ctx, f.cfg.Signer, sweepTx, f.sweeplessSweepSessions, + ctx, f.cfg.Signer, sweepTx, f.sweeplessSweepSessions, coopServerNonces, ) if err != nil { @@ -418,7 +424,7 @@ func (f *FSM) PushPreimageAction(eventCtx fsm.EventContext) fsm.EventType { // Now we'll finalize the sweepless sweep transaction. sweepTx, err = f.InstantOut.finalizeMusig2Transaction( - f.ctx, f.cfg.Signer, f.sweeplessSweepSessions, sweepTx, + ctx, f.cfg.Signer, f.sweeplessSweepSessions, sweepTx, pushPreImageRes.Musig2SweepSigs, ) if err != nil { @@ -430,7 +436,7 @@ func (f *FSM) PushPreimageAction(eventCtx fsm.EventContext) fsm.EventType { f.InstantOut.swapPreimage.Hash()) // Publish the sweepless sweep transaction. - err = f.cfg.Wallet.PublishTransaction(f.ctx, sweepTx, txLabel) + err = f.cfg.Wallet.PublishTransaction(ctx, sweepTx, txLabel) if err != nil { f.LastActionError = err return OnErrorPublishHtlc @@ -446,7 +452,7 @@ func (f *FSM) PushPreimageAction(eventCtx fsm.EventContext) fsm.EventType { // WaitForSweeplessSweepConfirmedAction waits for the sweepless sweep // transaction to be confirmed. -func (f *FSM) WaitForSweeplessSweepConfirmedAction( +func (f *FSM) WaitForSweeplessSweepConfirmedAction(ctx context.Context, eventCtx fsm.EventContext) fsm.EventType { pkscript, err := txscript.PayToAddrScript(f.InstantOut.sweepAddress) @@ -456,7 +462,7 @@ func (f *FSM) WaitForSweeplessSweepConfirmedAction( confChan, confErrChan, err := f.cfg.ChainNotifier. RegisterConfirmationsNtfn( - f.ctx, f.InstantOut.SweepTxHash, pkscript, + ctx, f.InstantOut.SweepTxHash, pkscript, 1, f.InstantOut.initiationHeight, ) if err != nil { @@ -483,11 +489,13 @@ func (f *FSM) WaitForSweeplessSweepConfirmedAction( // PublishHtlcAction publishes the htlc transaction and the htlc sweep // transaction. -func (f *FSM) PublishHtlcAction(eventCtx fsm.EventContext) fsm.EventType { +func (f *FSM) PublishHtlcAction(ctx context.Context, + eventCtx fsm.EventContext) fsm.EventType { + // Publish the htlc transaction. + label := fmt.Sprintf("htlc-%v", f.InstantOut.swapPreimage.Hash()) err := f.cfg.Wallet.PublishTransaction( - f.ctx, f.InstantOut.finalizedHtlcTx, - fmt.Sprintf("htlc-%v", f.InstantOut.swapPreimage.Hash()), + ctx, f.InstantOut.finalizedHtlcTx, label, ) if err != nil { return f.HandleError(err) @@ -499,7 +507,7 @@ func (f *FSM) PublishHtlcAction(eventCtx fsm.EventContext) fsm.EventType { // We'll now wait for the htlc to be confirmed. confChan, confErrChan, err := f.cfg.ChainNotifier. RegisterConfirmationsNtfn( - f.ctx, &txHash, + ctx, &txHash, f.InstantOut.finalizedHtlcTx.TxOut[0].PkScript, 1, f.InstantOut.initiationHeight, ) @@ -518,21 +526,23 @@ func (f *FSM) PublishHtlcAction(eventCtx fsm.EventContext) fsm.EventType { } // PublishHtlcSweepAction publishes the htlc sweep transaction. -func (f *FSM) PublishHtlcSweepAction(eventCtx fsm.EventContext) fsm.EventType { +func (f *FSM) PublishHtlcSweepAction(ctx context.Context, + eventCtx fsm.EventContext) fsm.EventType { + // Create a feerate that will confirm the htlc quickly. - feeRate, err := f.cfg.Wallet.EstimateFeeRate(f.ctx, urgentConfTarget) + feeRate, err := f.cfg.Wallet.EstimateFeeRate(ctx, urgentConfTarget) if err != nil { return f.HandleError(err) } - getInfo, err := f.cfg.LndClient.GetInfo(f.ctx) + getInfo, err := f.cfg.LndClient.GetInfo(ctx) if err != nil { return f.HandleError(err) } // We can immediately publish the htlc sweep transaction. htlcSweepTx, err := f.InstantOut.generateHtlcSweepTx( - f.ctx, f.cfg.Signer, feeRate, f.cfg.Network, getInfo.BlockHeight, + ctx, f.cfg.Signer, feeRate, f.cfg.Network, getInfo.BlockHeight, ) if err != nil { return f.HandleError(err) @@ -540,7 +550,7 @@ func (f *FSM) PublishHtlcSweepAction(eventCtx fsm.EventContext) fsm.EventType { label := fmt.Sprintf("htlc-sweep-%v", f.InstantOut.swapPreimage.Hash()) - err = f.cfg.Wallet.PublishTransaction(f.ctx, htlcSweepTx, label) + err = f.cfg.Wallet.PublishTransaction(ctx, htlcSweepTx, label) if err != nil { log.Errorf("error publishing htlc sweep tx: %v", err) return f.HandleError(err) @@ -555,7 +565,7 @@ func (f *FSM) PublishHtlcSweepAction(eventCtx fsm.EventContext) fsm.EventType { // WaitForHtlcSweepConfirmedAction waits for the htlc sweep transaction to be // confirmed. -func (f *FSM) WaitForHtlcSweepConfirmedAction( +func (f *FSM) WaitForHtlcSweepConfirmedAction(ctx context.Context, eventCtx fsm.EventContext) fsm.EventType { sweepPkScript, err := txscript.PayToAddrScript( @@ -566,7 +576,7 @@ func (f *FSM) WaitForHtlcSweepConfirmedAction( } confChan, confErrChan, err := f.cfg.ChainNotifier.RegisterConfirmationsNtfn( - f.ctx, f.InstantOut.SweepTxHash, sweepPkScript, + ctx, f.InstantOut.SweepTxHash, sweepPkScript, 1, f.InstantOut.initiationHeight, ) if err != nil { @@ -592,10 +602,11 @@ func (f *FSM) WaitForHtlcSweepConfirmedAction( // handleErrorAndUnlockReservations handles an error and unlocks the // reservations. -func (f *FSM) handleErrorAndUnlockReservations(err error) fsm.EventType { +func (f *FSM) handleErrorAndUnlockReservations(ctx context.Context, + err error) fsm.EventType { // We might get here from a canceled context, we create a new context // with a timeout to unlock the reservations. - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() // Unlock the reservations. @@ -613,7 +624,7 @@ func (f *FSM) handleErrorAndUnlockReservations(err error) fsm.EventType { // release the reservations. This can be done in a goroutine as we // wan't to fail the fsm early. go func() { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() _, cancelErr := f.cfg.InstantOutClient.CancelInstantSwap( ctx, &swapserverrpc.CancelInstantSwapRequest{ diff --git a/instantout/fsm.go b/instantout/fsm.go index 96c6d776..9cb0c994 100644 --- a/instantout/fsm.go +++ b/instantout/fsm.go @@ -176,8 +176,6 @@ type Config struct { type FSM struct { *fsm.StateMachine - ctx context.Context - // cfg contains all the services that the reservation manager needs to // operate. cfg *Config @@ -195,24 +193,19 @@ type FSM struct { } // NewFSM creates a new instant out FSM. -func NewFSM(ctx context.Context, cfg *Config, - protocolVersion ProtocolVersion) (*FSM, error) { - +func NewFSM(cfg *Config, protocolVersion ProtocolVersion) (*FSM, error) { instantOut := &InstantOut{ State: fsm.EmptyState, protocolVersion: protocolVersion, } - return NewFSMFromInstantOut(ctx, cfg, instantOut) + return NewFSMFromInstantOut(cfg, instantOut) } // NewFSMFromInstantOut creates a new instantout FSM from an existing instantout // recovered from the database. -func NewFSMFromInstantOut(ctx context.Context, cfg *Config, - instantOut *InstantOut) (*FSM, error) { - +func NewFSMFromInstantOut(cfg *Config, instantOut *InstantOut) (*FSM, error) { instantOutFSM := &FSM{ - ctx: ctx, cfg: cfg, InstantOut: instantOut, } @@ -328,7 +321,9 @@ func (f *FSM) GetV1ReservationStates() fsm.States { // updateInstantOut is called after every action and updates the reservation // in the db. -func (f *FSM) updateInstantOut(notification fsm.Notification) { +func (f *FSM) updateInstantOut(ctx context.Context, + notification fsm.Notification) { + f.Infof("Previous: %v, Event: %v, Next: %v", notification.PreviousState, notification.Event, notification.NextState) @@ -349,7 +344,7 @@ func (f *FSM) updateInstantOut(notification fsm.Notification) { return } - err := f.cfg.Store.UpdateInstantLoopOut(f.ctx, f.InstantOut) + err := f.cfg.Store.UpdateInstantLoopOut(ctx, f.InstantOut) if err != nil { log.Errorf("Error updating instant out: %v", err) return diff --git a/instantout/manager.go b/instantout/manager.go index d9d15fbc..38207c44 100644 --- a/instantout/manager.go +++ b/instantout/manager.go @@ -111,7 +111,7 @@ func (m *Manager) recoverInstantOuts(ctx context.Context) error { log.Debugf("Recovering instantout %v", instantOut.SwapHash) instantOutFSM, err := NewFSMFromInstantOut( - ctx, m.cfg, instantOut, + m.cfg, instantOut, ) if err != nil { return err @@ -122,7 +122,7 @@ func (m *Manager) recoverInstantOuts(ctx context.Context) error { // As SendEvent can block, we'll start a goroutine to process // the event. go func() { - err := instantOutFSM.SendEvent(OnRecover, nil) + err := instantOutFSM.SendEvent(ctx, OnRecover, nil) if err != nil { log.Errorf("FSM %v Error sending recover "+ "event %v, state: %v", @@ -162,9 +162,7 @@ func (m *Manager) NewInstantOut(ctx context.Context, sweepAddress: sweepAddr, } - instantOut, err := NewFSM( - m.runCtx, m.cfg, ProtocolVersionFullReservation, - ) + instantOut, err := NewFSM(m.cfg, ProtocolVersionFullReservation) if err != nil { m.Unlock() return nil, err @@ -174,7 +172,7 @@ func (m *Manager) NewInstantOut(ctx context.Context, // Start the instantout FSM. go func() { - err := instantOut.SendEvent(OnStart, request) + err := instantOut.SendEvent(m.runCtx, OnStart, request) if err != nil { log.Errorf("Error sending event: %v", err) }