diff --git a/loopd/daemon.go b/loopd/daemon.go index 50405613..96af9d90 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -24,6 +24,7 @@ import ( "github.com/lightninglabs/loop/notifications" "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" @@ -541,6 +542,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. @@ -618,6 +620,30 @@ 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, + ) + + staticLoopInManager = loopin.NewManager(&loopin.Config{ + Server: staticAddressClient, + QuoteGetter: swapClient.Server, + 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, + ValidateLoopInContract: loop.ValidateLoopInContract, + MaxStaticAddrHtlcFeePercentage: d.cfg.MaxStaticAddrHtlcFeePercentage, + MaxStaticAddrHtlcBackupFeePercentage: d.cfg.MaxStaticAddrHtlcBackupFeePercentage, + }) } // Now finally fully initialize the swap client RPC server instance. @@ -636,6 +662,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { staticAddressManager: staticAddressManager, depositManager: depositManager, withdrawalManager: withdrawalManager, + staticLoopInManager: staticLoopInManager, } // Retrieve all currently existing swaps from the database. @@ -845,6 +872,34 @@ 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 98ffb592..d47257e8 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 8982dd8e..88500ef5 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -28,6 +28,7 @@ import ( "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" "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 @@ -1481,6 +1483,57 @@ func (s *swapClientServer) GetStaticAddressSummary(ctx context.Context, ) } +// StaticAddressLoopIn initiates a loop-in request using static address +// deposits. +func (s *swapClientServer) StaticAddressLoopIn(ctx context.Context, + in *looprpc.StaticAddressLoopInRequest) ( + *looprpc.StaticAddressLoopInResponse, error) { + + log.Infof("Static loop-in request received") + + routeHints, err := unmarshallRouteHints(in.RouteHints) + if err != nil { + return nil, err + } + + req := &loop.StaticAddressLoopInRequest{ + DepositOutpoints: in.Outpoints, + MaxSwapFee: btcutil.Amount(in.MaxSwapFeeSatoshis), + Label: in.Label, + Initiator: in.Initiator, + Private: in.Private, + RouteHints: routeHints, + PaymentTimeoutSeconds: in.PaymentTimeoutSeconds, + } + + if in.LastHop != nil { + lastHop, err := route.NewVertexFromBytes(in.LastHop) + if err != nil { + return nil, err + } + req.LastHop = &lastHop + } + + loopIn, err := s.staticLoopInManager.DeliverLoopInRequest(ctx, req) + if err != nil { + return nil, err + } + + return &looprpc.StaticAddressLoopInResponse{ + SwapHash: loopIn.SwapHash[:], + State: string(loopIn.GetState()), + Amount: uint64(loopIn.TotalDepositAmount()), + HtlcCltv: loopIn.HtlcCltvExpiry, + MaxSwapFeeSatoshis: int64(loopIn.MaxSwapFee), + InitiationHeight: loopIn.InitiationHeight, + ProtocolVersion: loopIn.ProtocolVersion.String(), + Initiator: loopIn.Initiator, + Label: loopIn.Label, + PaymentTimeoutSeconds: loopIn.PaymentTimeoutSeconds, + QuotedSwapFeeSatoshis: int64(loopIn.QuotedSwapFee), + }, nil +} + func (s *swapClientServer) depositSummary(ctx context.Context, deposits []*deposit.Deposit, stateFilter looprpc.DepositState, outpointsFilter []string) (*looprpc.StaticAddressSummaryResponse, @@ -1492,6 +1545,8 @@ func (s *swapClientServer) depositSummary(ctx context.Context, valueDeposited int64 valueExpired int64 valueWithdrawn int64 + valueLoopedIn int64 + htlcTimeoutSwept int64 ) // Value unconfirmed. @@ -1517,6 +1572,12 @@ func (s *swapClientServer) depositSummary(ctx context.Context, case deposit.Withdrawn: valueWithdrawn += value + + case deposit.LoopedIn: + valueLoopedIn += value + + case deposit.HtlcTimeoutSwept: + htlcTimeoutSwept += value } } @@ -1545,7 +1606,7 @@ func (s *swapClientServer) depositSummary(ctx context.Context, return true } - return d.GetState() == toServerState(stateFilter) + return d.IsInState(toServerState(stateFilter)) } clientDeposits = filter(deposits, f) } @@ -1563,13 +1624,15 @@ func (s *swapClientServer) depositSummary(ctx context.Context, } return &looprpc.StaticAddressSummaryResponse{ - StaticAddress: address.String(), - TotalNumDeposits: uint32(totalNumDeposits), - ValueUnconfirmedSatoshis: valueUnconfirmed, - ValueDepositedSatoshis: valueDeposited, - ValueExpiredSatoshis: valueExpired, - ValueWithdrawnSatoshis: valueWithdrawn, - FilteredDeposits: clientDeposits, + StaticAddress: address.String(), + TotalNumDeposits: uint32(totalNumDeposits), + ValueUnconfirmedSatoshis: valueUnconfirmed, + ValueDepositedSatoshis: valueDeposited, + ValueExpiredSatoshis: valueExpired, + ValueWithdrawnSatoshis: valueWithdrawn, + ValueLoopedInSatoshis: valueLoopedIn, + ValueHtlcTimeoutSweepsSatoshis: htlcTimeoutSwept, + FilteredDeposits: clientDeposits, }, nil } @@ -1612,6 +1675,18 @@ func toClientState(state fsm.StateType) looprpc.DepositState { case deposit.PublishExpirySweep: return looprpc.DepositState_PUBLISH_EXPIRED + case deposit.LoopingIn: + return looprpc.DepositState_LOOPING_IN + + case deposit.LoopedIn: + return looprpc.DepositState_LOOPED_IN + + case deposit.SweepHtlcTimeout: + return looprpc.DepositState_SWEEP_HTLC_TIMEOUT + + case deposit.HtlcTimeoutSwept: + return looprpc.DepositState_HTLC_TIMEOUT_SWEPT + case deposit.WaitForExpirySweep: return looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP @@ -1637,6 +1712,18 @@ func toServerState(state looprpc.DepositState) fsm.StateType { case looprpc.DepositState_PUBLISH_EXPIRED: return deposit.PublishExpirySweep + case looprpc.DepositState_LOOPING_IN: + return deposit.LoopingIn + + case looprpc.DepositState_LOOPED_IN: + return deposit.LoopedIn + + case looprpc.DepositState_SWEEP_HTLC_TIMEOUT: + return deposit.SweepHtlcTimeout + + case looprpc.DepositState_HTLC_TIMEOUT_SWEPT: + return deposit.HtlcTimeoutSwept + case looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP: return deposit.WaitForExpirySweep