Skip to content

Commit 7cafbe9

Browse files
committed
loopd: add instantout handling
1 parent 6c07f88 commit 7cafbe9

File tree

5 files changed

+130
-10
lines changed

5 files changed

+130
-10
lines changed

loopd/daemon.go

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
1717
"github.com/lightninglabs/lndclient"
1818
"github.com/lightninglabs/loop"
19+
"github.com/lightninglabs/loop/instantout"
1920
"github.com/lightninglabs/loop/loopd/perms"
2021
"github.com/lightninglabs/loop/loopdb"
2122
"github.com/lightninglabs/loop/sweepbatcher"
@@ -24,6 +25,7 @@ import (
2425
loop_looprpc "github.com/lightninglabs/loop/looprpc"
2526

2627
loop_swaprpc "github.com/lightninglabs/loop/swapserverrpc"
28+
"github.com/lightningnetwork/lnd/clock"
2729
"github.com/lightningnetwork/lnd/lntypes"
2830
"github.com/lightningnetwork/lnd/macaroons"
2931
"google.golang.org/grpc"
@@ -67,10 +69,6 @@ type Daemon struct {
6769
// same process.
6870
swapClientServer
6971

70-
// reservationManager is the manager that handles all reservation state
71-
// machines.
72-
reservationManager *reservation.Manager
73-
7472
// ErrChan is an error channel that users of the Daemon struct must use
7573
// to detect runtime errors and also whether a shutdown is fully
7674
// completed.
@@ -429,6 +427,11 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
429427
swapClient.Conn,
430428
)
431429

430+
// Create an instantout server client.
431+
instantOutClient := loop_swaprpc.NewInstantSwapServerClient(
432+
swapClient.Conn,
433+
)
434+
432435
// Both the client RPC server and the swap server client should stop
433436
// on main context cancel. So we create it early and pass it down.
434437
d.mainCtx, d.mainCtxCancel = context.WithCancel(context.Background())
@@ -486,7 +489,11 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
486489
}
487490
}
488491

489-
// Create the reservation rpc server.
492+
var (
493+
reservationManager *reservation.Manager
494+
instantOutManager *instantout.Manager
495+
)
496+
// Create the reservation and instantout managers.
490497
if d.cfg.EnableExperimental {
491498
reservationStore := reservation.NewSQLStore(baseDb)
492499
reservationConfig := &reservation.Config{
@@ -497,9 +504,30 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
497504
FetchL402: swapClient.Server.FetchL402,
498505
}
499506

500-
d.reservationManager = reservation.NewManager(
507+
reservationManager = reservation.NewManager(
501508
reservationConfig,
502509
)
510+
511+
// Create the instantout services.
512+
instantOutStore := instantout.NewSQLStore(
513+
baseDb, clock.NewDefaultClock(), reservationStore,
514+
d.lnd.ChainParams,
515+
)
516+
instantOutConfig := &instantout.Config{
517+
Store: instantOutStore,
518+
LndClient: d.lnd.Client,
519+
RouterClient: d.lnd.Router,
520+
ChainNotifier: d.lnd.ChainNotifier,
521+
Signer: d.lnd.Signer,
522+
Wallet: d.lnd.WalletKit,
523+
ReservationManager: reservationManager,
524+
InstantOutClient: instantOutClient,
525+
Network: d.lnd.ChainParams,
526+
}
527+
528+
instantOutManager = instantout.NewInstantOutManager(
529+
instantOutConfig,
530+
)
503531
}
504532

505533
// Now finally fully initialize the swap client RPC server instance.
@@ -513,7 +541,8 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
513541
subscribers: make(map[int]chan<- interface{}),
514542
statusChan: make(chan loop.SwapInfo),
515543
mainCtx: d.mainCtx,
516-
reservationManager: d.reservationManager,
544+
reservationManager: reservationManager,
545+
instantOutManager: instantOutManager,
517546
}
518547

519548
// Retrieve all currently existing swaps from the database.
@@ -606,6 +635,43 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
606635
}()
607636
}
608637

638+
// Start the instant out manager.
639+
if d.instantOutManager != nil {
640+
d.wg.Add(1)
641+
initChan := make(chan struct{})
642+
go func() {
643+
defer d.wg.Done()
644+
645+
getInfo, err := d.lnd.Client.GetInfo(d.mainCtx)
646+
if err != nil {
647+
d.internalErrChan <- err
648+
return
649+
}
650+
651+
log.Info("Starting instantout manager")
652+
defer log.Info("Instantout manager stopped")
653+
654+
err = d.instantOutManager.Run(
655+
d.mainCtx, initChan, int32(getInfo.BlockHeight),
656+
)
657+
if err != nil && !errors.Is(err, context.Canceled) {
658+
d.internalErrChan <- err
659+
}
660+
}()
661+
662+
// Wait for the instantout server to be ready before starting the
663+
// grpc server.
664+
timeOutCtx, cancel := context.WithTimeout(d.mainCtx, 10*time.Second)
665+
select {
666+
case <-timeOutCtx.Done():
667+
cancel()
668+
return fmt.Errorf("reservation server not ready: %v",
669+
timeOutCtx.Err())
670+
case <-initChan:
671+
cancel()
672+
}
673+
}
674+
609675
// Last, start our internal error handler. This will return exactly one
610676
// error or nil on the main error channel to inform the caller that
611677
// something went wrong or that shutdown is complete. We don't add to

