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 {
{children}
- - ) } diff --git a/app/routes/app/components/components/Socket.js b/app/routes/app/components/components/Socket.js deleted file mode 100644 index 68113f28..00000000 --- a/app/routes/app/components/components/Socket.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import Websocket from 'react-websocket' - -const Socket = ({ fetchChannels }) => { - const onMessage = () => { - // TODO: Assumes only socket relationship is with channels. Actually flesh out socket logic - fetchChannels() - } - - return ( - - ) -} - -Socket.propTypes = { - fetchChannels: PropTypes.func.isRequired -} - -export default Socket diff --git a/app/routes/wallet/components/components/Channels/components/ChannelForm/ChannelForm.js b/app/routes/wallet/components/components/Channels/components/ChannelForm/ChannelForm.js index 88d6d076..01329542 100644 --- a/app/routes/wallet/components/components/Channels/components/ChannelForm/ChannelForm.js +++ b/app/routes/wallet/components/components/Channels/components/ChannelForm/ChannelForm.js @@ -12,9 +12,9 @@ const ChannelForm = ({ form, setForm, ticker, peers, openChannel }) => { const localamt = ticker.currency === 'btc' ? btc.btcToSatoshis(local_amt) : btc.btcToSatoshis(usd.usdToBtc(local_amt, ticker.btcTicker.price_usd)) const pushamt = ticker.currency === 'btc' ? btc.btcToSatoshis(push_amt) : btc.btcToSatoshis(usd.usdToBtc(push_amt, ticker.btcTicker.price_usd)) - openChannel({ pubkey: node_key, localamt, pushamt }).then((channel) => { - if (channel.data) { setForm({ isOpen: false }) } - }) + openChannel({ pubkey: node_key, localamt, pushamt }) + setForm({ isOpen: false }) + } const customStyles = { diff --git a/app/routes/wallet/components/components/Peers/components/PeerForm/PeerForm.js b/app/routes/wallet/components/components/Peers/components/PeerForm/PeerForm.js index adcbd07c..80a092d6 100644 --- a/app/routes/wallet/components/components/Peers/components/PeerForm/PeerForm.js +++ b/app/routes/wallet/components/components/Peers/components/PeerForm/PeerForm.js @@ -6,10 +6,7 @@ import styles from './PeerForm.scss' const PeerForm = ({ form, setForm, connect }) => { const submit = () => { const { pubkey, host } = form - - connect({ pubkey, host }).then((success) => { - if (success.data) { setForm({ isOpen: false }) } - }) + connect({ pubkey, host }) } const customStyles = { diff --git a/app/routes/wallet/components/components/Peers/components/PeerModal/PeerModal.js b/app/routes/wallet/components/components/Peers/components/PeerModal/PeerModal.js index 2c3b0724..ed578209 100644 --- a/app/routes/wallet/components/components/Peers/components/PeerModal/PeerModal.js +++ b/app/routes/wallet/components/components/Peers/components/PeerModal/PeerModal.js @@ -4,11 +4,6 @@ import ReactModal from 'react-modal' import styles from './PeerModal.scss' const PeerModal = ({ isOpen, resetPeer, peer, disconnect }) => { - const disconnectClicked = () => { - disconnect({ pubkey: peer.pub_key }) - .then(success => (success ? resetPeer(null) : null)) - } - const customStyles = { overlay: { cursor: 'pointer', @@ -55,7 +50,7 @@ const PeerModal = ({ isOpen, resetPeer, peer, disconnect }) => {
{peer.bytes_sent}
-
+
disconnect({ pubkey: peer.pub_key })}>
Disconnect peer
diff --git a/package.json b/package.json index 6994a903..74676645 100644 --- a/package.json +++ b/package.json @@ -184,6 +184,7 @@ }, "dependencies": { "axios": "^0.16.2", + "bitcore-lib": "^0.14.0", "devtron": "^1.4.0", "electron-debug": "^1.2.0", "font-awesome": "^4.7.0", diff --git a/yarn.lock b/yarn.lock index daedb418..53e25b8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1485,6 +1485,17 @@ binary-extensions@^1.0.0: buffers "~0.1.1" chainsaw "~0.1.0" +bitcore-lib@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/bitcore-lib/-/bitcore-lib-0.14.0.tgz#21cb2359fe7b997a3b7b773eb7d7275ae37d644e" + dependencies: + bn.js "=2.0.4" + bs58 "=2.0.0" + buffer-compare "=1.0.0" + elliptic "=3.0.3" + inherits "=2.0.1" + lodash "=3.10.1" + bl@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.1.tgz#cac328f7bee45730d404b692203fcb590e172d5e" @@ -1507,6 +1518,14 @@ bluebird@^3.0.5, bluebird@^3.4.7, bluebird@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" +bn.js@=2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-2.0.4.tgz#220a7cd677f7f1bfa93627ff4193776fe7819480" + +bn.js@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-2.2.0.tgz#12162bc2ae71fc40a5626c33438f3a875cd37625" + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.6" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" @@ -1634,6 +1653,10 @@ browserslist@^1.1.1, browserslist@^1.1.3, browserslist@^1.3.6, browserslist@^1.5 caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" +bs58@=2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.0.tgz#72b713bed223a0ac518bbda0e3ce3f4817f39eb5" + bser@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.2.tgz#381116970b2a6deea5646dd15dd7278444b56169" @@ -1646,6 +1669,10 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" +buffer-compare@=1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-compare/-/buffer-compare-1.0.0.tgz#acaa7a966e98eee9fae14b31c39a5f158fb3c4a2" + buffer-crc32@^0.2.1: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -2969,6 +2996,15 @@ electron@^1.6.10: electron-download "^3.0.1" extract-zip "^1.0.3" +elliptic@=3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-3.0.3.tgz#865c9b420bfbe55006b9f969f97a0d2c44966595" + dependencies: + bn.js "^2.0.0" + brorand "^1.0.1" + hash.js "^1.0.0" + inherits "^2.0.1" + elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -4442,7 +4478,7 @@ inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, i version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" -inherits@2.0.1: +inherits@2.0.1, inherits@=2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" @@ -5553,6 +5589,10 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" +lodash@=3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" + lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1, lodash@^4.8.0, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"