Skip to content

Commit 6a078ff

Browse files
authored
Merge pull request #140 from guggero/monitor-rest
loopd+looprpc: add REST compatible status calls
2 parents 55908db + 2dc7d46 commit 6a078ff

File tree

11 files changed

+822
-151
lines changed

11 files changed

+822
-151
lines changed

cmd/loop/main.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ package main
22

33
import (
44
"bytes"
5-
"encoding/json"
65
"errors"
76
"fmt"
87
"os"
98
"strconv"
109
"time"
1110

12-
"github.com/golang/protobuf/jsonpb"
13-
"github.com/golang/protobuf/proto"
1411
"github.com/lightninglabs/loop"
1512
"github.com/lightninglabs/loop/looprpc"
1613
"github.com/lightninglabs/loop/swap"
14+
"github.com/lightninglabs/protobuf-hex-display/json"
15+
"github.com/lightninglabs/protobuf-hex-display/jsonpb"
16+
"github.com/lightninglabs/protobuf-hex-display/proto"
1717

1818
"github.com/btcsuite/btcutil"
1919

@@ -30,6 +30,10 @@ var (
3030
maxRoutingFeeRate = int64(20000)
3131

3232
defaultSwapWaitTime = 30 * time.Minute
33+
34+
// maxMsgRecvSize is the largest message our client will receive. We
35+
// set this to 200MiB atm.
36+
maxMsgRecvSize = grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 200)
3337
)
3438

