Skip to content

Commit

Permalink
connect to remote BOLT nodes
Browse files Browse the repository at this point in the history
this is a combination of earlier PRs mit-dci#434 and mit-dci#435 with newer changes.
With this PR, you must be able to

1. Connect to a remote lnd node and observe connection success
2. Connect remote lnd node to lit node for 15s
  • Loading branch information
Varunram committed Dec 5, 2018
1 parent 087d77e commit 00d4acc
Show file tree
Hide file tree
Showing 17 changed files with 478 additions and 301 deletions.
77 changes: 50 additions & 27 deletions cmd/lit-af/lit-af.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"encoding/hex"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -65,7 +66,7 @@ func newConfigParser(conf *litAfConfig, options flags.Options) *flags.Parser {
return parser
}

func (lc *litAfClient) litAfSetup(conf litAfConfig) {
func (lc *litAfClient) litAfSetup(conf litAfConfig) error {

var err error
// create home directory if it does not exist
Expand All @@ -89,42 +90,60 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) {
}
logging.SetLogLevel(logLevel) // defaults to zero

// we don't know whether the passed address is a remotePKH or a remotePK
// so we need to detect that here and then take steps accordingly
// so the first part involved here would be either dealing with raw pubkeys or
// dealing with pk hashes
// another question here is how do we deal with connecting to other nodes?
// if we need that in, we need to hcange our overall architecture to host
// pks as well as pk hashes
adr, host, port := lnutil.ParseAdrStringWithPort(conf.Con)
logging.Infof("Adr: %s, Host: %s, Port: %d", adr, host, port)
if litrpc.LndcRpcCanConnectLocallyWithHomeDir(defaultDir) && adr == "" && (host == "localhost" || host == "127.0.0.1") {
// now we've split the address, check if pkh, if not, route straight to noise_xk

if len(adr) == 0 {
// so the user didn't provide us with an address to connect to
// we need to connect to the locally running lit-af instance
lc.RPCClient, err = litrpc.NewLocalLndcRpcClientWithHomeDirAndPort(defaultDir, port)
if err != nil {
logging.Fatal(err.Error())
}
} else {
if !lnutil.LitAdrOK(adr) {
logging.Fatal("lit address passed in -con parameter is not valid")
}
return nil
}

keyFilePath := filepath.Join(defaultDir, "lit-af-key.hex")
privKey, err := lnutil.ReadKeyFile(keyFilePath)
if err != nil {
logging.Fatal(err.Error())
}
key, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), privKey[:])

if adr != "" && strings.HasPrefix(adr, "ln1") && host == "" {
ipv4, _, err := lnutil.Lookup(adr, conf.Tracker, "")
if err != nil {
logging.Fatalf("Error looking up address on the tracker: %s", err)
} else {
adr = fmt.Sprintf("%s@%s", adr, ipv4)
}
} else {
adr = fmt.Sprintf("%s@%s:%d", adr, host, port)
}
keyFilePath := filepath.Join(defaultDir, "lit-af-key.hex")
privKey, err := lnutil.ReadKeyFile(keyFilePath)
if err != nil {
logging.Fatal(err.Error())
}
key, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), privKey[:])
pubkey := key.PubKey().SerializeCompressed() // this is in bytes
fmt.Printf("The pubkey of this lit-af instance is: %s\n", hex.EncodeToString(pubkey))
var temp [33]byte
copy(temp[:], pubkey[:33])
fmt.Printf("The pkh of this lit-af instance is: %s\n", lnutil.LitAdrFromPubkey(temp))

if len(adr) == 44 && !lnutil.LitAdrOK(adr) {
logging.Fatal("lit address passed in -con parameter is not valid")
}

lc.RPCClient, err = litrpc.NewLndcRpcClient(adr, key)
if host == "" {
ipv4, _, err := lnutil.Lookup(adr, conf.Tracker, "")
if err != nil {
logging.Fatal(err.Error())
logging.Fatalf("Error looking up address on the tracker: %s", err)
}
adr = fmt.Sprintf("%s@%s", adr, ipv4)
} else {
// host is non empty or address is remotePK, doesn't matter since NewLndcRpcClient will take acre of it for us
adr = fmt.Sprintf("%s@%s:%d", adr, host, port)
}

