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

Accept pubkey connections in lit #434

Closed
wants to merge 3 commits into from
Closed
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
38 changes: 34 additions & 4 deletions cmd/lit-af/lit-af.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"strings"
"encoding/hex"

"github.com/chzyer/readline"
"github.com/fatih/color"
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,8 +90,28 @@ 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)
// now e've split the address, check if pkh, if not, convert to pkh
if len(adr) == 44 {
// remote PKH, do nothing
} else if len(adr) == 66 {
// remote PK, convert to remotePKH
addr, err := hex.DecodeString(adr)
if err != nil {
return fmt.Errorf("Unable to decode hex string")
}
var temp [33]byte
copy(temp[:33], addr)
adr = lnutil.LitAdrFromPubkey(temp)
}
fmt.Printf("Adr: %s, Host: %s, Port: %d\n", adr, host, port)
if litrpc.LndcRpcCanConnectLocallyWithHomeDir(defaultDir) && adr == "" && (host == "localhost" || host == "127.0.0.1") {

lc.RPCClient, err = litrpc.NewLocalLndcRpcClientWithHomeDirAndPort(defaultDir, port)
Expand All @@ -108,7 +129,11 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) {
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 adr != "" && strings.HasPrefix(adr, "ln1") && host == "" {
ipv4, _, err := lnutil.Lookup(adr, conf.Tracker, "")
if err != nil {
Expand All @@ -125,6 +150,7 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) {
logging.Fatal(err.Error())
}
}
return nil
}

// for now just testing how to connect and get messages back and forth
Expand All @@ -137,7 +163,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
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
4 changes: 3 additions & 1 deletion litrpc/netcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,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 @@ -83,7 +85,7 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error {
connectAdr = args.LNAddr
}

err = r.Node.DialPeer(connectAdr)
err = r.Node.PeerMan.TryConnectAddress(connectAdr, nil)
if err != nil {
return err
}
Expand Down
24 changes: 23 additions & 1 deletion lndc/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package lndc

import (
"bytes"
"encoding/hex"
"fmt"
"io"
"math"
Expand Down Expand Up @@ -34,8 +35,28 @@ var _ net.Conn = (*Conn)(nil)
// remote peer located at address which has remotePub as its long-term static
// public key. In the case of a handshake failure, the connection is closed and
// a non-nil error is returned.
func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remotePKH string,
func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteString string,
dialer func(string, string) (net.Conn, error)) (*Conn, error) {

// so the param passed can either be a remotePKH or can be a remotePK
// we can simply convert the remotePK into a remotePKH because we
// don't need to choose between XK and XX here and that's done later on
logging.Debug("Trying to connect to: ", remoteString, " at ", ipAddr)
var remotePKH string
if len(remoteString) == 41 {
remotePKH = remoteString
} else if len(remoteString) == 66 {
var temp [33]byte
x, err := hex.DecodeString(remoteString)
if err != nil {
logging.Error(err)
return nil, err
}
copy(temp[:], []byte(x))
logging.Info("Converted passed remote pk to remotepkh", temp)
remotePKH = lnutil.LitAdrFromPubkey(temp)
}

var conn net.Conn
var err error
conn, err = dialer("tcp", ipAddr)
Expand Down Expand Up @@ -81,6 +102,7 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remotePKH string,
}

logging.Info("Received pubkey", s)
logging.Debug("Received pubkey: ", lnutil.LitAdrFromPubkey(s), " have pubkey: ", remotePKH)
if lnutil.LitAdrFromPubkey(s) != remotePKH {
return nil, fmt.Errorf("Remote PKH doesn't match. Quitting!")
}
Expand Down
50 changes: 29 additions & 21 deletions lnp2p/peermgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package lnp2p
//"crypto/ecdsa" // TODO Use ecdsa not koblitz
import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"net"
"strconv"
Expand Down Expand Up @@ -102,6 +103,11 @@ func (pm *PeerManager) GetExternalAddress() string {
return string(addr)
}

func (pm *PeerManager) GetExternalPubkeyString() string {
c := koblitz.PublicKey(ecdsa.PublicKey(pm.idkey.PublicKey))
return hex.EncodeToString(c.SerializeCompressed())
}

