Skip to content

Commit 6a395e2

Browse files
committed
Merge branch 'lnd-14-2'
2 parents fa725f7 + 6fb8aee commit 6a395e2

22 files changed

+2696
-408
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@
1111
# Output of the go coverage tool, specifically when used with LiteIDE
1212
*.out
1313

14+
# Vim files
15+
*.swp
16+
1417
# Dependency directories (remove the comment below to include it)
1518
# vendor/

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ The current compatibility matrix reads as follows:
2626

2727
| `lndclient` git tag | `lnd` version in `go.mod` | minimum required `lnd` version |
2828
| ---------------------------- | ------------------------- | ------------------------------ |
29-
| [`v0.12.0-0`](https://github.com/lightninglabs/lndclient/blob/v0.12.0-0) | `1efef51268d6` | `v0.11.1-beta` |
30-
| `master` / [`v0.11.1-5`](https://github.com/lightninglabs/lndclient/blob/v0.11.1-5) | `v0.11.1-beta` | `v0.11.1-beta` |
29+
| [`v0.14.2-2`](https://github.com/lightninglabs/lndclient/blob/v0.14.2-2) | `v0.14.2-beta` | `v0.14.2-beta` |
30+
| [`v0.13.0-10`](https://github.com/lightninglabs/lndclient/blob/v0.13.0-10) | `lnd @ d9f0f07142ea` | `v0.13.0-beta` |
31+
| `master` / [`v0.12.0-13`](https://github.com/lightninglabs/lndclient/blob/v0.12.0-13) | `v0.12.0-beta` | `v0.12.0-beta` |
3132

3233

3334
By default, `lndclient` requires (and enforces) the following RPC subservers to

basic_client.go

Lines changed: 112 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package lndclient
22

33
import (
4+
"encoding/hex"
45
"fmt"
56
"io/ioutil"
67
"path/filepath"
@@ -13,23 +14,27 @@ import (
1314
macaroon "gopkg.in/macaroon.v2"
1415
)
1516

16-
// BasicClientOption is a functional option argument that allows adding arbitrary
17-
// lnd basic client configuration overrides, without forcing existing users of
18-
// NewBasicClient to update their invocation. These are always processed in
19-
// order, with later options overriding earlier ones.
17+
// BasicClientOption is a functional option argument that allows adding
18+
// arbitrary lnd basic client configuration overrides, without forcing existing
19+
// users of NewBasicClient to update their invocation. These are always
20+
// processed in order, with later options overriding earlier ones.
2021
type BasicClientOption func(*basicClientOptions)
2122

2223
// basicClientOptions is a set of options that can configure the lnd client
2324
// returned by NewBasicClient.
2425
type basicClientOptions struct {
2526
macFilename string
27+
tlsData string
28+
macData string
29+
insecure bool
30+
systemCerts bool
2631
}
2732

28-
// defaultBasicClientOptions returns a basicClientOptions set to lnd basic client
29-
// defaults.
33+
// defaultBasicClientOptions returns a basicClientOptions set to lnd basic
34+
// client defaults.
3035
func defaultBasicClientOptions() *basicClientOptions {
3136
return &basicClientOptions{
32-
macFilename: defaultAdminMacaroonFilename,
37+
macFilename: adminMacFilename,
3338
}
3439
}
3540

@@ -41,9 +46,43 @@ func MacFilename(macFilename string) BasicClientOption {
4146
}
4247
}
4348

49+
// TLSData is a basic client option that sets TLS data (encoded in PEM format)
50+
// directly instead of loading them from a file.
51+
func TLSData(tlsData string) BasicClientOption {
52+
return func(bc *basicClientOptions) {
53+
bc.tlsData = tlsData
54+
}
55+
}
56+
57+
// MacaroonData is a basic client option that sets macaroon data (encoded as hex
58+
// string) directly instead of loading it from a file.
59+
func MacaroonData(macData string) BasicClientOption {
60+
return func(bc *basicClientOptions) {
61+
bc.macData = macData
62+
}
63+
}
64+
65+
// Insecure allows the basic client to establish an insecure (non-TLS)
66+
// connection to the RPC server.
67+
func Insecure() BasicClientOption {
68+
return func(bc *basicClientOptions) {
69+
bc.insecure = true
70+
}
71+
}
72+
73+
// SystemCerts instructs the basic client to use the system's certificate trust
74+
// store for verifying the TLS certificate of the RPC server.
75+
func SystemCerts() BasicClientOption {
76+
return func(bc *basicClientOptions) {
77+
bc.systemCerts = true
78+
}
79+
}
80+
4481
// applyBasicClientOptions updates a basicClientOptions set with functional
4582
// options.
46-
func (bc *basicClientOptions) applyBasicClientOptions(options ...BasicClientOption) {
83+
func (bc *basicClientOptions) applyBasicClientOptions(
84+
options ...BasicClientOption) {
85+
4786
for _, option := range options {
4887
option(bc)
4988
}
@@ -53,9 +92,7 @@ func (bc *basicClientOptions) applyBasicClientOptions(options ...BasicClientOpti
5392
// "basic" as it falls back to expected defaults if the arguments aren't
5493
// provided.
5594
func NewBasicClient(lndHost, tlsPath, macDir, network string,
56-
basicOptions ...BasicClientOption) (
57-
58-
lnrpc.LightningClient, error) {
95+
basicOptions ...BasicClientOption) (lnrpc.LightningClient, error) {
5996

6097
conn, err := NewBasicConn(
6198
lndHost, tlsPath, macDir, network, basicOptions...,
@@ -70,67 +107,93 @@ func NewBasicClient(lndHost, tlsPath, macDir, network string,
70107
// NewBasicConn creates a new basic gRPC connection to lnd. We call this
71108
// connection "basic" as it falls back to expected defaults if the arguments
72109
// aren't provided.
73-
func NewBasicConn(lndHost, tlsPath, macDir, network string,
74-
basicOptions ...BasicClientOption) (
75-
76-
*grpc.ClientConn, error) {
110+
func NewBasicConn(lndHost string, tlsPath, macDir, network string,
111+
basicOptions ...BasicClientOption) (*grpc.ClientConn, error) {
77112

78-
if tlsPath == "" {
79-
tlsPath = defaultTLSCertPath
113+
creds, mac, err := parseTLSAndMacaroon(
114+
tlsPath, macDir, network, basicOptions...,
115+
)
116+
if err != nil {
117+
return nil, err
80118
}
81119

82-
// Load the specified TLS certificate and build transport credentials
83-
creds, err := credentials.NewClientTLSFromFile(tlsPath, "")
120+
// Now we append the macaroon credentials to the dial options.
121+
cred, err := macaroons.NewMacaroonCredential(mac)
84122
if err != nil {
85-
return nil, err
123+
return nil, fmt.Errorf("error creating macaroon credential: %v",
124+
err)
86125
}
87126

88127
// Create a dial options array.
89128
opts := []grpc.DialOption{
90129
grpc.WithTransportCredentials(creds),
130+
grpc.WithPerRPCCredentials(cred),
131+
grpc.WithDefaultCallOptions(maxMsgRecvSize),
91132
}
92133

93-
if macDir == "" {
94-
macDir = filepath.Join(
95-
defaultLndDir, defaultDataDir, defaultChainSubDir,
96-
"bitcoin", network,
97-
)
134+
// We need to use a custom dialer so we can also connect to unix sockets
135+
// and not just TCP addresses.
136+
opts = append(
137+
opts, grpc.WithContextDialer(
138+
lncfg.ClientAddressDialer(defaultRPCPort),
139+
),
140+
)
141+
conn, err := grpc.Dial(lndHost, opts...)
142+
if err != nil {
143+
return nil, fmt.Errorf("unable to connect to RPC server: %v",
144+
err)
98145
}
99146

147+
return conn, nil
148+
}
149+
150+
// parseTLSAndMacaroon looks to see if the TLS certificate and macaroon were
151+
// passed in as a path or as straight-up data, and processes it accordingly, so
152+
// it can be passed into grpc to establish a connection with LND.
153+
func parseTLSAndMacaroon(tlsPath, macDir, network string,
154+
basicOptions ...BasicClientOption) (credentials.TransportCredentials,
155+
*macaroon.Macaroon, error) {
156+
100157
// Starting with the set of default options, we'll apply any specified
101158
// functional options to the basic client.
102159
bco := defaultBasicClientOptions()
103160
bco.applyBasicClientOptions(basicOptions...)
104161

105-
macPath := filepath.Join(macDir, bco.macFilename)
162+
creds, err := GetTLSCredentials(
163+
bco.tlsData, tlsPath, bco.insecure, bco.systemCerts,
164+
)
165+
if err != nil {
166+
return nil, nil, err
167+
}
106168

107-
// Load the specified macaroon file.
108-
macBytes, err := ioutil.ReadFile(macPath)
109-
if err == nil {
110-
// Only if file is found
111-
mac := &macaroon.Macaroon{}
112-
if err = mac.UnmarshalBinary(macBytes); err != nil {
113-
return nil, fmt.Errorf("unable to decode macaroon: %v",
114-
err)
169+
var macBytes []byte
170+
mac := &macaroon.Macaroon{}
171+
if bco.macData != "" {
172+
macBytes, err = hex.DecodeString(bco.macData)
173+
if err != nil {
174+
return nil, nil, err
115175
}
176+
} else {
177+
if macDir == "" {
178+
macDir = filepath.Join(
179+
defaultLndDir, defaultDataDir, defaultChainSubDir,
180+
"bitcoin", network,
181+
)
182+
}
183+
184+
macPath := filepath.Join(macDir, bco.macFilename)
116185

117-
// Now we append the macaroon credentials to the dial options.
118-
cred := macaroons.NewMacaroonCredential(mac)
119-
opts = append(opts, grpc.WithPerRPCCredentials(cred))
120-
opts = append(opts, grpc.WithDefaultCallOptions(maxMsgRecvSize))
186+
// Load the specified macaroon file.
187+
macBytes, err = ioutil.ReadFile(macPath)
188+
if err != nil {
189+
return nil, nil, err
190+
}
121191
}
122192

123-
// We need to use a custom dialer so we can also connect to unix sockets
124-
// and not just TCP addresses.
125-
opts = append(
126-
opts, grpc.WithContextDialer(
127-
lncfg.ClientAddressDialer(defaultRPCPort),
128-
),
129-
)
130-
conn, err := grpc.Dial(lndHost, opts...)
131-
if err != nil {
132-
return nil, fmt.Errorf("unable to connect to RPC server: %v", err)
193+
if err = mac.UnmarshalBinary(macBytes); err != nil {
194+
return nil, nil, fmt.Errorf("unable to decode macaroon: %v",
195+
err)
133196
}
134197

135-
return conn, nil
198+
return creds, mac, nil
136199
}

basic_client_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package lndclient
2+
3+
import (
4+
"encoding/hex"
5+
"io/ioutil"
6+
"os"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
// Tests that NewBasicConn works correctly when macaroon and TLS certificate
13+
// data are passed in directly instead of being supplied as file paths.
14+
func TestParseTLSAndMacaroon(t *testing.T) {
15+
16+
tlsData := `-----BEGIN CERTIFICATE-----
17+
MIIDhzCCAm+gAwIBAgIUEkmdMOVPL92AwgsSYFFBvz4ilmUwDQYJKoZIhvcNAQEL
18+
BQAwUzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1OMRQwEgYDVQQHDAtNaW5uZWFw
19+
b2xpczEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTIxMDQy
20+
MzA2NDkyNVoXDTIxMDUyMzA2NDkyNVowUzELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
21+
Ak1OMRQwEgYDVQQHDAtNaW5uZWFwb2xpczEhMB8GA1UECgwYSW50ZXJuZXQgV2lk
22+
Z2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnK21
23+
qJmWWs4Nwz2f2ZbTsDxJAumgDJdZ9JKsJBrqjFf7+25ip+1hIB15P1UHHPhtW5Yp
24+
P9Xm50z8W2RP2pHyCFB09cwKgdqPsS8Q2tzr5DINt+eNYa5JpxnWXM5ZqmYD7Zg0
25+
wSMVW3FuAWFpjlzNWs/UHSuDShiQLoMhl2xAjiGSsHbY9plV438/kypSKS+7wjxe
26+
0TJaTv/kWlHhQkXvnLqIMhD8J+ScGVSSk0OFgWiRmcCGDsLZgEGklHklC7ZKrr+Q
27+
Am2MGbvUaGuwW+R5d2ZaQRbQ5UVhHcna2MxUn6MzSjbEhpIsMKZoYVXCb0GFObcq
28+
UsLUOrIqpIyngd4G9wIDAQABo1MwUTAdBgNVHQ4EFgQU0lZJ2gp/RM79oAegXr/H
29+
sU+GU3YwHwYDVR0jBBgwFoAU0lZJ2gp/RM79oAegXr/HsU+GU3YwDwYDVR0TAQH/
30+
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAly744gq/LPuL0EnEbfxXrVqmvWh6
31+
t9kNljXybVjQNTZ00e4zGknOA3VM29JWOEYyQ7ut/tP+kquWLdfOq/Lehe7vnBSn
32+
lPR6IYbba9ck5AvPZgGG9fEncKxeUoI0ltI/luycmWL7Eb9j3128diIwljf9JXNT
33+
I/LThs8Nl5RSiMOuGer0e934vLlZlrEEI4rWs3DKK56WjrMeVf5dhvYK44usNwUh
34+
vKgMVFsUeyLLTN0EuZjGoFdi3lfLQo3vRwLD6h/EDa5uWK14pZXDQ30+fT2RjuVD
35+
XhkpT5dliEGFLNe6OOgeWTU1JpEXfCud/GImtNMHQi4EDWQfvWuCNGhOoQ==
36+
-----END CERTIFICATE-----`
37+
38+
macData := "0201047465737402067788991234560000062052d26ed139ea5af8" +
39+
"3e675500c4ccb2471f62191b745bab820f129e5588a255d2"
40+
41+
// Make sure it works when data is passed in.
42+
_, _, err := parseTLSAndMacaroon(
43+
"", "", "mainnet", MacFilename(""), TLSData(tlsData),
44+
MacaroonData(macData),
45+
)
46+
require.NoError(t, err)
47+
48+
// Now let's write the data to a file to make sure parseTLSAndMacaroon
49+
// parses that properly as well.
50+
tempDirPath, err := ioutil.TempDir("", ".testCreds")
51+
require.NoError(t, err)
52+
defer os.RemoveAll(tempDirPath)
53+
54+
certPath := tempDirPath + "/tls.cert"
55+
tlsPEMBytes := []byte(tlsData)
56+
57+
err = ioutil.WriteFile(certPath, tlsPEMBytes, 0644)
58+
require.NoError(t, err)
59+
60+
macPath := tempDirPath + "/test.macaroon"
61+
macBytes, err := hex.DecodeString(macData)
62+
require.NoError(t, err)
63+
64+
err = ioutil.WriteFile(macPath, macBytes, 0644)
65+
require.NoError(t, err)
66+
67+
_, _, err = parseTLSAndMacaroon(
68+
certPath, macPath, "mainnet", MacFilename(""),
69+
)
70+
require.NoError(t, err)
71+
}

chainnotifier_client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ type chainNotifierClient struct {
3535
wg sync.WaitGroup
3636
}
3737

38-
func newChainNotifierClient(conn *grpc.ClientConn,
38+
func newChainNotifierClient(conn grpc.ClientConnInterface,
3939
chainMac serializedMacaroon, timeout time.Duration) *chainNotifierClient {
4040

4141
return &chainNotifierClient{

go.mod

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
module github.com/lightninglabs/lndclient
22

33
require (
4-
github.com/btcsuite/btcd v0.21.0-beta.0.20201208033208-6bd4c64a54fa
4+
github.com/btcsuite/btcd v0.22.0-beta.0.20211005184431-e3449998be39
55
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
6-
github.com/btcsuite/btcutil v1.0.2
7-
github.com/btcsuite/btcwallet/wtxmgr v1.2.0
8-
github.com/lightningnetwork/lnd v0.12.0-beta
9-
github.com/stretchr/testify v1.5.1
10-
google.golang.org/grpc v1.24.0
6+
github.com/btcsuite/btcutil v1.0.3-0.20210527170813-e2ba6805a890
7+
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210822222949-9b5a201c344c
8+
github.com/lightningnetwork/lnd v0.14.2-beta
9+
github.com/lightningnetwork/lnd/kvdb v1.3.0
10+
github.com/stretchr/testify v1.7.0
11+
go.etcd.io/bbolt v1.3.6
12+
google.golang.org/grpc v1.38.0
13+
gopkg.in/macaroon-bakery.v2 v2.0.1
1114
gopkg.in/macaroon.v2 v2.1.0
1215
)
1316

0 commit comments

Comments
 (0)