fmt.Printf("Remote Host: %s, Port: %d\n", host, port)
lc.RPCClient, err = litrpc.NewLndcRpcClient(adr, key)
if err != nil {
logging.Fatal(err.Error())
}

return nil
}

// for now just testing how to connect and get messages back and forth
Expand All @@ -137,7 +156,11 @@ func main() {
Dir: defaultDir,
Tracker: defaultTracker,
}
lc.litAfSetup(conf) // setup lit-af to start
err = lc.litAfSetup(conf) // setup lit-af to start
if err != nil {
logging.Error(err)
return
}

rl, err := readline.NewEx(&readline.Config{
Prompt: lnutil.Prompt("lit-af") + lnutil.White("# "),
Expand Down
14 changes: 2 additions & 12 deletions db/lnbolt/peerdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,7 @@ func (pdb *peerboltdb) GetPeerAddrs() ([]lncore.LnAddr, error) {
if k == nil {
break
}
lnaddr, err := lncore.ParseLnAddr(string(k))
if err != nil {
logging.Warnf("lnbolt/peerdb: found invalid key in DB as lnaddr: %s (error: %s)", string(k), err.Error())
continue
}

atmp = append(atmp, lnaddr)
atmp = append(atmp, lncore.LnAddr(string(k)))
}

// Now that we have the final array return it.
Expand Down Expand Up @@ -132,11 +126,7 @@ func (pdb *peerboltdb) GetPeerInfos() (map[lncore.LnAddr]lncore.PeerInfo, error)
return err2
}

ka, err2 := lncore.ParseLnAddr(string(k))
if err2 != nil {
logging.Warnf("lnbolt/peerdb: found invalid key in DB as lnaddr: %s (error: %s)", string(k), err.Error())
continue
}
ka := lncore.LnAddr(string(k))
mtmp[ka] = pi

}
Expand Down
35 changes: 29 additions & 6 deletions lit.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"runtime"
"syscall"
"time"
"encoding/hex"

"github.com/mit-dci/lit/logging"

Expand Down Expand Up @@ -55,11 +56,12 @@ type litConfig struct { // define a struct for usage with go-flags
Rpcport uint16 `short:"p" long:"rpcport" description:"Set RPC port to connect to"`
Rpchost string `long:"rpchost" description:"Set RPC host to listen to"`
// auto config
AutoReconnect bool `long:"autoReconnect" description:"Attempts to automatically reconnect to known peers periodically."`
AutoReconnectInterval int64 `long:"autoReconnectInterval" description:"The interval (in seconds) the reconnect logic should be executed"`
AutoReconnectOnlyConnectedCoins bool `long:"autoReconnectOnlyConnectedCoins" description:"Only reconnect to peers that we have channels with in a coin whose coin daemon is available"`
AutoListenPort int `long:"autoListenPort" description:"When auto reconnect enabled, starts listening on this port"`
NoAutoListen bool `long:"noautolisten" description:"Don't automatically listen on any ports."`
AutoReconnect bool `long:"autoReconnect" description:"Attempts to automatically reconnect to known peers periodically."`
AutoReconnectInterval int64 `long:"autoReconnectInterval" description:"The interval (in seconds) the reconnect logic should be executed"`
AutoReconnectOnlyConnectedCoins bool `long:"autoReconnectOnlyConnectedCoins" description:"Only reconnect to peers that we have channels with in a coin whose coin daemon is available"`
AutoListenPort int `long:"autoListenPort" description:"When auto reconnect enabled, starts listening on this port"`
NoAutoListen bool `long:"noautolisten" description:"Don't automatically listen on any ports."`
Whitelist string `long:"whitelist" description:"Whitelist a single address so that you can enable a remote peer to login for the first time and authenticate others"`
Params *coinparam.Params
}

Expand Down Expand Up @@ -293,12 +295,33 @@ func main() {
// if we don't link wallet, we can still continue, no worries.
logging.Error(err)
}

logging.Info("Starting lit node")
rpcl := new(litrpc.LitRPC)
rpcl.Node = node
rpcl.OffButton = make(chan bool, 1)
node.RPC = rpcl

Authorization := new(qln.RemoteControlAuthorization)
Authorization.UnansweredRequest = false
Authorization.Allowed = true

