Browse Source

Merge pull request #21 from LN-Zap/feature/close-channel

Feature/close channel
renovate/lint-staged-8.x
jackmallers 7 years ago
committed by GitHub
parent
commit
393e2a1fc9
  1. 2
      app/components/CryptoIcon/CryptoIcon.js
  2. 601
      app/lnd/config/rpc.proto
  3. 20
      app/lnd/methods/closechannel.js
  4. 11
      app/lnd/methods/index.js
  5. 4
      app/lnd/methods/openchannel.js
  6. 16
      app/lnd/push/closechannel.js
  7. 2
      app/lnd/push/openchannel.js
  8. 61
      app/reducers/channels.js
  9. 26
      app/reducers/ipc.js
  10. 3
      app/routes/wallet/components/Wallet.js
  11. 10
      app/routes/wallet/components/components/Channels/Channels.js
  12. 12
      app/routes/wallet/components/components/Channels/components/ChannelModal/ChannelModal.js
  13. 4
      app/routes/wallet/containers/WalletContainer.js
  14. 6
      test/reducers/__snapshots__/channels.spec.js.snap

2
app/components/CryptoIcon/CryptoIcon.js

@ -17,7 +17,7 @@ const CryptoIcon = ({ currency, styles }) => {
CryptoIcon.propTypes = { CryptoIcon.propTypes = {
currency: PropTypes.string.isRequired, currency: PropTypes.string.isRequired,
styles: PropTypes.object.isRequired styles: PropTypes.object
} }
export default CryptoIcon export default CryptoIcon

601
app/lnd/config/rpc.proto

File diff suppressed because it is too large

20
app/lnd/methods/closechannel.js

@ -0,0 +1,20 @@
import bitcore from 'bitcore-lib'
import pushclosechannel from '../push/closechannel'
const BufferUtil = bitcore.util.buffer
export default function closechannel(lnd, event, payload) {
const tx = payload.channel_point.funding_txid.match(/.{2}/g).reverse().join('')
const res = {
channel_point: {
funding_txid: BufferUtil.hexToBuffer(tx),
output_index: Number(payload.channel_point.output_index)
}
}
return new Promise((resolve, reject) =>
pushclosechannel(lnd, event, res)
.then(data => resolve(data))
.catch(error => reject(error))
)
}

11
app/lnd/methods/index.js

@ -2,6 +2,7 @@
import channelbalance from './channelbalance' import channelbalance from './channelbalance'
import channels from './channels' import channels from './channels'
import closechannel from './closechannel'
import connectpeer from './connectpeer' import connectpeer from './connectpeer'
import createinvoice from './createinvoice' import createinvoice from './createinvoice'
import disconnectpeer from './disconnectpeer' import disconnectpeer from './disconnectpeer'
@ -100,6 +101,16 @@ export default function (lnd, event, msg, data) {
}) })
.catch(error => console.log('openChannel error: ', error)) .catch(error => console.log('openChannel error: ', error))
break break
case 'closeChannel':
// Response is empty. Streaming updates on channel status and updates
// { channel_point, force } = data
closechannel(lnd, event, data)
.then((result) => {
console.log('CLOSE CHANNEL: ', result)
event.sender.send('closeChannelSuccessful')
})
.catch(error => console.log('closeChannel error: ', error))
break
case 'connectPeer': case 'connectPeer':
// Returns a peer_id. Pass the pubkey, host and peer_id so we can add a new peer to the list // Returns a peer_id. Pass the pubkey, host and peer_id so we can add a new peer to the list
// { pubkey, host } = data // { pubkey, host } = data

4
app/lnd/methods/openchannel.js

@ -1,5 +1,5 @@
import bitcore from 'bitcore-lib' import bitcore from 'bitcore-lib'
import pushchannel from '../push/channel' import pushopenchannel from '../push/openchannel'
const BufferUtil = bitcore.util.buffer const BufferUtil = bitcore.util.buffer
@ -12,7 +12,7 @@ export default function openchannel(lnd, event, payload) {
} }
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
pushchannel(lnd, event, res) pushopenchannel(lnd, event, res)
.then(data => resolve(data)) .then(data => resolve(data))
.catch(error => reject(error)) .catch(error => reject(error))
) )

16
app/lnd/push/closechannel.js

@ -0,0 +1,16 @@
export default function pushclosechannel(lnd, event, payload) {
return new Promise((resolve, reject) => {
try {
const call = lnd.closeChannel(payload)
call.on('data', data => event.sender.send('pushclosechannelupdated', { data }))
call.on('end', () => event.sender.send('pushclosechannelend'))
call.on('error', error => event.sender.send('pushclosechannelerror', { error }))
call.on('status', status => event.sender.send('pushclosechannelstatus', { status }))
resolve(null, payload)
} catch (error) {
reject(error, null)
}
})
}