3539
func printJSON(resp interface{}) {
@@ -49,6 +53,7 @@ func printJSON(resp interface{}) {
4953

5054
func printRespJSON(resp proto.Message) {
5155
jsonMarshaler := &jsonpb.Marshaler{
56+
OrigName: true,
5257
EmitDefaults: true,
5358
Indent: " ",
5459
}
@@ -83,6 +88,7 @@ func main() {
8388
app.Commands = []cli.Command{
8489
loopOutCommand, loopInCommand, termsCommand,
8590
monitorCommand, quoteCommand, listAuthCommand,
91+
listSwapsCommand, swapInfoCommand,
8692
}
8793

8894
err := app.Run(os.Args)
@@ -232,6 +238,7 @@ func logSwap(swap *looprpc.SwapStatus) {
232238
func getClientConn(address string) (*grpc.ClientConn, error) {
233239
opts := []grpc.DialOption{
234240
grpc.WithInsecure(),
241+
grpc.WithDefaultCallOptions(maxMsgRecvSize),
235242
}
236243

237244
conn, err := grpc.Dial(address, opts...)

cmd/loop/swaps.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/hex"
6+
"fmt"
7+
8+
"github.com/lightninglabs/loop/looprpc"
9+
"github.com/lightningnetwork/lnd/lntypes"
10+
"github.com/urfave/cli"
11+
)
12+
13+
var listSwapsCommand = cli.Command{
14+
Name: "listswaps",
15+
Usage: "list all swaps in the local database",
16+
Description: "Allows the user to get a list of all swaps that are " +
17+
"currently stored in the database",
18+
Action: listSwaps,
19+
}
20+
21+
func listSwaps(ctx *cli.Context) error {
22+
client, cleanup, err := getClient(ctx)
23+
if err != nil {
24+
return err
25+
}
26+
defer cleanup()
27+
28+
resp, err := client.ListSwaps(
29+
context.Background(), &looprpc.ListSwapsRequest{},
30+
)
31+
if err != nil {
32+
return err
33+
}
34+
35+
printRespJSON(resp)
36+
return nil
37+
}
38+
39+
var swapInfoCommand = cli.Command{
40+
Name: "swapinfo",
41+
Usage: "show the status of a swap",
42+
ArgsUsage: "id",
43+
Description: "Allows the user to get the status of a single swap " +
44+
"currently stored in the database",
45+
Flags: []cli.Flag{
46+
cli.Uint64Flag{
47+
Name: "id",
48+
Usage: "the ID of the swap",
49+
},
50+
},
51+
Action: swapInfo,
52+
}
53+
54+
func swapInfo(ctx *cli.Context) error {
55+
args := ctx.Args()
56+
57+
var id string
58+
switch {
59+
case ctx.IsSet("id"):
60+
id = ctx.String("id")
61+
case ctx.NArg() > 0:
62+
id = args[0]
63+
args = args.Tail()
64+
default:
65+
// Show command help if no arguments and flags were provided.
66+
return cli.ShowCommandHelp(ctx, "swapinfo")
67+
}
68+
69+
if len(id) != hex.EncodedLen(lntypes.HashSize) {
70+
return fmt.Errorf("invalid swap ID")
71+
}
72+
idBytes, err := hex.DecodeString(id)
73+
if err != nil {
74+
return fmt.Errorf("cannot hex decode id: %v", err)
75+
}
76+
77+
client, cleanup, err := getClient(ctx)
78+
if err != nil {
79+
return err
80+
}
81+
defer cleanup()
82+
83+
resp, err := client.SwapInfo(
84+
context.Background(), &looprpc.SwapInfoRequest{Id: idBytes},
85+
)
86+
if err != nil {
87+
return err
88+
}
89+
90+
printRespJSON(resp)
91+
return nil
92+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/google/go-cmp v0.3.1 // indirect
1111
github.com/grpc-ecosystem/grpc-gateway v1.10.0
1212
github.com/jessevdk/go-flags v1.4.0
13+
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d
1314
github.com/lightningnetwork/lnd v0.9.0-beta-rc3.0.20200121213302-a2977c4438b5
1415
github.com/lightningnetwork/lnd/queue v1.0.2
1516
github.com/urfave/cli v1.20.0

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI
136136
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
137137
github.com/lightninglabs/neutrino v0.11.0 h1:lPpYFCtsfJX2W5zI4pWycPmbbBdr7zU+BafYdLoD6k0=
138138
github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg=
139+
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d h1:QWD/5MPnaZfUVP7P8wLa4M8Td2DI7XXHXt2vhVtUgGI=
139140
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI=
140141
github.com/lightningnetwork/lightning-onion v1.0.1 h1:qChGgS5+aPxFeR6JiUsGvanei1bn6WJpYbvosw/1604=
141142
github.com/lightningnetwork/lightning-onion v1.0.1/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4=

loopd/daemon.go

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,16 @@ import (
1616
"github.com/lightninglabs/loop"
1717
"github.com/lightninglabs/loop/lndclient"
1818
"github.com/lightninglabs/loop/looprpc"
19+
"github.com/lightningnetwork/lnd/lntypes"
1920
"google.golang.org/grpc"
2021
)
2122

23+
var (
24+
// maxMsgRecvSize is the largest message our REST proxy will receive. We
25+
// set this to 200MiB atm.
26+
maxMsgRecvSize = grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 200)
27+
)
28+
2229
// listenerCfg holds closures used to retrieve listeners for the gRPC services.
2330
type listenerCfg struct {
2431
// grpcListener returns a listener to use for the gRPC server.
@@ -68,14 +75,18 @@ func daemon(config *config, lisCfg *listenerCfg) error {
6875
return err
6976
}
7077

78+
swaps := make(map[lntypes.Hash]loop.SwapInfo)
7179
for _, s := range swapsList {
7280
swaps[s.SwapHash] = *s
7381
}
7482

7583
// Instantiate the loopd gRPC server.
7684
server := swapClientServer{
77-
impl: swapClient,
78-
lnd: &lnd.LndServices,
85+
impl: swapClient,
86+
lnd: &lnd.LndServices,
87+
swaps: swaps,
88+
subscribers: make(map[int]chan<- interface{}),
89+
statusChan: make(chan loop.SwapInfo),
7990
}
8091

8192
serverOpts := []grpc.ServerOption{}
@@ -92,12 +103,26 @@ func daemon(config *config, lisCfg *listenerCfg) error {
92103
}
93104
defer grpcListener.Close()
94105

106+
// The default JSON marshaler of the REST proxy only sets OrigName to
107+
// true, which instructs it to use the same field names as specified in
108+
// the proto file and not switch to camel case. What we also want is
109+
// that the marshaler prints all values, even if they are falsey.
110+
customMarshalerOption := proxy.WithMarshalerOption(
111+
proxy.MIMEWildcard, &proxy.JSONPb{
112+
OrigName: true,
113+
EmitDefaults: true,
114+
},
115+
)
116+
95117
// We'll also create and start an accompanying proxy to serve clients
96118
// through REST.
97119
ctx, cancel := context.WithCancel(context.Background())
98120
defer cancel()
99-
mux := proxy.NewServeMux()
100-
proxyOpts := []grpc.DialOption{grpc.WithInsecure()}
121+
mux := proxy.NewServeMux(customMarshalerOption)
122+
proxyOpts := []grpc.DialOption{
123+
grpc.WithInsecure(),
124+
grpc.WithDefaultCallOptions(maxMsgRecvSize),
125+
}
101126
err = looprpc.RegisterSwapClientHandlerFromEndpoint(
102127
ctx, mux, config.RPCListen, proxyOpts,
103128
)
@@ -130,8 +155,6 @@ func daemon(config *config, lisCfg *listenerCfg) error {
130155
log.Infof("REST proxy disabled")
131156
}
132157

133-
statusChan := make(chan loop.SwapInfo)
134-
135158
mainCtx, cancel := context.WithCancel(context.Background())
136159
var wg sync.WaitGroup
137160

@@ -141,7 +164,7 @@ func daemon(config *config, lisCfg *listenerCfg) error {
141164
defer wg.Done()
142165

143166
log.Infof("Starting swap client")
144-
err := swapClient.Run(mainCtx, statusChan)
167+
err := swapClient.Run(mainCtx, server.statusChan)
145168
if err != nil {
146169
log.Error(err)
147170
}
@@ -159,25 +182,7 @@ func daemon(config *config, lisCfg *listenerCfg) error {
159182
defer wg.Done()
160183

161184
log.Infof("Waiting for updates")
162-
for {
163-
select {
164-
case swap := <-statusChan:
165-
swapsLock.Lock()
166-
swaps[swap.SwapHash] = swap
167-
168-
for _, subscriber := range subscribers {
169-
select {
170-
case subscriber <- swap:
171-
case <-mainCtx.Done():
172-
return
173-
}
174-
}
175-
176-
swapsLock.Unlock()
177-
case <-mainCtx.Done():
178-
return
179-
}
180-
}
185+
server.processStatusUpdates(mainCtx)
181186
}()
182187

183188
// Start the grpc server.

loopd/start.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,16 @@ import (
77
"os"
88
"path/filepath"
99
"strings"
10-
"sync"
1110

1211
"github.com/jessevdk/go-flags"
1312
"github.com/lightninglabs/loop"
1413
"github.com/lightninglabs/loop/lndclient"
1514
"github.com/lightningnetwork/lnd/build"
16-
"github.com/lightningnetwork/lnd/lntypes"
1715
)
1816

1917
const (
20-
defaultConfTarget = int32(6)
21-
)
22-
23-
var (
18+
defaultConfTarget = int32(6)
2419
defaultConfigFilename = "loopd.conf"
25-
26-
swaps = make(map[lntypes.Hash]loop.SwapInfo)
27-
subscribers = make(map[int]chan<- interface{})
28-
nextSubscriberID int
29-
swapsLock sync.Mutex
3020
)
3121

3222
// RPCConfig holds optional options that can be used to make the loop daemon

0 commit comments

Comments
 (0)