Skip to content

Commit 3e00592

Browse files
committed
loopd: wire recovery service into daemon startup
Register a recovery gRPC endpoint, attach the new recovery package to the swap client server, and have daemon startup write an encrypted backup file whenever recoverable static-address or l402 state already exists locally.
1 parent 1585058 commit 3e00592

3 files changed

Lines changed: 94 additions & 0 deletions

File tree

loopd/daemon.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/lightninglabs/loop/loopdb"
2323
loop_looprpc "github.com/lightninglabs/loop/looprpc"
2424
"github.com/lightninglabs/loop/notifications"
25+
"github.com/lightninglabs/loop/recovery"
2526
"github.com/lightninglabs/loop/staticaddr/address"
2627
"github.com/lightninglabs/loop/staticaddr/deposit"
2728
"github.com/lightninglabs/loop/staticaddr/loopin"
@@ -255,6 +256,7 @@ func (d *Daemon) startWebServers() error {
255256
grpc.StreamInterceptor(streamInterceptor),
256257
)
257258
loop_looprpc.RegisterSwapClientServer(d.grpcServer, d)
259+
d.grpcServer.RegisterService(&recoveryServiceDesc, d)
258260

259261
// Register our debug server if it is compiled in.
260262
d.registerDebugServer()
@@ -689,6 +691,18 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
689691
return fmt.Errorf("unable to create loop-in manager: %w", err)
690692
}
691693

694+
recoveryService := recovery.NewService(
695+
d.cfg.DataDir, d.cfg.Network, d.lnd.Signer, d.lnd.WalletKit,
696+
staticAddressManager, depositManager,
697+
)
698+
backupFile, err := recoveryService.WriteBackup(d.mainCtx)
699+
if err != nil {
700+
return fmt.Errorf("unable to write backup file: %w", err)
701+
}
702+
if backupFile != "" {
703+
infof("Wrote encrypted backup file to %s", backupFile)
704+
}
705+
692706
var (
693707
reservationManager *reservation.Manager
694708
instantOutManager *instantout.Manager
@@ -753,6 +767,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
753767
staticLoopInManager: staticLoopInManager,
754768
openChannelManager: openChannelManager,
755769
assetClient: d.assetClient,
770+
recoveryService: recoveryService,
756771
stopDaemon: d.Stop,
757772
}
758773

loopd/recovery_service.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package loopd
2+
3+
import (
4+
"context"
5+
6+
"google.golang.org/grpc"
7+
"google.golang.org/protobuf/types/known/structpb"
8+
"google.golang.org/protobuf/types/known/wrapperspb"
9+
)
10+
11+
type recoveryServer interface {
12+
Recover(context.Context, *wrapperspb.StringValue) (*structpb.Struct,
13+
error)
14+
}
15+
16+
var recoveryServiceDesc = grpc.ServiceDesc{
17+
ServiceName: "looprpc.Recovery",
18+
HandlerType: (*recoveryServer)(nil),
19+
Methods: []grpc.MethodDesc{{
20+
MethodName: "Recover",
21+
Handler: recoveryHandler,
22+
}},
23+
Metadata: "loopd/recovery_service.go",
24+
}
25+
26+
func recoveryHandler(srv interface{}, ctx context.Context,
27+
dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (
28+
interface{}, error) {
29+
30+
in := new(wrapperspb.StringValue)
31+
if err := dec(in); err != nil {
32+
return nil, err
33+
}
34+
35+
if interceptor == nil {
36+
return srv.(recoveryServer).Recover(ctx, in)
37+
}
38+
39+
info := &grpc.UnaryServerInfo{
40+
Server: srv,
41+
FullMethod: "/looprpc.Recovery/Recover",
42+
}
43+
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
44+
return srv.(recoveryServer).Recover(
45+
ctx, req.(*wrapperspb.StringValue),
46+
)
47+
}
48+
49+
return interceptor(ctx, in, info, handler)
50+
}
51+
52+
// Recover restores the local L402 token material and static address state from
53+
// an encrypted backup file.
54+
func (s *swapClientServer) Recover(ctx context.Context,
55+
req *wrapperspb.StringValue) (*structpb.Struct, error) {
56+
57+
result, err := s.recoveryService.Restore(ctx, req.GetValue())
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
values := map[string]any{
63+
"backup_file": result.BackupFile,
64+
"restored_l402": result.RestoredL402,
65+
"restored_static_address": result.RestoredStaticAddress,
66+
"num_deposits_found": result.NumDepositsFound,
67+
}
68+
if result.StaticAddress != "" {
69+
values["static_address"] = result.StaticAddress
70+
}
71+
if result.DepositReconciliationError != "" {
72+
values["deposit_reconciliation_error"] =
73+
result.DepositReconciliationError
74+
}
75+
76+
return structpb.NewStruct(values)
77+
}

loopd/swapclient_server.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/lightninglabs/loop/liquidity"
3030
"github.com/lightninglabs/loop/loopdb"
3131
"github.com/lightninglabs/loop/looprpc"
32+
"github.com/lightninglabs/loop/recovery"
3233
"github.com/lightninglabs/loop/staticaddr/address"
3334
"github.com/lightninglabs/loop/staticaddr/deposit"
3435
"github.com/lightninglabs/loop/staticaddr/loopin"
@@ -101,6 +102,7 @@ type swapClientServer struct {
101102
staticLoopInManager *loopin.Manager
102103
openChannelManager *openchannel.Manager
103104
assetClient *assets.TapdClient
105+
recoveryService *recovery.Service
104106
swaps map[lntypes.Hash]loop.SwapInfo
105107
subscribers map[int]chan<- any
106108
statusChan chan loop.SwapInfo

0 commit comments

Comments
 (0)