diff --git a/app/lnd/methods/connectpeer.js b/app/lnd/methods/connectpeer.js new file mode 100644 index 00000000..33608fc2 --- /dev/null +++ b/app/lnd/methods/connectpeer.js @@ -0,0 +1,10 @@ +// LND Connect to a peer +export default function connectpeer(lnd, { pubkey, host }) { + return new Promise((resolve, reject) => { + lnd.connectPeer({ addr: { pubkey, host }, perm: true }, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} diff --git a/app/lnd/methods/disconnectpeer.js b/app/lnd/methods/disconnectpeer.js new file mode 100644 index 00000000..e6a4bc88 --- /dev/null +++ b/app/lnd/methods/disconnectpeer.js @@ -0,0 +1,10 @@ +// LND Disconnect from a peer +export default function disconnectpeer(lnd, { pubkey }) { + return new Promise((resolve, reject) => { + lnd.disconnectPeer({ pub_key: pubkey }, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} diff --git a/app/lnd/methods/index.js b/app/lnd/methods/index.js index d047f041..d41cd17d 100644 --- a/app/lnd/methods/index.js +++ b/app/lnd/methods/index.js @@ -1,10 +1,13 @@ import allchannels from './allchannels' import channelbalance from './channelbalance' import channels from './channels' +import connectpeer from './connectpeer' import createinvoice from './createinvoice' +import disconnectpeer from './disconnectpeer' import info from './info' import invoice from './invoice' import invoices from './invoices' +import openchannel from './openchannel' import payinvoice from './payinvoice' import payments from './payments' import peers from './peers' @@ -56,18 +59,48 @@ export default function(lnd, event, msg, data) { break case 'createInvoice': // Invoice looks like { r_hash: Buffer, payment_request: '' } - const { memo, value } = data - createInvoice(lnd, { memo, value }) + // { memo, value } = data + createInvoice(lnd, data) .then(invoice => event.sender.send('createdInvoice', Object.assign(invoice, { memo, value, r_hash: new Buffer(invoice.r_hash,'hex').toString('hex') }))) .catch(error => console.log('createInvoice error: ', error)) break case 'sendPayment': // Payment looks like { payment_preimage: Buffer, payment_route: Object } - const { paymentRequest } = data - sendPayment(lnd, { paymentRequest }) + // { paymentRequest } = data + sendPayment(lnd, data) .then(payment => event.sender.send('paymentSuccessful')) .catch(error => console.log('sendPayment error: ', error)) break + case 'openChannel': + // Response is empty. Streaming updates on channel status and updates + // { pubkey, localamt, pushamt } = data + openchannel(lnd, event, data) + .then(channel => { + console.log('CHANNEL: ', channel) + event.sender.send('channelSuccessful', { channel }) + }) + .catch(error => console.log('openChannel error: ', error)) + break + case 'connectPeer': + // Returns a peer_id. Pass the pubkey, host and peer_id so we can add a new peer to the list + // { pubkey, host } = data + connectpeer(lnd, data) + .then(({ peer_id }) => { + console.log('peer_id: ', peer_id) + event.sender.send('connectSuccess', { pub_key: data.pubkey, address: data.host, peer_id }) + }) + .catch(error => console.log('connectPeer error: ', error)) + break + case 'disconnectPeer': + // Empty response. Pass back pubkey on success to remove it from the peers list + // { pubkey } = data + disconnectpeer(lnd, data) + .then(() => { + console.log('pubkey: ', data.pubkey) + event.sender.send('disconnectSuccess', { pubkey: data.pubkey }) + }) + .catch(error => console.log('disconnectPeer error: ', error)) + break default: return } diff --git a/app/lnd/methods/openchannel.js b/app/lnd/methods/openchannel.js new file mode 100644 index 00000000..a75f7400 --- /dev/null +++ b/app/lnd/methods/openchannel.js @@ -0,0 +1,18 @@ +import pushchannel from '../push/channel' +import bitcore from 'bitcore-lib' +const BufferUtil = bitcore.util.buffer + +export default function openchannel(lnd, event, data) { + const { pubkey, localamt, pushamt } = data + const payload = { + node_pubkey: BufferUtil.hexToBuffer(pubkey), + local_funding_amount: Number(localamt), + push_sat: Number(pushamt) + } + + return new Promise((resolve, reject) => + pushchannel(lnd, event, payload) + .then(data => resolve(data)) + .catch(error => reject(err)) + ) +} \ No newline at end of file diff --git a/app/lnd/push/channel.js b/app/lnd/push/channel.js new file mode 100644 index 00000000..7b48da38 --- /dev/null +++ b/app/lnd/push/channel.js @@ -0,0 +1,16 @@ +export default function pushchannel(lnd, event, payload) { + return new Promise((resolve, reject) => { + try { + const call = lnd.openChannel(payload) + + call.on('data', data => event.sender.send('pushchannelupdated', { data })) + call.on('end', () => event.sender.send('pushchannelend')) + call.on('error', error => event.sender.send('pushchannelerror', { error })) + call.on('status', status => event.sender.send('pushchannelstatus', { status })) + + resolve(null, payload) + } catch (error) { + reject(error, null) + } + }) +} \ No newline at end of file diff --git a/app/reducers/balance.js b/app/reducers/balance.js index 8609f2b3..ab5a3a13 100644 --- a/app/reducers/balance.js +++ b/app/reducers/balance.js @@ -1,5 +1,4 @@ import { ipcRenderer } from 'electron' -import { callApis } from '../api' // ------------------------------------ // Constants // ------------------------------------ diff --git a/app/reducers/channels.js b/app/reducers/channels.js index 33cbd5f3..898876f6 100644 --- a/app/reducers/channels.js +++ b/app/reducers/channels.js @@ -1,6 +1,5 @@ import { createSelector } from 'reselect' import { ipcRenderer } from 'electron' -import { callApi, callApis } from '../api' // ------------------------------------ // Constants // ------------------------------------ @@ -66,18 +65,36 @@ export const fetchChannels = () => async (dispatch) => { // Receive IPC event for channels export const receiveChannels = (event, { channels, pendingChannels }) => dispatch => dispatch({ type: RECEIVE_CHANNELS, channels, pendingChannels }) -export const openChannel = ({ pubkey, localamt, pushamt }) => async (dispatch) => { - const payload = { pubkey, localamt, pushamt } +// Send IPC event for opening a channel +export const openChannel = ({ pubkey, localamt, pushamt }) => dispatch => { dispatch(openingChannel()) - const channel = await callApi('addchannel', 'post', payload) + ipcRenderer.send('lnd', { msg: 'openChannel', data: { pubkey, localamt, pushamt } }) +} - if (channel.data) { - dispatch(openingSuccessful()) - } else { - dispatch(openingFailure()) - } +// TODO: Decide how to handle streamed updates for channels +// Receive IPC event for openChannel +export const channelSuccessful = (event, { channel }) => dispatch => { + dispatch(fetchChannels()) +} + +// Receive IPC event for updated channel +export const pushchannelupdated = (event, data) => dispatch => { + dispatch(fetchChannels()) +} + +// Receive IPC event for channel end +export const pushchannelend = (event, data) => dispatch => { + dispatch(fetchChannels()) +} + +// Receive IPC event for channel error +export const pushchannelerror = (event, data) => dispatch => { + dispatch(fetchChannels()) +} - return channel +// Receive IPC event for channel status +export const pushchannelstatus = (event, data) => dispatch => { + dispatch(fetchChannels()) } // ------------------------------------ diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index bea6c641..4980ed05 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -1,7 +1,14 @@ import createIpc from 'redux-electron-ipc' import { receiveInfo } from './info' -import { receivePeers } from './peers' -import { receiveChannels } from './channels' +import { receivePeers, connectSuccess, disconnectSuccess } from './peers' +import { + receiveChannels, + channelSuccessful, + pushchannelupdated, + pushchannelend, + pushchannelerror, + pushchannelstatus +} from './channels' import { receivePayments, paymentSuccessful } from './payment' import { receiveInvoices, createdInvoice, receiveFormInvoice } from './invoice' import { receiveBalance } from './balance' @@ -16,7 +23,14 @@ const ipc = createIpc({ 'receiveInvoice': receiveFormInvoice, 'receiveBalance': receiveBalance, 'createdInvoice': createdInvoice, - 'paymentSuccessful': paymentSuccessful + 'paymentSuccessful': paymentSuccessful, + 'channelSuccessful': channelSuccessful, + 'pushchannelupdated': pushchannelupdated, + 'pushchannelend': pushchannelend, + 'pushchannelerror': pushchannelerror, + 'pushchannelstatus': pushchannelstatus, + 'connectSuccess': connectSuccess, + 'disconnectSuccess': disconnectSuccess }) export default ipc \ No newline at end of file diff --git a/app/reducers/payment.js b/app/reducers/payment.js index 547d5ffa..99378969 100644 --- a/app/reducers/payment.js +++ b/app/reducers/payment.js @@ -58,19 +58,6 @@ export const fetchPayments = () => dispatch => { // Receive IPC event for payments export const receivePayments = (event, { payments }) => dispatch => dispatch({ type: RECEIVE_PAYMENTS, payments }) -// export const payInvoice = payment_request => async (dispatch) => { -// dispatch(sendPayment()) -// const payment = await callApi('sendpayment', 'post', { payment_request }) - -// if (payment) { -// dispatch(fetchPayments()) -// } else { -// dispatch(paymentFailed()) -// } - -// return payment -// } - export const payInvoice = paymentRequest => dispatch => { dispatch(sendPayment()) ipcRenderer.send('lnd', { msg: 'sendPayment', data: { paymentRequest } }) diff --git a/app/reducers/peers.js b/app/reducers/peers.js index b2fb2c59..f75e8505 100644 --- a/app/reducers/peers.js +++ b/app/reducers/peers.js @@ -1,6 +1,5 @@ import { createSelector } from 'reselect' import { ipcRenderer } from 'electron' -import { callApi } from '../api' // ------------------------------------ // Constants // ------------------------------------ @@ -28,13 +27,6 @@ export function connectPeer() { } } -export function connectSuccess(peer) { - return { - type: CONNECT_SUCCESS, - peer - } -} - export function connectFailure() { return { type: CONNECT_FAILURE @@ -47,13 +39,6 @@ export function disconnectPeer() { } } -export function disconnectSuccess(pubkey) { - return { - type: DISCONNECT_SUCCESS, - pubkey - } -} - export function disconnectFailure() { return { type: DISCONNECT_FAILURE @@ -89,40 +74,34 @@ export const fetchPeers = () => async (dispatch) => { // Receive IPC event for peers export const receivePeers = (event, { peers }) => dispatch => dispatch({ type: RECEIVE_PEERS, peers }) -export const connectRequest = ({ pubkey, host }) => async (dispatch) => { +// Send IPC event for connecting to a peer +export const connectRequest = ({ pubkey, host }) => dispatch => { dispatch(connectPeer()) - const success = await callApi('connect', 'post', { pubkey, host }) - if (success.data) { - dispatch(connectSuccess({ pub_key: pubkey, address: host, peer_id: success.data.peer_id })) - } else { - dispatch(connectFailure()) - } - - return success + ipcRenderer.send('lnd', { msg: 'connectPeer', data: { pubkey, host } }) } -export const disconnectRequest = ({ pubkey }) => async (dispatch) => { - dispatch(disconnectPeer()) - const success = await callApi('disconnect', 'post', { pubkey }) - if (success) { - dispatch(disconnectSuccess(pubkey)) - } else { - dispatch(disconnectFailure()) - } +// Send IPC receive for successfully connecting to a peer +export const connectSuccess = (event, peer) => dispatch => dispatch({ type: CONNECT_SUCCESS, peer }) - return success +// Send IPC send for disconnecting from a peer +export const disconnectRequest = ({ pubkey }) => dispatch => { + dispatch(disconnectPeer()) + ipcRenderer.send('lnd', { msg: 'disconnectPeer', data: { pubkey } }) } +// Send IPC receive for successfully disconnecting from a peer +export const disconnectSuccess = (event, { pubkey }) => dispatch => dispatch({ type: DISCONNECT_SUCCESS, pubkey }) + // ------------------------------------ // Action Handlers // ------------------------------------ const ACTION_HANDLERS = { [DISCONNECT_PEER]: state => ({ ...state, disconnecting: true }), - [DISCONNECT_SUCCESS]: (state, { pubkey }) => ({ ...state, disconnecting: false, peers: state.peers.filter(peer => peer.pub_key !== pubkey) }), + [DISCONNECT_SUCCESS]: (state, { pubkey }) => ({ ...state, disconnecting: false, peer: null, peers: state.peers.filter(peer => peer.pub_key !== pubkey) }), [DISCONNECT_FAILURE]: state => ({ ...state, disconnecting: false }), [CONNECT_PEER]: state => ({ ...state, connecting: true }), - [CONNECT_SUCCESS]: (state, { peer }) => ({ ...state, connecting: false, peers: [...state.peers, peer] }), + [CONNECT_SUCCESS]: (state, { peer }) => ({ ...state, connecting: false, peerForm: { pubkey: '', host: '', isOpen: false }, peers: [...state.peers, peer] }), [CONNECT_FAILURE]: state => ({ ...state, connecting: false }), [SET_PEER_FORM]: (state, { form }) => ({ ...state, peerForm: Object.assign({}, state.peerForm, form) }), diff --git a/app/routes/app/components/App.js b/app/routes/app/components/App.js index 61eed04c..8c06d483 100644 --- a/app/routes/app/components/App.js +++ b/app/routes/app/components/App.js @@ -3,7 +3,6 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import Form from './components/Form' import Nav from './components/Nav' -import Socket from './components/Socket' import styles from './App.scss' class App extends Component { @@ -62,8 +61,6 @@ class App extends Component {