// check for whitelisted addresses here
if conf.Whitelist != "" && len(conf.Whitelist) == 66 { // the length of a standard LNAddr
// pass the pubkey here, which is ugly
// we could pass the pkh, have the peer dial us and we could get
// the pubkey during the second round of noise, but maybe overkill?
// we need to decode this hex string into a byte slice
addr, _ := hex.DecodeString(conf.Whitelist)
var temp [33]byte
copy(temp[:33], addr)
logging.Info("Whitelisting address as requested: ",addr)
err = rpcl.Node.SaveRemoteControlAuthorization(temp, Authorization)
if err != nil {
logging.Errorf("Error whitelisting address: %s", err.Error())
// don't fatal since this doesn't affect program flow
}
}

if conf.UnauthRPC {
go litrpc.RPCListen(rpcl, conf.Rpchost, conf.Rpcport)
}
Expand Down
13 changes: 5 additions & 8 deletions litrpc/netcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/mit-dci/lit/bech32"
"github.com/mit-dci/lit/crypto/koblitz"
"github.com/mit-dci/lit/lncore"
"github.com/mit-dci/lit/lnp2p"
"github.com/mit-dci/lit/lnutil"
"github.com/mit-dci/lit/logging"
"github.com/mit-dci/lit/qln"
Expand Down Expand Up @@ -65,6 +66,8 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error {
// first, see if the peer to connect to is referenced by peer index.
var connectAdr string
// check if a peer number was supplied instead of a pubkeyhash
// accept either an string or a pubkey (raw)
// so args.LNAddr passed here contains blah@host:ip
peerIdxint, err := strconv.Atoi(args.LNAddr)
// number will mean no error
if err == nil {
Expand All @@ -81,7 +84,6 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error {
// use string as is, try to convert to ln address
connectAdr = args.LNAddr
}

err = r.Node.DialPeer(connectAdr)
if err != nil {
return err
Expand All @@ -94,13 +96,8 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error {
paddr = strings.SplitN(paddr, "@", 2)[0]
}

pm := r.Node.PeerMan
lnaddr, err := lncore.ParseLnAddr(paddr)
if err != nil {
return err
}

p := pm.GetPeer(lnaddr)
var pm *lnp2p.PeerManager = r.Node.PeerMan
p := pm.GetPeer(lncore.LnAddr(paddr))
if p == nil {
return fmt.Errorf("couldn't find peer in manager after connecting")
}
Expand Down
38 changes: 1 addition & 37 deletions lncore/peers.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,9 @@
package lncore

import (
"fmt"

"github.com/mit-dci/lit/bech32"
)

// LnAddr is just a bech32-encoded pubkey.
// TODO Move this to another package so it's more obviously not *just* IO-related.
type LnAddr string

// ParseLnAddr will verify that the string passed is a valid LN address, as in
// ln1pmclh89haeswrw0unf8awuyqeu4t2uell58nea.
func ParseLnAddr(m string) (LnAddr, error) {

prefix, raw, err := bech32.Decode(m)

// Check it's valid bech32.
if err != nil {
return "", err
}

// Check it has the right prefix.
if prefix != "ln" {
return "", fmt.Errorf("prefix is not 'ln'")
}

// Check the length of the content bytes is right.
if len(raw) > 20 {
return "", fmt.Errorf("address too long to be pubkey")
}

return LnAddr(m), nil // should be the only place we cast to this type

}

// ToString returns the LnAddr as a string. Right now it just unwraps it but it
// might do something more eventually.
func (lnaddr LnAddr) ToString() string {
return string(lnaddr)
}

// LitPeerStorage is storage for peer data.
type LitPeerStorage interface {
GetPeerAddrs() ([]LnAddr, error)
Expand All @@ -59,6 +22,7 @@ type PeerInfo struct {
LnAddr *LnAddr `json:"lnaddr"`
Nickname *string `json:"name"`
NetAddr *string `json:"netaddr"` // ip address, port, I guess
Pubkey *string `json:pubkey`

// TEMP This is again, for adapting to the old system.
PeerIdx uint32 `json:"hint_peeridx"`
Expand Down
Loading

0 comments on commit 00d4acc

Please sign in to comment.