lnsocket

A minimal C library for connecting to the lightning network
git clone git://jb55.com/lnsocket
Log | Files | Refs | Submodules | README | LICENSE

lnsocket.go (3881B)


      1 package lnsocket
      2 
      3 import (
      4 	"bytes"
      5 	"encoding/hex"
      6 	"fmt"
      7 	"io"
      8 	"net"
      9 
     10 	"github.com/btcsuite/btcd/btcec/v2"
     11 	"github.com/lightningnetwork/lnd/brontide"
     12 	"github.com/lightningnetwork/lnd/keychain"
     13 	"github.com/lightningnetwork/lnd/lnwire"
     14 	"github.com/lightningnetwork/lnd/tor"
     15 )
     16 
     17 const (
     18 	COMMANDO_CMD             = 0x4c4f
     19 	COMMANDO_REPLY_CONTINUES = 0x594b
     20 	COMMANDO_REPLY_TERM      = 0x594d
     21 )
     22 
     23 type CommandoMsg struct {
     24 	Rune      string
     25 	Method    string
     26 	Params    string
     27 	RequestId uint64
     28 }
     29 
     30 func NewCommandoMsg(token string, method string, params string) CommandoMsg {
     31 	return CommandoMsg{
     32 		Rune:   token,
     33 		Method: method,
     34 		Params: params,
     35 	}
     36 }
     37 
     38 // A compile time check to ensure Init implements the lnwire.Message
     39 // interface.
     40 
     41 func (msg *CommandoMsg) MsgType() lnwire.MessageType {
     42 	return COMMANDO_CMD
     43 }
     44 
     45 func (msg *CommandoMsg) Decode(reader io.Reader, size uint32) error {
     46 	return fmt.Errorf("implememt commando decode?")
     47 }
     48 
     49 func (msg *CommandoMsg) Encode(buf *bytes.Buffer, pver uint32) error {
     50 	if err := lnwire.WriteUint64(buf, msg.RequestId); err != nil {
     51 		return err
     52 	}
     53 
     54 	buf.WriteString("{\"method\": \"")
     55 	buf.WriteString(msg.Method)
     56 	buf.WriteString("\",\"params\":")
     57 	buf.WriteString(msg.Params)
     58 	buf.WriteString(",\"rune\":\"")
     59 	buf.WriteString(msg.Rune)
     60 	buf.WriteString("\"}")
     61 
     62 	return nil
     63 }
     64 
     65 type LNSocket struct {
     66 	Conn        net.Conn
     67 	PrivKeyECDH *keychain.PrivKeyECDH
     68 }
     69 
     70 func (ln *LNSocket) GenKey() {
     71 	remotePriv, _ := btcec.NewPrivateKey()
     72 	ln.PrivKeyECDH = &keychain.PrivKeyECDH{PrivKey: remotePriv}
     73 }
     74 
     75 func (ln *LNSocket) ConnectWith(netAddr *lnwire.NetAddress) error {
     76 	conn, err := brontide.Dial(ln.PrivKeyECDH, netAddr, tor.DefaultConnTimeout, net.DialTimeout)
     77 	ln.Conn = conn
     78 	return err
     79 }
     80 
     81 func (ln *LNSocket) Connect(hostname string, pubkey string) error {
     82 	addr, err := net.ResolveTCPAddr("tcp", hostname)
     83 	if err != nil {
     84 		return err
     85 	}
     86 	bytes, err := hex.DecodeString(pubkey)
     87 	if err != nil {
     88 		return err
     89 	}
     90 	key, err := btcec.ParsePubKey(bytes)
     91 	if err != nil {
     92 		return err
     93 	}
     94 
     95 	netAddr := &lnwire.NetAddress{
     96 		IdentityKey: key,
     97 		Address:     addr,
     98 	}
     99 
    100 	return ln.ConnectWith(netAddr)
    101 }
    102 
    103 func (ln *LNSocket) PerformInit() error {
    104 	no_features := lnwire.NewRawFeatureVector()
    105 	init_reply_msg := lnwire.NewInitMessage(no_features, no_features)
    106 
    107 	var b bytes.Buffer
    108 	_, err := lnwire.WriteMessage(&b, init_reply_msg, 0)
    109 	if err != nil {
    110 		return err
    111 	}
    112 	_, err = ln.Conn.Write(b.Bytes())
    113 
    114 	// receive the first init msg
    115 	_, _, err = ln.Recv()
    116 	if err != nil {
    117 		return err
    118 	}
    119 
    120 	return nil
    121 }
    122 
    123 func (ln *LNSocket) Rpc(token string, method string, params string) (string, error) {
    124 	commando_msg := NewCommandoMsg(token, method, params)
    125 
    126 	var b bytes.Buffer
    127 	_, err := lnwire.WriteMessage(&b, &commando_msg, 0)
    128 	if err != nil {
    129 		return "", err
    130 	}
    131 
    132 	bs := b.Bytes()
    133 	_, err = ln.Conn.Write(bs)
    134 	if err != nil {
    135 		return "", err
    136 	}
    137 
    138 	return ln.rpcReadAll()
    139 }
    140 
    141 func ParseMsgType(bytes []byte) uint16 {
    142 	return uint16(bytes[0])<<8 | uint16(bytes[1])
    143 }
    144 
    145 func (ln *LNSocket) Recv() (uint16, []byte, error) {
    146 	res := make([]byte, 65535)
    147 	n, err := ln.Conn.Read(res)
    148 	if err != nil {
    149 		return 0, nil, err
    150 	}
    151 	if n < 2 {
    152 		return 0, nil, fmt.Errorf("read too small")
    153 	}
    154 	res = res[:n]
    155 	msgtype := ParseMsgType(res)
    156 	return msgtype, res[2:], nil
    157 }
    158 
    159 func (ln *LNSocket) rpcReadAll() (string, error) {
    160 	all := []byte{}
    161 	for {
    162 		msgtype, res, err := ln.Recv()
    163 		if err != nil {
    164 			return "", err
    165 		}
    166 		switch msgtype {
    167 		case COMMANDO_REPLY_CONTINUES:
    168 			all = append(all, res[8:]...)
    169 			continue
    170 		case COMMANDO_REPLY_TERM:
    171 			all = append(all, res[8:]...)
    172 			return string(all), nil
    173 		default:
    174 			continue
    175 		}
    176 	}
    177 }
    178 
    179 func (ln *LNSocket) Disconnect() {
    180 	ln.Conn.Close()
    181 }
    182 
    183 func (ln *LNSocket) ConnectAndInit(hostname string, pubkey string) error {
    184 	err := ln.Connect(hostname, pubkey)
    185 	if err != nil {
    186 		return err
    187 	}
    188 
    189 	return ln.PerformInit()
    190 }