From a2e0f48bb753b523b8c4ab499d600a6e7c219b07 Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Fri, 20 Apr 2018 13:22:21 -0500 Subject: [PATCH 1/4] feature(close-channel): wire up close channel to UI --- app/components/Contacts/Network.js | 68 +++++++++++++++++-- app/components/Contacts/Network.scss | 82 +++++++++++++++++++++-- app/lnd/config/rpc.proto | 80 ++++++++++++++++++++-- app/lnd/lib/rpc.proto | 80 ++++++++++++++++++++-- app/lnd/methods/channelController.js | 9 +-- app/main.dev.js | 4 +- app/reducers/channels.js | 20 ++++-- app/routes/app/containers/AppContainer.js | 9 ++- app/rpc.proto | 80 ++++++++++++++++++++-- 9 files changed, 388 insertions(+), 44 deletions(-) diff --git a/app/components/Contacts/Network.js b/app/components/Contacts/Network.js index 8a77b7d6..fad6ee6e 100644 --- a/app/components/Contacts/Network.js +++ b/app/components/Contacts/Network.js @@ -7,6 +7,8 @@ import { btc } from 'utils' import plus from 'icons/plus.svg' import search from 'icons/search.svg' +import Value from 'components/Value' + import styles from './Network.scss' class Network extends Component { @@ -24,11 +26,13 @@ class Network extends Component { searchQuery, filterPulldown, filter, + selectedChannel, loadingChannelPubkeys // closingChannelIds }, currentChannels, balance, + ticker, currentTicker, nodes, @@ -42,7 +46,11 @@ class Network extends Component { updateChannelSearchQuery, - openContactModal + openContactModal, + + setSelectedChannel, + + closeChannel } = this.props @@ -73,6 +81,12 @@ class Network extends Component { }, 1000) } + // when the user clicks the action to close the channel + const removeClicked = (channel) => { + console.log('channel: ', channel) + closeChannel({ channel_point: channel.channel_point, chan_id: channel.chan_id, force: !channel.active }) + } + const displayNodeName = (channel) => { const node = find(nodes, n => channel.remote_pubkey === n.pub_key) @@ -166,12 +180,54 @@ class Network extends Component { { currentChannels.length > 0 && currentChannels.map((channelObj, index) => { const channel = Object.prototype.hasOwnProperty.call(channelObj, 'channel') ? channelObj.channel : channelObj + const pubkey = channel.remote_node_pub || channel.remote_pubkey + return ( -
  • openContactModal(channelObj)}> - {displayNodeName(channel)} - - - +
  • (selectedChannel === channel ? setSelectedChannel(null) : setSelectedChannel(channel))} + > +
    + {displayNodeName(channel)} + + + +
    + +
    +

    {`${pubkey.substring(0, 30)}...`}

    + +
    +
    +
    Pay Limit
    +

    + + {ticker.currency.toUpperCase()} +

    +
    +
    +
    Request Limit
    +

    + + {ticker.currency.toUpperCase()} +

    +
    +
    +
    +
    removeClicked(channel)}> +
    Disconnect
    +
    +
    +
  • ) }) diff --git a/app/components/Contacts/Network.scss b/app/components/Contacts/Network.scss index 03dd86de..2fe7a966 100644 --- a/app/components/Contacts/Network.scss +++ b/app/components/Contacts/Network.scss @@ -48,7 +48,7 @@ } .channels { - padding: 20px 0px 20px 20px; + padding: 20px 0px 20px 0px; overflow-y: auto; .listHeader { @@ -57,7 +57,7 @@ flex-direction: row; justify-content: space-between; align-items: baseline; - padding-right: 20px; + padding: 0 20px; h2, h2 span { color: $white; @@ -119,17 +119,35 @@ } .channel { - display: flex; - flex-direction: row; - justify-content: space-between; color: $white; - padding: 10px 20px 10px 0px; + padding: 10px 0px 10px 0px; margin: 10px 0; cursor: pointer; + &:hover { + background: #272B37; + } + + &.selectedChannel { + background: #272B37; + padding-bottom: 0; + + .channelDetails { + max-height: 500px; + } + } + span:nth-child(1) { font-size: 12px; - } + } + + .channelTitle { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 0 20px; + } .online { color: $green; @@ -233,4 +251,54 @@ 100% { transform: rotate(360deg); } +} + +.channelDetails { + overflow-y: hidden; + max-height: 0; + transition: all 0.25s; + + h4 { + color: $white; + opacity: 0.5; + font-size: 9px; + margin-top: 10px; + overflow-x: hidden; + padding: 0 20px; + letter-spacing: 1.1px; + } + + .limits { + display: flex; + flex-direction: row; + justify-content: space-around; + border-top: 0.5px solid #1A1C23; + margin-top: 10px; + padding: 20px; + + h5 { + font-size: 12px; + color: $white; + margin-bottom: 10px; + font-weight: 600; + } + + p { + color: $white; + opacity: 0.5; + font-size: 8px; + text-align: center; + } + } + + .actions { + text-align: center; + font-size: 12px; + color: #FF7686; + + section { + padding: 20px 0; + border-top: 0.5px solid #1A1C23; + } + } } \ No newline at end of file diff --git a/app/lnd/config/rpc.proto b/app/lnd/config/rpc.proto index 541a2d94..d4e7e9c6 100644 --- a/app/lnd/config/rpc.proto +++ b/app/lnd/config/rpc.proto @@ -44,7 +44,7 @@ service WalletUnlocker { }; } - /** lncli: `init` + /** InitWallet is used when lnd is starting up for the first time to fully initialize the daemon and its internal wallet. At the very least a wallet password must be provided. This will be used to encrypt sensitive material @@ -145,8 +145,7 @@ service Lightning { /** lncli: `walletbalance` WalletBalance returns total unspent outputs(confirmed and unconfirmed), all confirmed unspent outputs and all unconfirmed unspent outputs under control - by the wallet. This method can be modified by having the request specify - only witness outputs should be factored into the final output sum. + of the wallet. */ rpc WalletBalance (WalletBalanceRequest) returns (WalletBalanceResponse) { option (google.api.http) = { @@ -531,6 +530,25 @@ service Lightning { body: "*" }; } + + /** lncli: `fwdinghistory` + ForwardingHistory allows the caller to query the htlcswitch for a record of + all HTLC's forwarded within the target time range, and integer offset + within that time range. If no time-range is specified, then the first chunk + of the past 24 hrs of forwarding history are returned. + + A list of forwarding events are returned. The size of each forwarding event + is 40 bytes, and the max message size able to be returned in gRPC is 4 MiB. + As a result each message can only contain 50k entries. Each response has + the index offset of the last entry. The index offset can be provided to the + request to allow the caller to skip a series of records. + */ + rpc ForwardingHistory(ForwardingHistoryRequest) returns (ForwardingHistoryResponse) { + option (google.api.http) = { + post: "/v1/switch" + body: "*" + }; + }; } message Transaction { @@ -662,7 +680,6 @@ message NewAddressRequest { enum AddressType { WITNESS_PUBKEY_HASH = 0; NESTED_PUBKEY_HASH = 1; - PUBKEY_HASH = 2; } /// The address type @@ -1080,8 +1097,6 @@ message PendingChannelsResponse { } message WalletBalanceRequest { - /// If only witness outputs should be considered when calculating the wallet's balance - bool witness_only = 1; } message WalletBalanceResponse { /// The balance of the wallet @@ -1488,6 +1503,15 @@ message ChannelFeeReport { message FeeReportResponse { /// An array of channel fee reports which describes the current fee schedule for each channel. repeated ChannelFeeReport channel_fees = 1 [json_name = "channel_fees"]; + + /// The total amount of fee revenue (in satoshis) the switch has collected over the past 24 hrs. + uint64 day_fee_sum = 2 [json_name = "day_fee_sum"]; + + /// The total amount of fee revenue (in satoshis) the switch has collected over the past 1 week. + uint64 week_fee_sum = 3 [json_name = "week_fee_sum"]; + + /// The total amount of fee revenue (in satoshis) the switch has collected over the past 1 month. + uint64 month_fee_sum = 4 [json_name = "month_fee_sum"]; } message PolicyUpdateRequest { @@ -1509,4 +1533,48 @@ message PolicyUpdateRequest { uint32 time_lock_delta = 5 [json_name = "time_lock_delta"]; } message PolicyUpdateResponse { +} + +message ForwardingHistoryRequest { + /// Start time is the starting point of the forwarding history request. All records beyond this point will be included, respecting the end time, and the index offset. + uint64 start_time = 1 [json_name = "start_time"]; + + /// End time is the end point of the forwarding history request. The response will carry at most 50k records between the start time and the end time. The index offset can be used to implement pagination. + uint64 end_time = 2 [json_name = "end_time"]; + + /// Index offset is the offset in the time series to start at. As each response can only contain 50k records, callers can use this to skip around within a packed time series. + uint32 index_offset = 3 [json_name = "index_offset"]; + + /// The max number of events to return in the response to this query. + uint32 num_max_events = 4 [json_name = "num_max_events"]; +} +message ForwardingEvent { + /// Timestamp is the time (unix epoch offset) that this circuit was completed. + uint64 timestamp = 1 [json_name = "timestamp"]; + + /// The incoming channel ID that carried the HTLC that created the circuit. + uint64 chan_id_in = 2 [json_name = "chan_id_in"]; + + /// The outgoing channel ID that carried the preimage that completed the circuit. + uint64 chan_id_out = 4 [json_name = "chan_id_out"]; + + /// The total amount of the incoming HTLC that created half the circuit. + uint64 amt_in = 5 [json_name = "amt_in"]; + + /// The total amount of the outgoign HTLC that created the second half of the circuit. + uint64 amt_out = 6 [json_name = "amt_out"]; + + /// The total fee that this payment circuit carried. + uint64 fee = 7 [json_name = "fee"]; + + // TODO(roasbeef): add settlement latency? + // * use FPE on the chan id? + // * also list failures? +} +message ForwardingHistoryResponse { + /// A list of forwarding events from the time slice of the time series specified in the request. + repeated ForwardingEvent forwarding_events = 1 [json_name = "forwarding_events"]; + + /// The index of the last time in the set of returned forwarding events. Can be used to seek further, pagination style. + uint32 last_offset_index = 2 [json_name = "last_offset_index"]; } \ No newline at end of file diff --git a/app/lnd/lib/rpc.proto b/app/lnd/lib/rpc.proto index 541a2d94..d4e7e9c6 100644 --- a/app/lnd/lib/rpc.proto +++ b/app/lnd/lib/rpc.proto @@ -44,7 +44,7 @@ service WalletUnlocker { }; } - /** lncli: `init` + /** InitWallet is used when lnd is starting up for the first time to fully initialize the daemon and its internal wallet. At the very least a wallet password must be provided. This will be used to encrypt sensitive material @@ -145,8 +145,7 @@ service Lightning { /** lncli: `walletbalance` WalletBalance returns total unspent outputs(confirmed and unconfirmed), all confirmed unspent outputs and all unconfirmed unspent outputs under control - by the wallet. This method can be modified by having the request specify - only witness outputs should be factored into the final output sum. + of the wallet. */ rpc WalletBalance (WalletBalanceRequest) returns (WalletBalanceResponse) { option (google.api.http) = { @@ -531,6 +530,25 @@ service Lightning { body: "*" }; } + + /** lncli: `fwdinghistory` + ForwardingHistory allows the caller to query the htlcswitch for a record of + all HTLC's forwarded within the target time range, and integer offset + within that time range. If no time-range is specified, then the first chunk + of the past 24 hrs of forwarding history are returned. + + A list of forwarding events are returned. The size of each forwarding event + is 40 bytes, and the max message size able to be returned in gRPC is 4 MiB. + As a result each message can only contain 50k entries. Each response has + the index offset of the last entry. The index offset can be provided to the + request to allow the caller to skip a series of records. + */ + rpc ForwardingHistory(ForwardingHistoryRequest) returns (ForwardingHistoryResponse) { + option (google.api.http) = { + post: "/v1/switch" + body: "*" + }; + }; } message Transaction { @@ -662,7 +680,6 @@ message NewAddressRequest { enum AddressType { WITNESS_PUBKEY_HASH = 0; NESTED_PUBKEY_HASH = 1; - PUBKEY_HASH = 2; } /// The address type @@ -1080,8 +1097,6 @@ message PendingChannelsResponse { } message WalletBalanceRequest { - /// If only witness outputs should be considered when calculating the wallet's balance - bool witness_only = 1; } message WalletBalanceResponse { /// The balance of the wallet @@ -1488,6 +1503,15 @@ message ChannelFeeReport { message FeeReportResponse { /// An array of channel fee reports which describes the current fee schedule for each channel. repeated ChannelFeeReport channel_fees = 1 [json_name = "channel_fees"]; + + /// The total amount of fee revenue (in satoshis) the switch has collected over the past 24 hrs. + uint64 day_fee_sum = 2 [json_name = "day_fee_sum"]; + + /// The total amount of fee revenue (in satoshis) the switch has collected over the past 1 week. + uint64 week_fee_sum = 3 [json_name = "week_fee_sum"]; + + /// The total amount of fee revenue (in satoshis) the switch has collected over the past 1 month. + uint64 month_fee_sum = 4 [json_name = "month_fee_sum"]; } message PolicyUpdateRequest { @@ -1509,4 +1533,48 @@ message PolicyUpdateRequest { uint32 time_lock_delta = 5 [json_name = "time_lock_delta"]; } message PolicyUpdateResponse { +} + +message ForwardingHistoryRequest { + /// Start time is the starting point of the forwarding history request. All records beyond this point will be included, respecting the end time, and the index offset. + uint64 start_time = 1 [json_name = "start_time"]; + + /// End time is the end point of the forwarding history request. The response will carry at most 50k records between the start time and the end time. The index offset can be used to implement pagination. + uint64 end_time = 2 [json_name = "end_time"]; + + /// Index offset is the offset in the time series to start at. As each response can only contain 50k records, callers can use this to skip around within a packed time series. + uint32 index_offset = 3 [json_name = "index_offset"]; + + /// The max number of events to return in the response to this query. + uint32 num_max_events = 4 [json_name = "num_max_events"]; +} +message ForwardingEvent { + /// Timestamp is the time (unix epoch offset) that this circuit was completed. + uint64 timestamp = 1 [json_name = "timestamp"]; + + /// The incoming channel ID that carried the HTLC that created the circuit. + uint64 chan_id_in = 2 [json_name = "chan_id_in"]; + + /// The outgoing channel ID that carried the preimage that completed the circuit. + uint64 chan_id_out = 4 [json_name = "chan_id_out"]; + + /// The total amount of the incoming HTLC that created half the circuit. + uint64 amt_in = 5 [json_name = "amt_in"]; + + /// The total amount of the outgoign HTLC that created the second half of the circuit. + uint64 amt_out = 6 [json_name = "amt_out"]; + + /// The total fee that this payment circuit carried. + uint64 fee = 7 [json_name = "fee"]; + + // TODO(roasbeef): add settlement latency? + // * use FPE on the chan id? + // * also list failures? +} +message ForwardingHistoryResponse { + /// A list of forwarding events from the time slice of the time series specified in the request. + repeated ForwardingEvent forwarding_events = 1 [json_name = "forwarding_events"]; + + /// The index of the last time in the set of returned forwarding events. Can be used to seek further, pagination style. + uint32 last_offset_index = 2 [json_name = "last_offset_index"]; } \ No newline at end of file diff --git a/app/lnd/methods/channelController.js b/app/lnd/methods/channelController.js index 9cb2c48f..0fb9f259 100644 --- a/app/lnd/methods/channelController.js +++ b/app/lnd/methods/channelController.js @@ -103,12 +103,13 @@ export function listChannels(lnd, meta) { * @return {[type]} [description] */ export function closeChannel(lnd, meta, event, payload) { - const { chan_id, force } = payload - const tx = payload.channel_point.funding_txid.match(/.{2}/g).reverse().join('') + const { channel_point: { funding_txid, output_index }, chan_id, force } = payload + const tx = funding_txid.match(/.{2}/g).reverse().join('') + const res = { channel_point: { - funding_txid: Buffer.from(tx, 'hex'), - output_index: Number(payload.channel_point.output_index) + funding_txid_bytes: Buffer.from(tx, 'hex'), + output_index: Number(output_index) }, force } diff --git a/app/main.dev.js b/app/main.dev.js index 240cd09c..698f0f71 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -164,9 +164,7 @@ const startLnd = (alias, autopilot) => { '--bitcoin.active', '--bitcoin.testnet', '--bitcoin.node=neutrino', - '--neutrino.connect=188.166.148.62:18333', - '--neutrino.addpeer=btcd.jackmallers.com:18333', - '--neutrino.addpeer=159.65.48.139:18333', + '--neutrino.connect=188.166.148.62', '--neutrino.connect=127.0.0.1:18333', '--debuglevel=debug', `${autopilot ? '--autopilot.active' : ''}`, diff --git a/app/reducers/channels.js b/app/reducers/channels.js index 5b315ee6..779a0bf9 100644 --- a/app/reducers/channels.js +++ b/app/reducers/channels.js @@ -38,6 +38,8 @@ export const REMOVE_ClOSING_CHAN_ID = 'REMOVE_ClOSING_CHAN_ID' export const OPEN_CONTACT_MODAL = 'OPEN_CONTACT_MODAL' export const CLOSE_CONTACT_MODAL = 'CLOSE_CONTACT_MODAL' +export const SET_SELECTED_CHANNEL = 'SET_SELECTED_CHANNEL' + // ------------------------------------ // Actions // ------------------------------------ @@ -141,6 +143,13 @@ export function closeContactModal() { } } +export function setSelectedChannel(selectedChannel) { + return { + type: SET_SELECTED_CHANNEL, + selectedChannel + } +} + // Send IPC event for peers export const fetchChannels = () => async (dispatch) => { dispatch(getChannels()) @@ -206,8 +215,7 @@ export const closeChannel = ({ channel_point, chan_id, force }) => (dispatch) => funding_txid, output_index }, - force, - chan_id + force } } ) @@ -334,7 +342,9 @@ const ACTION_HANDLERS = { ), [OPEN_CONTACT_MODAL]: (state, { channel }) => ({ ...state, contactModal: { isOpen: true, channel } }), - [CLOSE_CONTACT_MODAL]: state => ({ ...state, contactModal: { isOpen: false, channel: null } }) + [CLOSE_CONTACT_MODAL]: state => ({ ...state, contactModal: { isOpen: false, channel: null } }), + + [SET_SELECTED_CHANNEL]: (state, { selectedChannel }) => ({ ...state, selectedChannel }) } const channelsSelectors = {} @@ -522,7 +532,9 @@ const initialState = { contactModal: { isOpen: false, channel: null - } + }, + + selectedChannel: null } export default function channelsReducer(state = initialState, action) { diff --git a/app/routes/app/containers/AppContainer.js b/app/routes/app/containers/AppContainer.js index 200ecad5..5834927d 100644 --- a/app/routes/app/containers/AppContainer.js +++ b/app/routes/app/containers/AppContainer.js @@ -35,7 +35,8 @@ import { changeFilter, updateChannelSearchQuery, openContactModal, - closeContactModal + closeContactModal, + setSelectedChannel } from 'reducers/channels' import { @@ -112,6 +113,7 @@ const mapDispatchToProps = { updateChannelSearchQuery, openContactModal, closeContactModal, + setSelectedChannel, openContactsForm, closeContactsForm, @@ -302,6 +304,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { contactsform: stateProps.contactsform, nodes: stateProps.network.nodes, nonActiveFilters: stateProps.nonActiveFilters, + ticker: stateProps.ticker, fetchChannels: dispatchProps.fetchChannels, openContactsForm: dispatchProps.openContactsForm, @@ -310,7 +313,9 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { toggleFilterPulldown: dispatchProps.toggleFilterPulldown, changeFilter: dispatchProps.changeFilter, updateChannelSearchQuery: dispatchProps.updateChannelSearchQuery, - openContactModal: dispatchProps.openContactModal + openContactModal: dispatchProps.openContactModal, + setSelectedChannel: dispatchProps.setSelectedChannel, + closeChannel: dispatchProps.closeChannel } const contactsFormProps = { diff --git a/app/rpc.proto b/app/rpc.proto index 541a2d94..d4e7e9c6 100644 --- a/app/rpc.proto +++ b/app/rpc.proto @@ -44,7 +44,7 @@ service WalletUnlocker { }; } - /** lncli: `init` + /** InitWallet is used when lnd is starting up for the first time to fully initialize the daemon and its internal wallet. At the very least a wallet password must be provided. This will be used to encrypt sensitive material @@ -145,8 +145,7 @@ service Lightning { /** lncli: `walletbalance` WalletBalance returns total unspent outputs(confirmed and unconfirmed), all confirmed unspent outputs and all unconfirmed unspent outputs under control - by the wallet. This method can be modified by having the request specify - only witness outputs should be factored into the final output sum. + of the wallet. */ rpc WalletBalance (WalletBalanceRequest) returns (WalletBalanceResponse) { option (google.api.http) = { @@ -531,6 +530,25 @@ service Lightning { body: "*" }; } + + /** lncli: `fwdinghistory` + ForwardingHistory allows the caller to query the htlcswitch for a record of + all HTLC's forwarded within the target time range, and integer offset + within that time range. If no time-range is specified, then the first chunk + of the past 24 hrs of forwarding history are returned. + + A list of forwarding events are returned. The size of each forwarding event + is 40 bytes, and the max message size able to be returned in gRPC is 4 MiB. + As a result each message can only contain 50k entries. Each response has + the index offset of the last entry. The index offset can be provided to the + request to allow the caller to skip a series of records. + */ + rpc ForwardingHistory(ForwardingHistoryRequest) returns (ForwardingHistoryResponse) { + option (google.api.http) = { + post: "/v1/switch" + body: "*" + }; + }; } message Transaction { @@ -662,7 +680,6 @@ message NewAddressRequest { enum AddressType { WITNESS_PUBKEY_HASH = 0; NESTED_PUBKEY_HASH = 1; - PUBKEY_HASH = 2; } /// The address type @@ -1080,8 +1097,6 @@ message PendingChannelsResponse { } message WalletBalanceRequest { - /// If only witness outputs should be considered when calculating the wallet's balance - bool witness_only = 1; } message WalletBalanceResponse { /// The balance of the wallet @@ -1488,6 +1503,15 @@ message ChannelFeeReport { message FeeReportResponse { /// An array of channel fee reports which describes the current fee schedule for each channel. repeated ChannelFeeReport channel_fees = 1 [json_name = "channel_fees"]; + + /// The total amount of fee revenue (in satoshis) the switch has collected over the past 24 hrs. + uint64 day_fee_sum = 2 [json_name = "day_fee_sum"]; + + /// The total amount of fee revenue (in satoshis) the switch has collected over the past 1 week. + uint64 week_fee_sum = 3 [json_name = "week_fee_sum"]; + + /// The total amount of fee revenue (in satoshis) the switch has collected over the past 1 month. + uint64 month_fee_sum = 4 [json_name = "month_fee_sum"]; } message PolicyUpdateRequest { @@ -1509,4 +1533,48 @@ message PolicyUpdateRequest { uint32 time_lock_delta = 5 [json_name = "time_lock_delta"]; } message PolicyUpdateResponse { +} + +message ForwardingHistoryRequest { + /// Start time is the starting point of the forwarding history request. All records beyond this point will be included, respecting the end time, and the index offset. + uint64 start_time = 1 [json_name = "start_time"]; + + /// End time is the end point of the forwarding history request. The response will carry at most 50k records between the start time and the end time. The index offset can be used to implement pagination. + uint64 end_time = 2 [json_name = "end_time"]; + + /// Index offset is the offset in the time series to start at. As each response can only contain 50k records, callers can use this to skip around within a packed time series. + uint32 index_offset = 3 [json_name = "index_offset"]; + + /// The max number of events to return in the response to this query. + uint32 num_max_events = 4 [json_name = "num_max_events"]; +} +message ForwardingEvent { + /// Timestamp is the time (unix epoch offset) that this circuit was completed. + uint64 timestamp = 1 [json_name = "timestamp"]; + + /// The incoming channel ID that carried the HTLC that created the circuit. + uint64 chan_id_in = 2 [json_name = "chan_id_in"]; + + /// The outgoing channel ID that carried the preimage that completed the circuit. + uint64 chan_id_out = 4 [json_name = "chan_id_out"]; + + /// The total amount of the incoming HTLC that created half the circuit. + uint64 amt_in = 5 [json_name = "amt_in"]; + + /// The total amount of the outgoign HTLC that created the second half of the circuit. + uint64 amt_out = 6 [json_name = "amt_out"]; + + /// The total fee that this payment circuit carried. + uint64 fee = 7 [json_name = "fee"]; + + // TODO(roasbeef): add settlement latency? + // * use FPE on the chan id? + // * also list failures? +} +message ForwardingHistoryResponse { + /// A list of forwarding events from the time slice of the time series specified in the request. + repeated ForwardingEvent forwarding_events = 1 [json_name = "forwarding_events"]; + + /// The index of the last time in the set of returned forwarding events. Can be used to seek further, pagination style. + uint32 last_offset_index = 2 [json_name = "last_offset_index"]; } \ No newline at end of file From eca44c9b34557d4cd26d03f2278285911d77ec6c Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Fri, 20 Apr 2018 15:23:00 -0500 Subject: [PATCH 2/4] feature(close-channel): hook up new UI --- app/components/Contacts/Network.js | 46 ++++++++++++++++++++++------ app/components/Contacts/Network.scss | 42 ++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/app/components/Contacts/Network.js b/app/components/Contacts/Network.js index fad6ee6e..dc4b08bf 100644 --- a/app/components/Contacts/Network.js +++ b/app/components/Contacts/Network.js @@ -27,8 +27,8 @@ class Network extends Component { filterPulldown, filter, selectedChannel, - loadingChannelPubkeys - // closingChannelIds + loadingChannelPubkeys, + closingChannelIds }, currentChannels, balance, @@ -87,6 +87,11 @@ class Network extends Component { closeChannel({ channel_point: channel.channel_point, chan_id: channel.chan_id, force: !channel.active }) } + // when a user clicks a channel + const channelClicked = (channel) => { + selectedChannel === channel ? setSelectedChannel(null) : setSelectedChannel(channel) + } + const displayNodeName = (channel) => { const node = find(nodes, n => channel.remote_pubkey === n.pub_key) @@ -102,9 +107,13 @@ class Network extends Component { // if the channel has a closing tx that means it's closing if (Object.prototype.hasOwnProperty.call(channel, 'closing_txid')) { return 'closing' } + // if we are in the process of closing this channel + if (closingChannelIds.includes(channel.chan_id)) { return 'closing' } + // if the channel isn't active that means the remote peer isn't online if (!channel.active) { return 'offline' } + // if all of the above conditionals fail we can assume the node is online :) return 'online' } @@ -186,17 +195,29 @@ class Network extends Component {
  • (selectedChannel === channel ? setSelectedChannel(null) : setSelectedChannel(channel))} + onClick={() => channelClicked(channel)} >
    - {displayNodeName(channel)} - - + + { + closingChannelIds.includes(channel.chan_id) ? + + + + : + + } + {displayNodeName(channel)} + { + selectedChannel === channel && + }
    -

    {`${pubkey.substring(0, 30)}...`}

    +
    +

    {`${pubkey.substring(0, 30)}...`}

    +
    @@ -218,13 +239,20 @@ class Network extends Component { currency={ticker.currency} currentTicker={currentTicker} /> - {ticker.currency.toUpperCase()} + {ticker.currency.toUpperCase()}

    removeClicked(channel)}> -
    Disconnect
    + { + closingChannelIds.includes(channel.chan_id) ? + + Closing + + : +
    Disconnect
    + }
    diff --git a/app/components/Contacts/Network.scss b/app/components/Contacts/Network.scss index 2fe7a966..cbaecf96 100644 --- a/app/components/Contacts/Network.scss +++ b/app/components/Contacts/Network.scss @@ -145,8 +145,27 @@ display: flex; flex-direction: row; align-items: center; - justify-content: space-between; padding: 0 20px; + + span { + &:nth-child(2) { + margin-left: 10px; + font-size: 12px; + font-weight: 300; + letter-spacing: 0.6px; + line-height: 17px; + } + + &:nth-child(3) { + margin-left: auto; + + svg { + width: 15px; + height: 15px; + opacity: 0.5; + } + } + } } .online { @@ -227,6 +246,11 @@ -o-animation: animation-rotate 1000ms linear infinite; animation: animation-rotate 1000ms linear infinite; display: inline-block; + + &.closing { + border: 1px solid rgba(255, 85, 106, 0.1); + border-left-color: rgba(255, 85, 106, 0.4); + } } @-webkit-keyframes animation-rotate { @@ -299,6 +323,22 @@ section { padding: 20px 0; border-top: 0.5px solid #1A1C23; + + div { + transition: all 0.25s; + + &:hover { + opacity: 0.5; + } + } + } + + .loading i { + vertical-align: top; + + &:nth-child(1) { + margin-right: 5px; + } } } } \ No newline at end of file From 0ea5cc743e76d1771a62b47603d6a8fa7722c201 Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Fri, 20 Apr 2018 15:29:14 -0500 Subject: [PATCH 3/4] fix(lint/tests): fix lint errors and gen new snapshots --- app/components/Contacts/Network.js | 14 +++++++++----- app/routes/app/containers/AppContainer.js | 3 --- test/reducers/__snapshots__/channels.spec.js.snap | 5 +++++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/components/Contacts/Network.js b/app/components/Contacts/Network.js index dc4b08bf..77e64e6d 100644 --- a/app/components/Contacts/Network.js +++ b/app/components/Contacts/Network.js @@ -46,8 +46,6 @@ class Network extends Component { updateChannelSearchQuery, - openContactModal, - setSelectedChannel, closeChannel @@ -83,13 +81,17 @@ class Network extends Component { // when the user clicks the action to close the channel const removeClicked = (channel) => { - console.log('channel: ', channel) closeChannel({ channel_point: channel.channel_point, chan_id: channel.chan_id, force: !channel.active }) } // when a user clicks a channel const channelClicked = (channel) => { - selectedChannel === channel ? setSelectedChannel(null) : setSelectedChannel(channel) + // selectedChannel === channel ? setSelectedChannel(null) : setSelectedChannel(channel) + if (selectedChannel === channel) { + setSelectedChannel(null) + } else { + setSelectedChannel(channel) + } } const displayNodeName = (channel) => { @@ -289,13 +291,15 @@ Network.propTypes = { channels: PropTypes.object.isRequired, balance: PropTypes.object.isRequired, currentTicker: PropTypes.object.isRequired, + ticker: PropTypes.object.isRequired, fetchChannels: PropTypes.func.isRequired, openContactsForm: PropTypes.func.isRequired, toggleFilterPulldown: PropTypes.func.isRequired, changeFilter: PropTypes.func.isRequired, updateChannelSearchQuery: PropTypes.func.isRequired, - openContactModal: PropTypes.func.isRequired + setSelectedChannel: PropTypes.func.isRequired, + closeChannel: PropTypes.func.isRequired } export default Network diff --git a/app/routes/app/containers/AppContainer.js b/app/routes/app/containers/AppContainer.js index 5834927d..cad23e15 100644 --- a/app/routes/app/containers/AppContainer.js +++ b/app/routes/app/containers/AppContainer.js @@ -34,7 +34,6 @@ import { toggleFilterPulldown, changeFilter, updateChannelSearchQuery, - openContactModal, closeContactModal, setSelectedChannel } from 'reducers/channels' @@ -111,7 +110,6 @@ const mapDispatchToProps = { toggleFilterPulldown, changeFilter, updateChannelSearchQuery, - openContactModal, closeContactModal, setSelectedChannel, @@ -313,7 +311,6 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { toggleFilterPulldown: dispatchProps.toggleFilterPulldown, changeFilter: dispatchProps.changeFilter, updateChannelSearchQuery: dispatchProps.updateChannelSearchQuery, - openContactModal: dispatchProps.openContactModal, setSelectedChannel: dispatchProps.setSelectedChannel, closeChannel: dispatchProps.closeChannel } diff --git a/test/reducers/__snapshots__/channels.spec.js.snap b/test/reducers/__snapshots__/channels.spec.js.snap index 6f8dcd6a..518f65af 100644 --- a/test/reducers/__snapshots__/channels.spec.js.snap +++ b/test/reducers/__snapshots__/channels.spec.js.snap @@ -53,6 +53,7 @@ Object { "total_limbo_balance": "", }, "searchQuery": "", + "selectedChannel": null, "viewType": 0, } `; @@ -110,6 +111,7 @@ Object { "total_limbo_balance": "", }, "searchQuery": "", + "selectedChannel": null, "viewType": 0, } `; @@ -168,6 +170,7 @@ Object { 4, ], "searchQuery": "", + "selectedChannel": null, "viewType": 0, } `; @@ -225,6 +228,7 @@ Object { "total_limbo_balance": "", }, "searchQuery": "", + "selectedChannel": null, "viewType": 0, } `; @@ -282,6 +286,7 @@ Object { "total_limbo_balance": "", }, "searchQuery": "", + "selectedChannel": null, "viewType": 0, } `; From 48cdbd7a6e084c65e97fba45597a7e5874391520 Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Fri, 20 Apr 2018 17:06:43 -0500 Subject: [PATCH 4/4] fix(travis): add travis_wait --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b6114188..50f5ac0b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,4 +37,4 @@ before_script: - sleep 3 script: - - "yarn $TEST" + - "travis_wait 30 yarn $TEST"