loopd/log.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/lightninglabs/lndclient"
77
"github.com/lightninglabs/loop"
88
"github.com/lightninglabs/loop/fsm"
9+
"github.com/lightninglabs/loop/instantout"
910
"github.com/lightninglabs/loop/instantout/reservation"
1011
"github.com/lightninglabs/loop/liquidity"
1112
"github.com/lightninglabs/loop/loopdb"
@@ -44,6 +45,9 @@ func SetupLoggers(root *build.RotatingLogWriter, intercept signal.Interceptor) {
4445
lnd.AddSubLogger(
4546
root, reservation.Subsystem, intercept, reservation.UseLogger,
4647
)
48+
lnd.AddSubLogger(
49+
root, instantout.Subsystem, intercept, instantout.UseLogger,
50+
)
4751
}
4852

4953
// genSubLogger creates a logger for a subsystem. We provide an instance of

loopd/perms/perms.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,11 @@ var RequiredPermissions = map[string][]bakery.Op{
9797
Action: "in",
9898
}},
9999
"/looprpc.SwapClient/ListReservations": {{
100-
Entity: "reservation",
100+
Entity: "swap",
101101
Action: "read",
102102
}},
103+
"/looprpc.SwapClient/InstantOut": {{
104+
Entity: "swap",
105+
Action: "execute",
106+
}},
103107
}

loopd/swapclient_server.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/lightninglabs/aperture/lsat"
1919
"github.com/lightninglabs/lndclient"
2020
"github.com/lightninglabs/loop"
21+
"github.com/lightninglabs/loop/instantout"
2122
"github.com/lightninglabs/loop/instantout/reservation"
2223
"github.com/lightninglabs/loop/labels"
2324
"github.com/lightninglabs/loop/liquidity"
@@ -81,6 +82,7 @@ type swapClientServer struct {
8182
liquidityMgr *liquidity.Manager
8283
lnd *lndclient.LndServices
8384
reservationManager *reservation.Manager
85+
instantOutManager *instantout.Manager
8486
swaps map[lntypes.Hash]loop.SwapInfo
8587
subscribers map[int]chan<- interface{}
8688
statusChan chan loop.SwapInfo
@@ -1169,6 +1171,44 @@ func (s *swapClientServer) ListReservations(ctx context.Context,
11691171
}, nil
11701172
}
11711173

1174+
// InstantOut initiates an instant out swap.
1175+
func (s *swapClientServer) InstantOut(ctx context.Context,
1176+
req *clientrpc.InstantOutRequest) (*clientrpc.InstantOutResponse,
1177+
error) {
1178+
1179+
reservationIds := make([]reservation.ID, len(req.ReservationIds))
1180+
for i, id := range req.ReservationIds {
1181+
if len(id) != reservation.IdLength {
1182+
return nil, fmt.Errorf("invalid reservation id: "+
1183+
"expected %v bytes, got %d",
1184+
reservation.IdLength, len(id))
1185+
}
1186+
1187+
var resId reservation.ID
1188+
copy(resId[:], id)
1189+
1190+
reservationIds[i] = resId
1191+
}
1192+
1193+
instantOutFsm, err := s.instantOutManager.NewInstantOut(
1194+
ctx, reservationIds,
1195+
)
1196+
if err != nil {
1197+
return nil, err
1198+
}
1199+
1200+
res := &clientrpc.InstantOutResponse{
1201+
InstantOutHash: instantOutFsm.InstantOut.SwapHash[:],
1202+
State: string(instantOutFsm.InstantOut.State),
1203+
}
1204+
1205+
if instantOutFsm.InstantOut.SweepTxHash != nil {
1206+
res.SweepTxId = instantOutFsm.InstantOut.SweepTxHash.String()
1207+
}
1208+
1209+
return res, nil
1210+
}
1211+
11721212
func rpcAutoloopReason(reason liquidity.Reason) (clientrpc.AutoReason, error) {
11731213
switch reason {
11741214
case liquidity.ReasonNone:
@@ -1448,11 +1488,11 @@ func toClientReservation(
14481488
res *reservation.Reservation) *clientrpc.ClientReservation {
14491489

14501490
var (
1451-
txid []byte
1491+
txid string
14521492
vout uint32
14531493
)
14541494
if res.Outpoint != nil {
1455-
txid = res.Outpoint.Hash[:]
1495+
txid = res.Outpoint.Hash.String()
14561496
vout = res.Outpoint.Index
14571497
}
14581498

loopd/utils.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ func openDatabase(cfg *Config, chainParams *chaincfg.Params) (loopdb.SwapStore,
5959
db, err = loopdb.NewSqliteStore(
6060
cfg.Sqlite, chainParams,
6161
)
62+
if err != nil {
63+
return nil, nil, err
64+
}
6265
baseDb = *db.(*loopdb.SqliteSwapStore).BaseDB
6366

6467
case DatabaseBackendPostgres:
@@ -67,6 +70,9 @@ func openDatabase(cfg *Config, chainParams *chaincfg.Params) (loopdb.SwapStore,
6770
db, err = loopdb.NewPostgresStore(
6871
cfg.Postgres, chainParams,
6972
)
73+
if err != nil {
74+
return nil, nil, err
75+
}
7076
baseDb = *db.(*loopdb.PostgresStore).BaseDB
7177

7278
default:

0 commit comments

Comments
 (0)