diff --git a/loopd/daemon.go b/loopd/daemon.go index 0a31e7fabc..6280a2da8f 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -23,6 +23,7 @@ import ( loop_looprpc "github.com/lightninglabs/loop/looprpc" "github.com/lightninglabs/loop/staticaddr/address" "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/loopin" "github.com/lightninglabs/loop/staticaddr/withdraw" loop_swaprpc "github.com/lightninglabs/loop/swapserverrpc" "github.com/lightninglabs/loop/sweepbatcher" @@ -513,6 +514,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { staticAddressManager *address.Manager depositManager *deposit.Manager withdrawalManager *withdraw.Manager + staticLoopInManager *loopin.Manager ) // Create the reservation and instantout managers. if d.cfg.EnableExperimental { @@ -589,6 +591,28 @@ func (d *Daemon) initialize(withMacaroonService bool) error { Signer: d.lnd.Signer, } withdrawalManager = withdraw.NewManager(withdrawalCfg) + + // Static address loop-in manager setup. + staticAddressLoopInStore := loopin.NewSqlStore( + loopdb.NewTypedStore[loopin.Querier](baseDb), + clock.NewDefaultClock(), d.lnd.ChainParams, + ) + + loopinCfg := &loopin.Config{ + StaticAddressServerClient: staticAddressClient, + SwapClient: swapClient, + LndClient: d.lnd.Client, + InvoicesClient: d.lnd.Invoices, + NodePubkey: d.lnd.NodePubkey, + AddressManager: staticAddressManager, + DepositManager: depositManager, + Store: staticAddressLoopInStore, + WalletKit: d.lnd.WalletKit, + ChainNotifier: d.lnd.ChainNotifier, + ChainParams: d.lnd.ChainParams, + Signer: d.lnd.Signer, + } + staticLoopInManager = loopin.NewManager(loopinCfg) } // Now finally fully initialize the swap client RPC server instance. @@ -607,6 +631,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { staticAddressManager: staticAddressManager, depositManager: depositManager, withdrawalManager: withdrawalManager, + staticLoopInManager: staticLoopInManager, } // Retrieve all currently existing swaps from the database. @@ -764,6 +789,33 @@ func (d *Daemon) initialize(withMacaroonService bool) error { withdrawalManager.WaitInitComplete() } + // Start the static address loop-in manager. + if staticLoopInManager != nil { + d.wg.Add(1) + go func() { + defer d.wg.Done() + + // Lnd's GetInfo call supplies us with the current block + // height. + info, err := d.lnd.Client.GetInfo(d.mainCtx) + if err != nil { + d.internalErrChan <- err + return + } + + log.Info("Starting static address loop-in manager...") + err = staticLoopInManager.Run( + d.mainCtx, info.BlockHeight, + ) + if err != nil && !errors.Is(context.Canceled, err) { + d.internalErrChan <- err + } + log.Info("Starting static address loop-in manager " + + "stopped") + }() + staticLoopInManager.WaitInitComplete() + } + // Last, start our internal error handler. This will return exactly one // error or nil on the main error channel to inform the caller that // something went wrong or that shutdown is complete. We don't add to diff --git a/loopd/perms/perms.go b/loopd/perms/perms.go index e04762f8cb..663fd2e722 100644 --- a/loopd/perms/perms.go +++ b/loopd/perms/perms.go @@ -101,6 +101,13 @@ var RequiredPermissions = map[string][]bakery.Op{ Entity: "loop", Action: "in", }}, + "/looprpc.SwapClient/StaticAddressLoopIn": {{ + Entity: "swap", + Action: "read", + }, { + Entity: "loop", + Action: "in", + }}, "/looprpc.SwapClient/GetLsatTokens": {{ Entity: "auth", Action: "read", diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index 7d511969a6..d071b28c74 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -28,6 +28,7 @@ import ( clientrpc "github.com/lightninglabs/loop/looprpc" "github.com/lightninglabs/loop/staticaddr/address" "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/loopin" "github.com/lightninglabs/loop/staticaddr/withdraw" "github.com/lightninglabs/loop/swap" looprpc "github.com/lightninglabs/loop/swapserverrpc" @@ -91,6 +92,7 @@ type swapClientServer struct { staticAddressManager *address.Manager depositManager *deposit.Manager withdrawalManager *withdraw.Manager + staticLoopInManager *loopin.Manager swaps map[lntypes.Hash]loop.SwapInfo subscribers map[int]chan<- interface{} statusChan chan loop.SwapInfo @@ -1461,6 +1463,45 @@ func (s *swapClientServer) GetStaticAddressSummary(ctx context.Context, ) } +// StaticAddressLoopIn initiates a loop-in request using static address +// deposits. +func (s *swapClientServer) StaticAddressLoopIn(_ context.Context, + in *clientrpc.StaticAddressLoopInRequest) ( + *clientrpc.StaticAddressLoopInResponse, error) { + + log.Infof("Static loop-in request received") + + routeHints, err := unmarshallRouteHints(in.RouteHints) + if err != nil { + return nil, err + } + + var lastHop route.Vertex + if in.LastHop != nil { + lastHop, err = route.NewVertexFromBytes(in.LastHop) + if err != nil { + return nil, err + } + } + + req := &loop.StaticAddressLoopInRequest{ + DepositOutpoints: in.Outpoints, + MaxSwapFee: btcutil.Amount(in.MaxSwapFee), + LastHop: &lastHop, + Label: in.Label, + Initiator: in.Initiator, + Private: in.Private, + RouteHints: routeHints, + } + + err = s.staticLoopInManager.InitiateLoopIn(req) + if err != nil { + return nil, err + } + + return &clientrpc.StaticAddressLoopInResponse{}, nil +} + func (s *swapClientServer) depositSummary(ctx context.Context, deposits []*deposit.Deposit, stateFilter clientrpc.DepositState, outpointsFilter []string) (*clientrpc.StaticAddressSummaryResponse, @@ -1472,6 +1513,8 @@ func (s *swapClientServer) depositSummary(ctx context.Context, valueDeposited int64 valueExpired int64 valueWithdrawn int64 + valueLoopedIn int64 + htlcTimeoutSwept int64 ) // Value unconfirmed. @@ -1497,6 +1540,12 @@ func (s *swapClientServer) depositSummary(ctx context.Context, case deposit.Withdrawn: valueWithdrawn += value + + case deposit.LoopedIn: + valueLoopedIn += value + + case deposit.HtlcTimeoutSwept: + htlcTimeoutSwept += value } } @@ -1525,7 +1574,7 @@ func (s *swapClientServer) depositSummary(ctx context.Context, return true } - return d.GetState() == toServerState(stateFilter) + return d.IsInState(toServerState(stateFilter)) } clientDeposits = filter(deposits, f) } @@ -1543,13 +1592,15 @@ func (s *swapClientServer) depositSummary(ctx context.Context, } return &clientrpc.StaticAddressSummaryResponse{ - StaticAddress: address.String(), - TotalNumDeposits: uint32(totalNumDeposits), - ValueUnconfirmed: valueUnconfirmed, - ValueDeposited: valueDeposited, - ValueExpired: valueExpired, - ValueWithdrawn: valueWithdrawn, - FilteredDeposits: clientDeposits, + StaticAddress: address.String(), + TotalNumDeposits: uint32(totalNumDeposits), + ValueUnconfirmed: valueUnconfirmed, + ValueDeposited: valueDeposited, + ValueExpired: valueExpired, + ValueWithdrawn: valueWithdrawn, + ValueLoopedIn: valueLoopedIn, + ValueHtlcTimeoutSweeps: htlcTimeoutSwept, + FilteredDeposits: clientDeposits, }, nil } @@ -1592,6 +1643,18 @@ func toClientState(state fsm.StateType) clientrpc.DepositState { case deposit.PublishExpiredDeposit: return clientrpc.DepositState_PUBLISH_EXPIRED + case deposit.LoopingIn: + return clientrpc.DepositState_LOOPING_IN + + case deposit.LoopedIn: + return clientrpc.DepositState_LOOPED_IN + + case deposit.SweepHtlcTimout: + return clientrpc.DepositState_SWEEP_HTLC_TIMEOUT + + case deposit.HtlcTimeoutSwept: + return clientrpc.DepositState_HTLC_TIMEOUT_SWEPT + case deposit.WaitForExpirySweep: return clientrpc.DepositState_WAIT_FOR_EXPIRY_SWEEP @@ -1617,6 +1680,18 @@ func toServerState(state clientrpc.DepositState) fsm.StateType { case clientrpc.DepositState_WITHDRAWN: return deposit.Withdrawn + case clientrpc.DepositState_LOOPING_IN: + return deposit.LoopingIn + + case clientrpc.DepositState_LOOPED_IN: + return deposit.LoopedIn + + case clientrpc.DepositState_SWEEP_HTLC_TIMEOUT: + return deposit.SweepHtlcTimout + + case clientrpc.DepositState_HTLC_TIMEOUT_SWEPT: + return deposit.HtlcTimeoutSwept + case clientrpc.DepositState_PUBLISH_EXPIRED: return deposit.PublishExpiredDeposit