Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add version 2 (reduce QR-Code Density) #19

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
defaultReadMacFilename = "readonly.macaroon"
defaultInvoiceMacFilename = "invoice.macaroon"
defaultRPCPort = 10009
defaultVersion = 2
)

var (
Expand Down Expand Up @@ -55,6 +56,7 @@ type lndConnectConfig struct {
Invoice bool `long:"invoice" description:"Use invoice macaroon"`
Readonly bool `long:"readonly" description:"Use readonly macaroon"`
Query arrayFlags `short:"q" long:"query" description:"Add additional url query parameters"`
Version uint8 `short:"v" long:"version" description:"The lndconnect version to use"`
}

// config defines the configuration options for lndconnect.
Expand Down Expand Up @@ -100,6 +102,7 @@ type config struct {
func loadConfig() (*config, error) {
defaultCfg := config{
LndConnect: &lndConnectConfig{
Version: defaultVersion,
Port: defaultRPCPort,
},
LndDir: defaultLndDir,
Expand Down
202 changes: 145 additions & 57 deletions lndconnect.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"net/url"
"os"
"strings"
"crypto/sha256"
"strconv"

"github.com/Baozisoftware/qrcode-terminal-go"
"github.com/glendc/go-external-ip"
Expand Down Expand Up @@ -54,83 +56,169 @@ func main() {
func displayLink(loadedConfig *config) {
var err error

// host
ipString := ""
if loadedConfig.LndConnect.Host != "" {
ipString = loadedConfig.LndConnect.Host
} else if loadedConfig.LndConnect.LocalIp {
ipString = getLocalIP()
} else if loadedConfig.LndConnect.Localhost {
ipString = "127.0.0.1"
} else {
ipString = getPublicIP()
}
if loadedConfig.LndConnect.Version == 1 {
// host
ipString := ""
if loadedConfig.LndConnect.Host != "" {
ipString = loadedConfig.LndConnect.Host
} else if loadedConfig.LndConnect.LocalIp {
ipString = getLocalIP()
} else if loadedConfig.LndConnect.Localhost {
ipString = "127.0.0.1"
} else {
ipString = getPublicIP()
}

ipString = net.JoinHostPort(ipString, fmt.Sprint(loadedConfig.LndConnect.Port))

u := url.URL{Scheme: "lndconnect", Host: ipString}
q := u.Query()

// cert
if !loadedConfig.LndConnect.NoCert {
certBytes, err := ioutil.ReadFile(loadedConfig.TLSCertPath)
if err != nil {
fmt.Println(err)
return
}

block, _ := pem.Decode(certBytes)
if block == nil || block.Type != "CERTIFICATE" {
fmt.Println("failed to decode PEM block containing certificate")
}

ipString = net.JoinHostPort(ipString, fmt.Sprint(loadedConfig.LndConnect.Port))
certificate := b64.RawURLEncoding.EncodeToString([]byte(block.Bytes))

q.Add("cert", certificate)
}

u := url.URL{Scheme: "lndconnect", Host: ipString}
q := u.Query()
// macaroon
var macBytes []byte
if loadedConfig.LndConnect.Invoice {
macBytes, err = ioutil.ReadFile(loadedConfig.InvoiceMacPath)
} else if loadedConfig.LndConnect.Readonly {
macBytes, err = ioutil.ReadFile(loadedConfig.ReadMacPath)
} else {
macBytes, err = ioutil.ReadFile(loadedConfig.AdminMacPath)
}

// cert
if !loadedConfig.LndConnect.NoCert {
certBytes, err := ioutil.ReadFile(loadedConfig.TLSCertPath)
if err != nil {
fmt.Println(err)
return
}

block, _ := pem.Decode(certBytes)
if block == nil || block.Type != "CERTIFICATE" {
fmt.Println("failed to decode PEM block containing certificate")
macaroonB64 := b64.RawURLEncoding.EncodeToString([]byte(macBytes))

q.Add("macaroon", macaroonB64)

// custom query
for _, s := range loadedConfig.LndConnect.Query {
queryParts := strings.Split(s, "=")

if len(queryParts) != 2 {
fmt.Println("Invalid Query Argument:", s)
return
}

q.Add(queryParts[0], queryParts[1])
}

certificate := b64.RawURLEncoding.EncodeToString([]byte(block.Bytes))
u.RawQuery = q.Encode()

// generate link / QR Code
if loadedConfig.LndConnect.Url {
fmt.Println(u.String())
} else if loadedConfig.LndConnect.Image {
qrcode.WriteFile(u.String(), qrcode.Low, 512, "lndconnect-qr.png")
fmt.Println("Wrote QR Code to file \"lndconnect-qr.png\"")
} else {
obj := qrcodeTerminal.New2(qrcodeTerminal.ConsoleColors.BrightBlack, qrcodeTerminal.ConsoleColors.BrightWhite,qrcodeTerminal.QRCodeRecoveryLevels.Low)
obj.Get(u.String()).Print()
fmt.Println("\n⚠️ Press \"cmd + -\" a few times to see the full QR Code!\nIf that doesn't work run \"lndconnect -j\" to get a code you can copy paste into the app.")
}
} else { // Version 2 and higher...
// host
ipString := ""
if loadedConfig.LndConnect.Host != "" {
ipString = loadedConfig.LndConnect.Host
} else if loadedConfig.LndConnect.LocalIp {
ipString = getLocalIP()
} else if loadedConfig.LndConnect.Localhost {
ipString = "127.0.0.1"
} else {
ipString = getPublicIP()
}

q.Add("cert", certificate)
}
ipString = net.JoinHostPort(ipString, fmt.Sprint(loadedConfig.LndConnect.Port))

// macaroon
var macBytes []byte
if loadedConfig.LndConnect.Invoice {
macBytes, err = ioutil.ReadFile(loadedConfig.InvoiceMacPath)
} else if loadedConfig.LndConnect.Readonly {
macBytes, err = ioutil.ReadFile(loadedConfig.ReadMacPath)
} else {
macBytes, err = ioutil.ReadFile(loadedConfig.AdminMacPath)
}
u := url.URL{Scheme: "lndconn", Host: ipString}
q := u.Query()

if err != nil {
fmt.Println(err)
return
}
// version
q.Add("v", strconv.Itoa(int(loadedConfig.LndConnect.Version)))

// cert
if !loadedConfig.LndConnect.NoCert {
certBytes, err := ioutil.ReadFile(loadedConfig.TLSCertPath)
if err != nil {
fmt.Println(err)
return
}

macaroonB64 := b64.RawURLEncoding.EncodeToString([]byte(macBytes))
block, _ := pem.Decode(certBytes)
if block == nil || block.Type != "CERTIFICATE" {
fmt.Println("failed to decode PEM block containing certificate")
}

q.Add("macaroon", macaroonB64)
hash := sha256.Sum256(block.Bytes)
certificate := b64.RawURLEncoding.EncodeToString(hash[:])

// custom query
for _, s := range loadedConfig.LndConnect.Query {
queryParts := strings.Split(s, "=")
q.Add("c", certificate)
}

// macaroon
var macBytes []byte
if loadedConfig.LndConnect.Invoice {
macBytes, err = ioutil.ReadFile(loadedConfig.InvoiceMacPath)
} else if loadedConfig.LndConnect.Readonly {
macBytes, err = ioutil.ReadFile(loadedConfig.ReadMacPath)
} else {
macBytes, err = ioutil.ReadFile(loadedConfig.AdminMacPath)
}

if len(queryParts) != 2 {
fmt.Println("Invalid Query Argument:", s)
if err != nil {
fmt.Println(err)
return
}

q.Add(queryParts[0], queryParts[1])
}
macaroonB64 := b64.RawURLEncoding.EncodeToString([]byte(macBytes))

q.Add("m", macaroonB64)

// custom query
for _, s := range loadedConfig.LndConnect.Query {
queryParts := strings.Split(s, "=")

u.RawQuery = q.Encode()

// generate link / QR Code
if loadedConfig.LndConnect.Url {
fmt.Println(u.String())
} else if loadedConfig.LndConnect.Image {
qrcode.WriteFile(u.String(), qrcode.Medium, 512, "lndconnect-qr.png")
fmt.Println("Wrote QR Code to file \"lndconnect-qr.png\"")
} else {
obj := qrcodeTerminal.New()
obj.Get(u.String()).Print()
fmt.Println("\n⚠️ Press \"cmd + -\" a few times to see the full QR Code!\nIf that doesn't work run \"lndconnect -j\" to get a code you can copy paste into the app.")
if len(queryParts) != 2 {
fmt.Println("Invalid Query Argument:", s)
return
}

q.Add(queryParts[0], queryParts[1])
}

u.RawQuery = q.Encode()

// generate link / QR Code
if loadedConfig.LndConnect.Url {
fmt.Println(u.String())
} else if loadedConfig.LndConnect.Image {
qrcode.WriteFile(u.String(), qrcode.Low, 512, "lndconnect-qr.png")
fmt.Println("Wrote QR Code to file \"lndconnect-qr.png\"")
} else {
obj := qrcodeTerminal.New2(qrcodeTerminal.ConsoleColors.BrightBlack, qrcodeTerminal.ConsoleColors.BrightWhite,qrcodeTerminal.QRCodeRecoveryLevels.Low)
obj.Get(u.String()).Print()
fmt.Println("\n⚠️ Press \"cmd + -\" a few times to see the full QR Code!\nIf that doesn't work run \"lndconnect -j\" to get a code you can copy paste into the app.")
}
}
}
85 changes: 85 additions & 0 deletions lndconnect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ func ExampleLocalhost() {
LndConnect: &lndConnectConfig{
Localhost: true,
Url: true,
Version: 1,
},
}
displayLink(c)
Expand All @@ -22,6 +23,7 @@ func ExampleQuery() {
Localhost: true,
Url: true,
Query: arrayFlags{"test=abc"},
Version: 1,
},
}
displayLink(c)
Expand All @@ -37,6 +39,7 @@ func ExamplePort() {
Localhost: true,
Url: true,
Port: 123,
Version: 1,
},
}
displayLink(c)
Expand All @@ -52,6 +55,7 @@ func ExampleHost() {
Localhost: true,
Url: true,
Host: "1.2.3.4",
Version: 1,
},
}
displayLink(c)
Expand All @@ -68,9 +72,90 @@ func ExampleNoCert() {
Url: true,
Host: "1.2.3.4",
NoCert: true,
Version: 1,
},
}
displayLink(c)

// Output: lndconnect://1.2.3.4:0?macaroon=AgEDbG5kArsBAwoQ3_I9f6kgSE6aUPd85lWpOBIBMBoWCgdhZGRyZXNzEgRyZWFkEgV3cml0ZRoTCgRpbmZvEgRyZWFkEgV3cml0ZRoXCghpbnZvaWNlcxIEcmVhZBIFd3JpdGUaFgoHbWVzc2FnZRIEcmVhZBIFd3JpdGUaFwoIb2ZmY2hhaW4SBHJlYWQSBXdyaXRlGhYKB29uY2hhaW4SBHJlYWQSBXdyaXRlGhQKBXBlZXJzEgRyZWFkEgV3cml0ZQAABiAiUTBv3Eh6iDbdjmXCfNxp4HBEcOYNzXhrm-ncLHf5jA
}

func ExampleLocalhostV2() {
c := &config{
TLSCertPath: "testdata/tls.cert",
AdminMacPath: "testdata/admin.macaroon",
LndConnect: &lndConnectConfig{
Localhost: true,
Url: true,
Version: 2,
},
}
displayLink(c)

// Output: lndconn://127.0.0.1:0?c=somNeor-nJza1UFoBULswg6lRo3f1_-euBY7zV2DIUk&m=AgEDbG5kArsBAwoQ3_I9f6kgSE6aUPd85lWpOBIBMBoWCgdhZGRyZXNzEgRyZWFkEgV3cml0ZRoTCgRpbmZvEgRyZWFkEgV3cml0ZRoXCghpbnZvaWNlcxIEcmVhZBIFd3JpdGUaFgoHbWVzc2FnZRIEcmVhZBIFd3JpdGUaFwoIb2ZmY2hhaW4SBHJlYWQSBXdyaXRlGhYKB29uY2hhaW4SBHJlYWQSBXdyaXRlGhQKBXBlZXJzEgRyZWFkEgV3cml0ZQAABiAiUTBv3Eh6iDbdjmXCfNxp4HBEcOYNzXhrm-ncLHf5jA&v=2
}

func ExampleQueryV2() {
c := &config{
TLSCertPath: "testdata/tls.cert",
AdminMacPath: "testdata/admin.macaroon",
LndConnect: &lndConnectConfig{
Localhost: true,
Url: true,
Query: arrayFlags{"test=abc"},
Version: 2,
},
}
displayLink(c)

// Output: lndconn://127.0.0.1:0?c=somNeor-nJza1UFoBULswg6lRo3f1_-euBY7zV2DIUk&m=AgEDbG5kArsBAwoQ3_I9f6kgSE6aUPd85lWpOBIBMBoWCgdhZGRyZXNzEgRyZWFkEgV3cml0ZRoTCgRpbmZvEgRyZWFkEgV3cml0ZRoXCghpbnZvaWNlcxIEcmVhZBIFd3JpdGUaFgoHbWVzc2FnZRIEcmVhZBIFd3JpdGUaFwoIb2ZmY2hhaW4SBHJlYWQSBXdyaXRlGhYKB29uY2hhaW4SBHJlYWQSBXdyaXRlGhQKBXBlZXJzEgRyZWFkEgV3cml0ZQAABiAiUTBv3Eh6iDbdjmXCfNxp4HBEcOYNzXhrm-ncLHf5jA&test=abc&v=2
}

func ExamplePortV2() {
c := &config{
TLSCertPath: "testdata/tls.cert",
AdminMacPath: "testdata/admin.macaroon",
LndConnect: &lndConnectConfig{
Localhost: true,
Url: true,
Port: 123,
Version: 2,
},
}
displayLink(c)

// Output: lndconn://127.0.0.1:123?c=somNeor-nJza1UFoBULswg6lRo3f1_-euBY7zV2DIUk&m=AgEDbG5kArsBAwoQ3_I9f6kgSE6aUPd85lWpOBIBMBoWCgdhZGRyZXNzEgRyZWFkEgV3cml0ZRoTCgRpbmZvEgRyZWFkEgV3cml0ZRoXCghpbnZvaWNlcxIEcmVhZBIFd3JpdGUaFgoHbWVzc2FnZRIEcmVhZBIFd3JpdGUaFwoIb2ZmY2hhaW4SBHJlYWQSBXdyaXRlGhYKB29uY2hhaW4SBHJlYWQSBXdyaXRlGhQKBXBlZXJzEgRyZWFkEgV3cml0ZQAABiAiUTBv3Eh6iDbdjmXCfNxp4HBEcOYNzXhrm-ncLHf5jA&v=2
}

func ExampleHostV2() {
c := &config{
TLSCertPath: "testdata/tls.cert",
AdminMacPath: "testdata/admin.macaroon",
LndConnect: &lndConnectConfig{
Localhost: true,
Url: true,
Host: "1.2.3.4",
Version: 2,
},
}
displayLink(c)

// Output: lndconn://1.2.3.4:0?c=somNeor-nJza1UFoBULswg6lRo3f1_-euBY7zV2DIUk&m=AgEDbG5kArsBAwoQ3_I9f6kgSE6aUPd85lWpOBIBMBoWCgdhZGRyZXNzEgRyZWFkEgV3cml0ZRoTCgRpbmZvEgRyZWFkEgV3cml0ZRoXCghpbnZvaWNlcxIEcmVhZBIFd3JpdGUaFgoHbWVzc2FnZRIEcmVhZBIFd3JpdGUaFwoIb2ZmY2hhaW4SBHJlYWQSBXdyaXRlGhYKB29uY2hhaW4SBHJlYWQSBXdyaXRlGhQKBXBlZXJzEgRyZWFkEgV3cml0ZQAABiAiUTBv3Eh6iDbdjmXCfNxp4HBEcOYNzXhrm-ncLHf5jA&v=2
}

func ExampleNoCertV2() {
c := &config{
TLSCertPath: "testdata/tls.cert",
AdminMacPath: "testdata/admin.macaroon",
LndConnect: &lndConnectConfig{
Localhost: true,
Url: true,
Host: "1.2.3.4",
NoCert: true,
Version: 2,
},
}
displayLink(c)

// Output: lndconn://1.2.3.4:0?m=AgEDbG5kArsBAwoQ3_I9f6kgSE6aUPd85lWpOBIBMBoWCgdhZGRyZXNzEgRyZWFkEgV3cml0ZRoTCgRpbmZvEgRyZWFkEgV3cml0ZRoXCghpbnZvaWNlcxIEcmVhZBIFd3JpdGUaFgoHbWVzc2FnZRIEcmVhZBIFd3JpdGUaFwoIb2ZmY2hhaW4SBHJlYWQSBXdyaXRlGhYKB29uY2hhaW4SBHJlYWQSBXdyaXRlGhQKBXBlZXJzEgRyZWFkEgV3cml0ZQAABiAiUTBv3Eh6iDbdjmXCfNxp4HBEcOYNzXhrm-ncLHf5jA&v=2
}