func computeIdentKeyFromRoot(rootkey *hdkeychain.ExtendedKey) (privkey, error) {
var kg portxo.KeyGen
kg.Depth = 5
Expand Down Expand Up @@ -144,38 +150,40 @@ func (pm *PeerManager) GetPeerByIdx(id int32) *Peer {
}

// TryConnectAddress attempts to connect to the specified LN address.
func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (*Peer, error) {
func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) error {

// the address we're dialing can either be of the following two types:
// 1. pkhash@ip:port
// 2. pk@ip:port (where pk is in hex and is a compressed public key)
// Figure out who we're trying to connect to.
who, where := splitAdrString(addr)
pkOrpkHash, where := splitAdrString(addr)
if where == "" {
ipv4, _, err := lnutil.Lookup(addr, pm.trackerURL, "")
if err != nil {
return nil, err
return err
}
where = fmt.Sprintf("%s:2448", ipv4)
}

lnwho := lncore.LnAddr(who)
x, y := pm.tryConnectPeer(where, &lnwho, settings)
return x, y

who := lncore.LnAddr(pkOrpkHash)
return pm.tryConnectPeer(&who, where, settings)
}

func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, settings *NetSettings) (*Peer, error) {
// tryConnectPeer tries to dial to the passed addr at where along with the passed
// settings. Returns an error
func (pm *PeerManager) tryConnectPeer(addr *lncore.LnAddr, where string, settings *NetSettings) (error) {

// lnaddr check, to make sure that we do the right thing.
if lnaddr == nil {
return nil, fmt.Errorf("connection to a peer with unknown lnaddr not supported yet")
if addr == nil {
return fmt.Errorf("connection to a peer with unknown addr not supported yet")
}

// Do NAT setup stuff.
if settings != nil && settings.NatMode != nil {

// Do some type juggling.
x, err := strconv.Atoi(netaddr[1:])
x, err := strconv.Atoi(where[1:])
if err != nil {
return nil, err
return err
}
lisPort := uint16(x) // if only Atoi could infer which type we wanted to parse as!

Expand All @@ -185,18 +193,18 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set
logging.Infof("Attempting port forwarding via UPnP...")
err = nat.SetupUpnp(lisPort)
if err != nil {
return nil, err
return err
}
} else if *settings.NatMode == "pmp" {
// NAT Port Mapping Protocol
timeout := time.Duration(10 * time.Second)
logging.Infof("Attempting port forwarding via PMP...")
_, err = nat.SetupPmp(timeout, lisPort)
if err != nil {
return nil, err
return err
}
} else {
return nil, fmt.Errorf("invalid NAT type: %s", *settings.NatMode)
return fmt.Errorf("invalid NAT type: %s", *settings.NatMode)
}
}

Expand All @@ -206,18 +214,18 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set
if settings != nil && settings.ProxyAddr != nil {
d, err := connectToProxyTCP(*settings.ProxyAddr, settings.ProxyAuth)
if err != nil {
return nil, err
return err
}
dialer = d
}

// Set up the connection.
lndcconn, err := lndc.Dial(pm.idkey, netaddr, string(*lnaddr), dialer)
lndcconn, err := lndc.Dial(pm.idkey, where, string(*addr), dialer)
if err != nil {
return nil, err
return err
}

pi, err := pm.peerdb.GetPeerInfo(*lnaddr)
pi, err := pm.peerdb.GetPeerInfo(*addr)
if err != nil {
logging.Errorf("Problem loading peer info from DB: %s\n", err.Error())
// don't kill the connection?
Expand Down Expand Up @@ -266,7 +274,7 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set
go processConnectionInboundTraffic(p, pm)

// Return
return p, nil
return nil

}

Expand Down
3 changes: 2 additions & 1 deletion qln/netio.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func (nd *LitNode) TCPListener(port int) (string, error) {

lnaddr := nd.PeerMan.GetExternalAddress()

logging.Infof("My raw hex Public Key is: %s", nd.PeerMan.GetExternalPubkeyString())
logging.Infof("Listening with ln address: %s \n", lnaddr)

// Don't announce on the tracker if we are communicating via SOCKS proxy
Expand Down Expand Up @@ -80,7 +81,7 @@ func splitAdrString(adr string) (string, string) {
// TODO Remove this.
func (nd *LitNode) DialPeer(connectAdr string) error {

_, err := nd.PeerMan.TryConnectAddress(connectAdr, nil)
err := nd.PeerMan.TryConnectAddress(connectAdr, nil)
if err != nil {
return err
}
Expand Down
9 changes: 8 additions & 1 deletion qln/remotecontrol.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ func (nd *LitNode) RemoteControlRequestHandler(msg lnutil.RemoteControlRpcReques

// If i'm not authorized, and it's not a whitelisted method then we fail the
// request with an 'unauthorized' error
// while this is good, if you're the first person to connect to lit
// using the --con option, you're out of luck.
// Because nobody has authenticated you and neither can you authenticate yourself.
// In order to solve this, we need to pass a cli option to lit which can allow for
// authentication before the first peer logs in.
if !auth.Allowed && !whitelisted {
err = fmt.Errorf("Received remote control request from unauthorized peer: %x", pubKey)
logging.Errorf(err.Error())
Expand Down Expand Up @@ -259,14 +264,15 @@ func (nd *LitNode) SaveRemoteControlAuthorization(pub [33]byte, auth *RemoteCont
})
}

// GetRemoteControlAuthorization retrieves the remote controlauthorizzation for
// GetRemoteControlAuthorization retrieves the remote control authorization for
// a specific pubkey from the database.
func (nd *LitNode) GetRemoteControlAuthorization(pub [33]byte) (*RemoteControlAuthorization, error) {
r := new(RemoteControlAuthorization)

// If the client uses our default remote control key (derived from our root priv)
// then it has access to our private key (file) and is most likely running from our
// localhost. So we always accept this.
logging.Debug("Fetching remote control authorization for pubkey: ", pub[:], nd.DefaultRemoteControlKey.SerializeCompressed())
if bytes.Equal(pub[:], nd.DefaultRemoteControlKey.SerializeCompressed()) {
r.Allowed = true
return r, nil
Expand All @@ -278,6 +284,7 @@ func (nd *LitNode) GetRemoteControlAuthorization(pub [33]byte) (*RemoteControlAu
// serialize state
b := cbk.Get(pub[:])
r = RemoteControlAuthorizationFromBytes(b, pub)
logging.Debug("Fetched pubkey ", r," from the database")
return nil
})
return r, err
Expand Down
4 changes: 4 additions & 0 deletions uspv/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ func CheckHeaderChain(

// reorg is go, snip to attach height
reorgDepth := height - attachHeight
if reorgDepth > numheaders {
logging.Info("Reorg depth is greater than the number of headers received, exiting!")
return 0, fmt.Errorf("Reorg depth is greater than the number of headers received, exiting!")
}
oldHeaders = oldHeaders[:numheaders-reorgDepth]
}

Expand Down