2
app/lnd/push/channel.js → app/lnd/push/openchannel.js

@ -1,4 +1,4 @@
export default function pushchannel(lnd, event, payload) { export default function pushopenchannel(lnd, event, payload) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
const call = lnd.openChannel(payload) const call = lnd.openChannel(payload)

61
app/reducers/channels.js

@ -14,6 +14,10 @@ export const OPENING_CHANNEL = 'OPENING_CHANNEL'
export const OPENING_SUCCESSFUL = 'OPENING_SUCCESSFUL' export const OPENING_SUCCESSFUL = 'OPENING_SUCCESSFUL'
export const OPENING_FAILURE = 'OPENING_FAILURE' export const OPENING_FAILURE = 'OPENING_FAILURE'
export const CLOSING_CHANNEL = 'CLOSING_CHANNEL'
export const CLOSING_SUCCESSFUL = 'CLOSING_SUCCESSFUL'
export const CLOSING_FAILURE = 'CLOSING_FAILURE'
// ------------------------------------ // ------------------------------------
// Actions // Actions
// ------------------------------------ // ------------------------------------
@ -44,6 +48,12 @@ export function openingChannel() {
} }
} }
export function closingChannel() {
return {
type: CLOSING_CHANNEL
}
}
export function openingSuccessful() { export function openingSuccessful() {
return { return {
type: OPENING_SUCCESSFUL type: OPENING_SUCCESSFUL
@ -97,6 +107,51 @@ export const pushchannelstatus = () => (dispatch) => {
dispatch(fetchChannels()) dispatch(fetchChannels())
} }
// Send IPC event for opening a channel
export const closeChannel = ({ channel_point }) => (dispatch) => {
dispatch(closingChannel())
const channelPoint = channel_point.split(':')
ipcRenderer.send(
'lnd',
{
msg: 'closeChannel',
data: {
channel_point: {
funding_txid: channelPoint[0],
output_index: channelPoint[1]
},
force: true
}
}
)
}
// TODO: Decide how to handle streamed updates for closing channels
// Receive IPC event for closeChannel
export const closeChannelSuccessful = () => (dispatch) => {
dispatch(fetchChannels())
}
// Receive IPC event for updated closing channel
export const pushclosechannelupdated = () => (dispatch) => {
dispatch(fetchChannels())
}
// Receive IPC event for closing channel end
export const pushclosechannelend = () => (dispatch) => {
dispatch(fetchChannels())
}
// Receive IPC event for closing channel error
export const pushclosechannelerror = () => (dispatch) => {
dispatch(fetchChannels())
}
// Receive IPC event for closing channel status
export const pushclosechannelstatus = () => (dispatch) => {
dispatch(fetchChannels())
}
// ------------------------------------ // ------------------------------------
// Action Handlers // Action Handlers
// ------------------------------------ // ------------------------------------
@ -112,7 +167,8 @@ const ACTION_HANDLERS = {
{ ...state, channelsLoading: false, channels, pendingChannels } { ...state, channelsLoading: false, channels, pendingChannels }
), ),
[OPENING_CHANNEL]: state => ({ ...state, openingChannel: true }) [OPENING_CHANNEL]: state => ({ ...state, openingChannel: true }),
[CLOSING_CHANNEL]: state => ({ ...state, closingChannel: true })
} }
const channelsSelectors = {} const channelsSelectors = {}
@ -158,7 +214,8 @@ const initialState = {
local_amt: '', local_amt: '',
push_amt: '' push_amt: ''
}, },
openingChannel: false openingChannel: false,
closingChannel: false
} }
export default function channelsReducer(state = initialState, action) { export default function channelsReducer(state = initialState, action) {

26
app/reducers/ipc.js

@ -5,11 +5,19 @@ import { receiveCryptocurrency } from './ticker'
import { receivePeers, connectSuccess, disconnectSuccess } from './peers' import { receivePeers, connectSuccess, disconnectSuccess } from './peers'
import { import {
receiveChannels, receiveChannels,
channelSuccessful, channelSuccessful,
pushchannelupdated, pushchannelupdated,
pushchannelend, pushchannelend,
pushchannelerror, pushchannelerror,
pushchannelstatus pushchannelstatus,
closeChannelSuccessful,
pushclosechannelupdated,
pushclosechannelend,
pushclosechannelerror,
pushclosechannelstatus
} from './channels' } from './channels'
import { receivePayments, paymentSuccessful } from './payment' import { receivePayments, paymentSuccessful } from './payment'
import { receiveInvoices, createdInvoice, receiveFormInvoice } from './invoice' import { receiveInvoices, createdInvoice, receiveFormInvoice } from './invoice'
@ -18,21 +26,35 @@ import { receiveBalance } from './balance'
// Import all receiving IPC event handlers and pass them into createIpc // Import all receiving IPC event handlers and pass them into createIpc
const ipc = createIpc({ const ipc = createIpc({
receiveInfo, receiveInfo,
receivePeers, receivePeers,
receiveChannels, receiveChannels,
receivePayments, receivePayments,
receiveInvoices, receiveInvoices,
receiveInvoice: receiveFormInvoice, receiveInvoice: receiveFormInvoice,
receiveBalance,
createdInvoice, createdInvoice,
receiveBalance,
paymentSuccessful, paymentSuccessful,
channelSuccessful, channelSuccessful,
pushchannelupdated, pushchannelupdated,
pushchannelend, pushchannelend,
pushchannelerror, pushchannelerror,
pushchannelstatus, pushchannelstatus,
closeChannelSuccessful,
pushclosechannelupdated,
pushclosechannelend,
pushclosechannelerror,
pushclosechannelstatus,
connectSuccess, connectSuccess,
disconnectSuccess, disconnectSuccess,
receiveAddress, receiveAddress,
receiveCryptocurrency receiveCryptocurrency
}) })

3
app/routes/wallet/components/Wallet.js

@ -31,6 +31,7 @@ class Wallet extends Component {
disconnectRequest, disconnectRequest,
allChannels, allChannels,
openChannel, openChannel,
closeChannel,
currentTicker, currentTicker,
explorerLinkBase explorerLinkBase
} = this.props } = this.props
@ -75,6 +76,7 @@ class Wallet extends Component {
channelForm={channelForm} channelForm={channelForm}
setChannelForm={setChannelForm} setChannelForm={setChannelForm}
openChannel={openChannel} openChannel={openChannel}
closeChannel={closeChannel}
currentTicker={currentTicker} currentTicker={currentTicker}
explorerLinkBase={explorerLinkBase} explorerLinkBase={explorerLinkBase}
/> />
@ -101,6 +103,7 @@ Wallet.propTypes = {
disconnectRequest: PropTypes.func.isRequired, disconnectRequest: PropTypes.func.isRequired,
allChannels: PropTypes.array.isRequired, allChannels: PropTypes.array.isRequired,
openChannel: PropTypes.func.isRequired, openChannel: PropTypes.func.isRequired,
closeChannel: PropTypes.func.isRequired,
newAddress: PropTypes.func.isRequired, newAddress: PropTypes.func.isRequired,
address: PropTypes.object.isRequired, address: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired, currentTicker: PropTypes.object.isRequired,

10
app/routes/wallet/components/components/Channels/Channels.js

@ -19,11 +19,18 @@ const Channels = ({
setChannelForm, setChannelForm,
allChannels, allChannels,
openChannel, openChannel,
closeChannel,
currentTicker, currentTicker,
explorerLinkBase explorerLinkBase
}) => ( }) => (
<div className={styles.channels}> <div className={styles.channels}>
<ChannelModal isOpen={channelModalOpen} resetChannel={setChannel} channel={modalChannel} explorerLinkBase={explorerLinkBase} /> <ChannelModal
isOpen={channelModalOpen}
resetChannel={setChannel}
channel={modalChannel}
explorerLinkBase={explorerLinkBase}
closeChannel={closeChannel}
/>
<ChannelForm form={channelForm} setForm={setChannelForm} ticker={ticker} peers={peers} openChannel={openChannel} currentTicker={currentTicker} /> <ChannelForm form={channelForm} setForm={setChannelForm} ticker={ticker} peers={peers} openChannel={openChannel} currentTicker={currentTicker} />
<div className={styles.header}> <div className={styles.header}>
<h3>Channels</h3> <h3>Channels</h3>
@ -88,6 +95,7 @@ Channels.propTypes = {
setChannelForm: PropTypes.func.isRequired, setChannelForm: PropTypes.func.isRequired,
allChannels: PropTypes.array.isRequired, allChannels: PropTypes.array.isRequired,
openChannel: PropTypes.func.isRequired, openChannel: PropTypes.func.isRequired,
closeChannel: PropTypes.func.isRequired,
currentTicker: PropTypes.object.isRequired, currentTicker: PropTypes.object.isRequired,
explorerLinkBase: PropTypes.string.isRequired explorerLinkBase: PropTypes.string.isRequired
} }

12
app/routes/wallet/components/components/Channels/components/ChannelModal/ChannelModal.js

@ -4,7 +4,7 @@ import PropTypes from 'prop-types'
import ReactModal from 'react-modal' import ReactModal from 'react-modal'
import styles from './ChannelModal.scss' import styles from './ChannelModal.scss'
const ChannelModal = ({ isOpen, resetChannel, channel, explorerLinkBase }) => { const ChannelModal = ({ isOpen, resetChannel, channel, explorerLinkBase, closeChannel }) => {
const customStyles = { const customStyles = {
overlay: { overlay: {
cursor: 'pointer', cursor: 'pointer',
@ -21,6 +21,11 @@ const ChannelModal = ({ isOpen, resetChannel, channel, explorerLinkBase }) => {
} }
} }
const closeChannelClicked = () => {
closeChannel({ channel_point: channel.channel_point })
resetChannel(null)
}
return ( return (
<ReactModal <ReactModal
isOpen={isOpen} isOpen={isOpen}
@ -71,7 +76,7 @@ const ChannelModal = ({ isOpen, resetChannel, channel, explorerLinkBase }) => {
<dd>{channel.num_updates}</dd> <dd>{channel.num_updates}</dd>
</dl> </dl>
</div> </div>
<div className={styles.close}> <div className={styles.close} onClick={closeChannelClicked}>
<div>Close channel</div> <div>Close channel</div>
</div> </div>
<footer className={styles.active}> <footer className={styles.active}>
@ -89,7 +94,8 @@ ChannelModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
resetChannel: PropTypes.func.isRequired, resetChannel: PropTypes.func.isRequired,
channel: PropTypes.object, channel: PropTypes.object,
explorerLinkBase: PropTypes.string.isRequired explorerLinkBase: PropTypes.string.isRequired,
closeChannel: PropTypes.func.isRequired
} }
export default ChannelModal export default ChannelModal

4
app/routes/wallet/containers/WalletContainer.js

@ -16,7 +16,8 @@ import {
setChannel, setChannel,
channelsSelectors, channelsSelectors,
setChannelForm, setChannelForm,
openChannel openChannel,
closeChannel
} from '../../../reducers/channels' } from '../../../reducers/channels'
import Wallet from '../components/Wallet' import Wallet from '../components/Wallet'
@ -32,6 +33,7 @@ const mapDispatchToProps = {
fetchPendingChannels, fetchPendingChannels,
setChannel, setChannel,
openChannel, openChannel,
closeChannel,
setPeerForm, setPeerForm,
setChannelForm setChannelForm

6
test/reducers/__snapshots__/channels.spec.js.snap

@ -11,6 +11,7 @@ Object {
}, },
"channels": Array [], "channels": Array [],
"channelsLoading": true, "channelsLoading": true,
"closingChannel": false,
"openingChannel": false, "openingChannel": false,
"pendingChannels": Object { "pendingChannels": Object {
"pending_closing_channels": Array [], "pending_closing_channels": Array [],
@ -32,6 +33,7 @@ Object {
}, },
"channels": Array [], "channels": Array [],
"channelsLoading": false, "channelsLoading": false,
"closingChannel": false,
"openingChannel": true, "openingChannel": true,
"pendingChannels": Object { "pendingChannels": Object {
"pending_closing_channels": Array [], "pending_closing_channels": Array [],
@ -56,6 +58,7 @@ Object {
2, 2,
], ],
"channelsLoading": false, "channelsLoading": false,
"closingChannel": false,
"openingChannel": false, "openingChannel": false,
"pendingChannels": Array [ "pendingChannels": Array [
3, 3,
@ -75,6 +78,7 @@ Object {
}, },
"channels": Array [], "channels": Array [],
"channelsLoading": false, "channelsLoading": false,
"closingChannel": false,
"openingChannel": false, "openingChannel": false,
"pendingChannels": Object { "pendingChannels": Object {
"pending_closing_channels": Array [], "pending_closing_channels": Array [],
@ -96,6 +100,7 @@ Object {
}, },
"channels": Array [], "channels": Array [],
"channelsLoading": false, "channelsLoading": false,
"closingChannel": false,
"openingChannel": false, "openingChannel": false,
"pendingChannels": Object { "pendingChannels": Object {
"pending_closing_channels": Array [], "pending_closing_channels": Array [],
@ -117,6 +122,7 @@ Object {
}, },
"channels": Array [], "channels": Array [],
"channelsLoading": false, "channelsLoading": false,
"closingChannel": false,
"openingChannel": false, "openingChannel": false,
"pendingChannels": Object { "pendingChannels": Object {
"pending_closing_channels": Array [], "pending_closing_channels": Array [],

Loading…
Cancel
Save