forked from lightninglabs/lseed
-
Notifications
You must be signed in to change notification settings - Fork 1
/
lseed.go
235 lines (186 loc) · 6.51 KB
/
lseed.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
package main
import (
"context"
"flag"
"fmt"
"io/ioutil"
"net"
"net/http"
_ "net/http/pprof"
"os"
"path/filepath"
"strings"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
macaroon "gopkg.in/macaroon.v2"
log "github.com/Sirupsen/logrus"
"github.com/ltcsuite/lnd/lnrpc"
"github.com/ltcsuite/lnd/macaroons"
"github.com/ltcsuite/ltcutil"
"github.com/roasbeef/lseed/seed"
)
var (
listenAddr = flag.String("listen", "0.0.0.0:53", "Listen address for incoming requests.")
bitcoinNodeHost = flag.String("btc-lnd-node", "", "The host:port of the backing btc lnd node")
litecoinNodeHost = flag.String("ltc-lnd-node", "", "The host:port of the backing ltc lnd node")
testNodeHost = flag.String("test-lnd-node", "", "The host:port of the backing btc testlnd node")
bitcoinTLSPath = flag.String("btc-tls-path", "", "The path to the TLS cert for the btc lnd node")
litecoinTLSPath = flag.String("ltc-tls-path", "", "The path to the TLS cert for the ltc lnd node")
testTLSPath = flag.String("test-tls-path", "", "The path to the TLS cert for the test lnd node")
bitcoinMacPath = flag.String("btc-mac-path", "", "The path to the macaroon for the btc lnd node")
litecoinMacPath = flag.String("ltc-mac-path", "", "The path to the macaroon for the ltc lnd node")
testMacPath = flag.String("test-mac-path", "", "The path to the macaroon for the test lnd node")
rootDomain = flag.String("root-domain", "nodes.lightning.directory", "Root DNS seed domain.")
authoritativeIP = flag.String("root-ip", "127.0.0.1", "The IP address of the authoritative name server. This is used to create a dummy record which allows clients to access the seed directly over TCP")
pollInterval = flag.Int("poll-interval", 600, "Time between polls to lightningd for updates")
debug = flag.Bool("debug", false, "Be very verbose")
numResults = flag.Int("results", 25, "How many results shall we return to a query?")
)
var (
lndHomeDir = ltcutil.AppDataDir("lnd", false)
maxMsgRecvSize = grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 50)
)
// cleanAndExpandPath expands environment variables and leading ~ in the passed
// path, cleans the result, and returns it.
// This function is taken from https://github.com/btcsuite/btcd
func cleanAndExpandPath(path string) string {
// Expand initial ~ to OS specific home directory.
if strings.HasPrefix(path, "~") {
homeDir := filepath.Dir(lndHomeDir)
path = strings.Replace(path, "~", homeDir, 1)
}
// NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
// but the variables can still be expanded via POSIX-style $VARIABLE.
return filepath.Clean(os.ExpandEnv(path))
}
// initLightningClient attempts to initialize, and connect out to the backing
// lnd node as specified by the lndNode ccommand line flag.
func initLightningClient(nodeHost, tlsCertPath, macPath string) (lnrpc.LightningClient, error) {
// First attempt to establish a connection to lnd's RPC sever.
tlsCertPath = cleanAndExpandPath(tlsCertPath)
creds, err := credentials.NewClientTLSFromFile(tlsCertPath, "")
if err != nil {
return nil, fmt.Errorf("unable to read cert file: %v", err)
}
opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}
// Load the specified macaroon file.
macPath = cleanAndExpandPath(macPath)
macBytes, err := ioutil.ReadFile(macPath)
if err != nil {
return nil, err
}
mac := &macaroon.Macaroon{}
if err = mac.UnmarshalBinary(macBytes); err != nil {
return nil, err
}
// Now we append the macaroon credentials to the dial options.
opts = append(
opts,
grpc.WithPerRPCCredentials(macaroons.NewMacaroonCredential(mac)),
)
opts = append(opts, grpc.WithDefaultCallOptions(maxMsgRecvSize))
conn, err := grpc.Dial(nodeHost, opts...)
if err != nil {
return nil, fmt.Errorf("unable to dial to lnd's gRPC server: ",
err)
}
// If we're able to connect out to the lnd node, then we can start up
// our RPC connection properly.
lnd := lnrpc.NewLightningClient(conn)
// Before we proceed, make sure that we can query the target node.
_, err = lnd.GetInfo(
context.Background(), &lnrpc.GetInfoRequest{},
)
if err != nil {
return nil, err
}
return lnd, nil
}
// poller regularly polls the backing lnd node and updates the local network
// view.
func poller(lnd lnrpc.LightningClient, nview *seed.NetworkView) {
scrapeGraph := func() {
graphReq := &lnrpc.ChannelGraphRequest{}
graph, err := lnd.DescribeGraph(
context.Background(), graphReq,
)
if err != nil {
return
}
log.Debugf("Got %d nodes from lnd", len(graph.Nodes))
for _, node := range graph.Nodes {
if len(node.Addresses) == 0 {
continue
}
if _, err := nview.AddNode(node); err != nil {
log.Debugf("Unable to add node: %v", err)
}
}
}
scrapeGraph()
ticker := time.NewTicker(time.Second * time.Duration(*pollInterval))
for range ticker.C {
scrapeGraph()
}
}
// Parse flags and configure subsystems according to flags
func configure() {
flag.Parse()
if *debug {
log.SetLevel(log.DebugLevel)
} else {
log.SetLevel(log.InfoLevel)
}
}
// Main entry point for the lightning-seed
func main() {
log.SetOutput(os.Stdout)
configure()
go func() {
log.Println(http.ListenAndServe(":9091", nil))
}()
netViewMap := make(map[string]*seed.ChainView)
if *bitcoinNodeHost != "" && *bitcoinTLSPath != "" && *bitcoinMacPath != "" {
panic(fmt.Sprintf("this release of lseed does NOT support bitcoin"))
}
if *litecoinNodeHost != "" && *litecoinTLSPath != "" && *litecoinMacPath != "" {
log.Infof("Creating LTC chain view")
lndNode, err := initLightningClient(
*litecoinNodeHost, *litecoinTLSPath, *litecoinMacPath,
)
if err != nil {
panic(fmt.Sprintf("unable to connect to ltc lnd: %v", err))
}
nView := seed.NewNetworkView("litecoin")
go poller(lndNode, nView)
netViewMap[""] = &seed.ChainView{
NetView: nView,
Node: lndNode,
}
}
if *testNodeHost != "" && *testTLSPath != "" && *testMacPath != "" {
log.Infof("Creating LTC testnet chain view")
lndNode, err := initLightningClient(
*testNodeHost, *testTLSPath, *testMacPath,
)
if err != nil {
panic(fmt.Sprintf("unable to connect to test lnd: %v", err))
}
nView := seed.NewNetworkView("testnet")
go poller(lndNode, nView)
log.Infof("TLTC chain view active")
netViewMap["test."] = &seed.ChainView{
NetView: nView,
Node: lndNode,
}
}
if len(netViewMap) == 0 {
panic(fmt.Sprintf("must specify at least one node type"))
}
rootIP := net.ParseIP(*authoritativeIP)
dnsServer := seed.NewDnsServer(
netViewMap, *listenAddr, *rootDomain, rootIP,
)
dnsServer.Serve()
}