From d25d1dfa739223ec77c68e2c897d152a5381e73d Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Thu, 17 Aug 2017 17:46:34 -0500 Subject: [PATCH 01/53] feature(lnd-ipc): initial lnd + ipcRenderer setup w/ info as example --- app/lnd/config/index.js | 4 + app/lnd/config/rpc.proto | 690 +++++++++++++++++++++++++++++++ app/lnd/index.js | 18 + app/lnd/lib/lightning.js | 13 + app/main.dev.js | 16 +- app/reducers/info.js | 8 +- app/routes/app/components/App.js | 6 +- package.json | 2 + yarn.lock | 125 +++++- 9 files changed, 866 insertions(+), 16 deletions(-) create mode 100644 app/lnd/config/index.js create mode 100644 app/lnd/config/rpc.proto create mode 100644 app/lnd/index.js create mode 100644 app/lnd/lib/lightning.js diff --git a/app/lnd/config/index.js b/app/lnd/config/index.js new file mode 100644 index 00000000..e409c073 --- /dev/null +++ b/app/lnd/config/index.js @@ -0,0 +1,4 @@ +export default { + lightningRpc: `${__dirname}/rpc.proto`, + lightningHost: 'localhost:10009' +} diff --git a/app/lnd/config/rpc.proto b/app/lnd/config/rpc.proto new file mode 100644 index 00000000..f341e415 --- /dev/null +++ b/app/lnd/config/rpc.proto @@ -0,0 +1,690 @@ +syntax = "proto3"; + +//import "google/api/annotations.proto"; + +package lnrpc; + +service Lightning { + rpc WalletBalance (WalletBalanceRequest) returns (WalletBalanceResponse) { + option (google.api.http) = { + get: "/v1/balance/blockchain" + }; + } + rpc ChannelBalance (ChannelBalanceRequest) returns (ChannelBalanceResponse) { + option (google.api.http) = { + get: "/v1/balance/channels" + }; + } + + rpc GetTransactions (GetTransactionsRequest) returns (TransactionDetails) { + option (google.api.http) = { + get: "/v1/transactions" + }; + } + rpc SendCoins (SendCoinsRequest) returns (SendCoinsResponse) { + option (google.api.http) = { + post: "/v1/transactions" + body: "*" + }; + } + rpc SubscribeTransactions (GetTransactionsRequest) returns (stream Transaction); + + rpc SendMany (SendManyRequest) returns (SendManyResponse); + + rpc NewAddress (NewAddressRequest) returns (NewAddressResponse); + rpc NewWitnessAddress (NewWitnessAddressRequest) returns (NewAddressResponse) { + option (google.api.http) = { + get: "/v1/newaddress" + }; + } + + rpc SignMessage (SignMessageRequest) returns (SignMessageResponse); + rpc VerifyMessage (VerifyMessageRequest) returns (VerifyMessageResponse); + + rpc ConnectPeer (ConnectPeerRequest) returns (ConnectPeerResponse) { + option (google.api.http) = { + post: "/v1/peers" + body: "*" + }; + } + + rpc DisconnectPeer (DisconnectPeerRequest) returns (DisconnectPeerResponse) { + option (google.api.http) = { + delete: "/v1/peers/{pub_key}" + }; + } + + rpc ListPeers (ListPeersRequest) returns (ListPeersResponse) { + option (google.api.http) = { + get: "/v1/peers" + }; + } + rpc GetInfo (GetInfoRequest) returns (GetInfoResponse) { + option (google.api.http) = { + get: "/v1/getinfo" + }; + } + + // TODO(roasbeef): merge with below with bool? + rpc PendingChannels (PendingChannelRequest) returns (PendingChannelResponse) { + option (google.api.http) = { + get: "/v1/channels/pending" + }; + } + rpc ListChannels (ListChannelsRequest) returns (ListChannelsResponse) { + option (google.api.http) = { + get: "/v1/channels" + }; + } + rpc OpenChannelSync (OpenChannelRequest) returns (ChannelPoint) { + option (google.api.http) = { + post: "/v1/channels" + body: "*" + }; + } + + rpc OpenChannel (OpenChannelRequest) returns (stream OpenStatusUpdate); + + rpc CloseChannel (CloseChannelRequest) returns (stream CloseStatusUpdate) { + option (google.api.http) = { + delete: "/v1/channels/{channel_point.funding_txid}/{channel_point.output_index}/{force}" + }; + } + + rpc SendPayment (stream SendRequest) returns (stream SendResponse); + + rpc SendPaymentSync (SendRequest) returns (SendResponse) { + option (google.api.http) = { + post: "/v1/channels/transactions" + body: "*" + }; + } + + rpc AddInvoice (Invoice) returns (AddInvoiceResponse) { + option (google.api.http) = { + post: "/v1/invoices" + body: "*" + }; + } + rpc ListInvoices (ListInvoiceRequest) returns (ListInvoiceResponse) { + option (google.api.http) = { + get: "/v1/invoices/{pending_only}" + }; + } + rpc LookupInvoice (PaymentHash) returns (Invoice) { + option (google.api.http) = { + get: "/v1/invoices/{r_hash_str}" + }; + } + rpc SubscribeInvoices (InvoiceSubscription) returns (stream Invoice) { + option (google.api.http) = { + get: "/v1/invoices/subscribe" + }; + } + rpc DecodePayReq (PayReqString) returns (PayReq) { + option (google.api.http) = { + get: "/v1/payreq/{pay_req}" + }; + } + + rpc ListPayments (ListPaymentsRequest) returns (ListPaymentsResponse) { + option (google.api.http) = { + get: "/v1/payments" + }; + }; + + rpc DeleteAllPayments (DeleteAllPaymentsRequest) returns (DeleteAllPaymentsResponse) { + option (google.api.http) = { + delete: "/v1/payments" + }; + }; + + rpc DescribeGraph (ChannelGraphRequest) returns (ChannelGraph) { + option (google.api.http) = { + get: "/v1/graph" + }; + } + + rpc GetChanInfo (ChanInfoRequest) returns (ChannelEdge) { + option (google.api.http) = { + get: "/v1/graph/edge/{chan_id}" + }; + } + + rpc GetNodeInfo (NodeInfoRequest) returns (NodeInfo) { + option (google.api.http) = { + get: "/v1/graph/node/{pub_key}" + }; + } + + rpc QueryRoutes(QueryRoutesRequest) returns (QueryRoutesResponse) { + option (google.api.http) = { + get: "/v1/graph/routes/{pub_key}/{amt}" + }; + } + + rpc GetNetworkInfo (NetworkInfoRequest) returns (NetworkInfo) { + option (google.api.http) = { + get: "/v1/graph/info" + }; + } + + rpc StopDaemon(StopRequest) returns (StopResponse); + + rpc SubscribeChannelGraph(GraphTopologySubscription) returns (stream GraphTopologyUpdate); + + rpc SetAlias(SetAliasRequest) returns (SetAliasResponse); + + rpc DebugLevel (DebugLevelRequest) returns (DebugLevelResponse); +} + +message Transaction { + string tx_hash = 1 [ json_name = "tx_hash" ]; + int64 amount = 2 [ json_name = "amount" ]; + int32 num_confirmations = 3 [ json_name = "num_confirmations" ]; + string block_hash = 4 [ json_name = "block_hash" ]; + int32 block_height = 5 [ json_name = "block_height" ]; + int64 time_stamp = 6 [ json_name = "time_stamp" ]; + int64 total_fees = 7 [ json_name = "total_fees" ]; +} +message GetTransactionsRequest { +} +message TransactionDetails { + repeated Transaction transactions = 1 [json_name = "transactions"]; +} + +message SendRequest { + bytes dest = 1; + string dest_string = 2; + + int64 amt = 3; + + bytes payment_hash = 4; + string payment_hash_string = 5; + + string payment_request = 6; +} +message SendResponse { + string payment_error = 1 [json_name = "payment_error"]; + bytes payment_preimage = 2 [json_name = "payment_preimage"]; + Route payment_route = 3 [json_name = "payment_route"]; +} + +message ChannelPoint { + // TODO(roasbeef): make str vs bytes into a oneof + bytes funding_txid = 1 [ json_name = "funding_txid" ]; + string funding_txid_str = 2 [ json_name = "funding_txid_str" ]; + uint32 output_index = 3 [ json_name = "output_index" ]; +} + +message LightningAddress { + string pubkey = 1 [json_name = "pubkey"]; + string host = 2 [json_name = "host"]; +} + +message SendManyRequest { + map AddrToAmount = 1; +} +message SendManyResponse { + string txid = 1 [json_name = "txid"]; +} + +message SendCoinsRequest { + string addr = 1; + int64 amount = 2; +} +message SendCoinsResponse { + string txid = 1 [json_name = "txid"]; +} + +message NewAddressRequest { + enum AddressType { + WITNESS_PUBKEY_HASH = 0; + NESTED_PUBKEY_HASH = 1; + PUBKEY_HASH = 2; + } + AddressType type = 1; +} +message NewWitnessAddressRequest { +} +message NewAddressResponse { + string address = 1 [json_name = "address"]; +} + +message SignMessageRequest { + bytes msg = 1 [ json_name = "msg" ]; +} +message SignMessageResponse { + string signature = 1 [ json_name = "signature" ]; +} + +message VerifyMessageRequest { + bytes msg = 1 [ json_name = "msg" ]; + string signature = 2 [ json_name = "signature" ]; +} +message VerifyMessageResponse { + bool valid = 1 [ json_name = "valid" ]; + string pubkey = 2 [ json_name = "pubkey" ]; +} + +message ConnectPeerRequest { + LightningAddress addr = 1; + bool perm = 2; +} +message ConnectPeerResponse { + int32 peer_id = 1 [json_name = "peer_id"]; +} + +message DisconnectPeerRequest { + string pub_key = 1 [json_name = "pub_key"]; +} +message DisconnectPeerResponse { +} + +message HTLC { + bool incoming = 1 [json_name = "incoming"]; + int64 amount = 2 [json_name = "amount"]; + bytes hash_lock = 3 [json_name = "hash_lock"]; + uint32 expiration_height = 4 [json_name = "expiration_height"]; + uint32 revocation_delay = 5 [json_name = "revocation_delay"]; +} + +message ActiveChannel { + bool active = 1 [json_name = "active"]; + string remote_pubkey = 2 [json_name = "remote_pubkey"]; + string channel_point = 3 [json_name = "channel_point"]; + uint64 chan_id = 4 [json_name = "chan_id"]; + + int64 capacity = 5 [json_name = "capacity"]; + int64 local_balance = 6 [json_name = "local_balance"]; + int64 remote_balance = 7 [json_name = "remote_balance"]; + + int64 commit_fee = 8 [json_name = "commit_fee"]; + int64 commit_weight = 9 [ json_name = "commit_weight" ]; + int64 fee_per_kw = 10 [json_name = "fee_per_kw"]; + + int64 unsettled_balance = 11 [json_name = "unsettled_balance"]; + int64 total_satoshis_sent = 12 [json_name = "total_satoshis_sent"]; + int64 total_satoshis_received = 13 [json_name = "total_satoshis_received"]; + uint64 num_updates = 14 [json_name = "num_updates"]; + + repeated HTLC pending_htlcs = 15 [json_name = "pending_htlcs"]; +} + +message ListChannelsRequest { +} +message ListChannelsResponse { + repeated ActiveChannel channels = 11 [json_name = "channels"]; +} + +message Peer { + string pub_key = 1 [json_name = "pub_key"]; + int32 peer_id = 2 [json_name = "peer_id"]; + string address = 3 [json_name = "address"]; + + uint64 bytes_sent = 4 [json_name = "bytes_sent"]; + uint64 bytes_recv = 5 [json_name = "bytes_recv"]; + + int64 sat_sent = 6 [json_name = "sat_sent"]; + int64 sat_recv = 7 [json_name = "sat_recv"]; + + bool inbound = 8 [json_name = "inbound"]; + + int64 ping_time = 9 [json_name = "ping_time"]; +} + +message ListPeersRequest { +} +message ListPeersResponse { + repeated Peer peers = 1 [json_name = "peers"]; +} + +message GetInfoRequest { +} +message GetInfoResponse { + string identity_pubkey = 1 [json_name = "identity_pubkey"]; + string alias = 2 [json_name = "alias"]; + + uint32 num_pending_channels = 3 [json_name = "num_pending_channels"]; + uint32 num_active_channels = 4 [json_name = "num_active_channels"]; + + uint32 num_peers = 5 [json_name = "num_peers"]; + + uint32 block_height = 6 [json_name = "block_height"]; + string block_hash = 8 [json_name = "block_hash"]; + + bool synced_to_chain = 9 [ json_name = "synced_to_chain" ]; + bool testnet = 10 [ json_name = "testnet" ]; + + repeated string chains = 11 [ json_name = "chains" ]; +} + +message ConfirmationUpdate { + bytes block_sha = 1; + int32 block_height = 2; + + uint32 num_confs_left = 3; +} + +message ChannelOpenUpdate { + ChannelPoint channel_point = 1 [json_name = "channel_point"]; +} + +message ChannelCloseUpdate { + bytes closing_txid = 1 [json_name = "closing_txid"]; + + bool success = 2 [json_name = "success"]; +} + +message CloseChannelRequest { + ChannelPoint channel_point = 1; + int64 time_limit = 2; + bool force = 3; +} +message CloseStatusUpdate { + oneof update { + PendingUpdate close_pending = 1 [json_name = "close_pending"]; + ConfirmationUpdate confirmation = 2 [json_name = "confirmation"]; + ChannelCloseUpdate chan_close = 3 [json_name = "chan_close"]; + } +} + +message PendingUpdate { + bytes txid = 1 [json_name = "txid"]; + uint32 output_index = 2 [json_name = "output_index"]; +} + +message OpenChannelRequest { + int32 target_peer_id = 1 [json_name = "target_peer_id"]; + bytes node_pubkey = 2 [json_name = "node_pubkey"]; + string node_pubkey_string = 3 [json_name = "node_pubkey_string"]; + + int64 local_funding_amount = 4 [json_name = "local_funding_amount"]; + int64 push_sat = 5 [json_name = "push_sat"]; + + uint32 num_confs = 6 [json_name = "num_confs"]; +} +message OpenStatusUpdate { + oneof update { + PendingUpdate chan_pending = 1 [json_name = "chan_pending"]; + ConfirmationUpdate confirmation = 2 [json_name = "confirmation"]; + ChannelOpenUpdate chan_open = 3 [json_name = "chan_open"]; + } +} + +message PendingChannelRequest {} +message PendingChannelResponse { + message PendingChannel { + string remote_node_pub = 1 [ json_name = "remote_node_pub" ]; + string channel_point = 2 [ json_name = "channel_point" ]; + + int64 capacity = 3 [ json_name = "capacity" ]; + + int64 local_balance = 4 [ json_name = "local_balance" ]; + int64 remote_balance = 5 [ json_name = "remote_balance" ]; + } + + message PendingOpenChannel { + PendingChannel channel = 1 [ json_name = "channel" ]; + + uint32 confirmation_height = 2 [ json_name = "confirmation_height" ]; + uint32 blocks_till_open = 3 [ json_name = "blocks_till_open" ]; + + int64 commit_fee = 4 [json_name = "commit_fee" ]; + int64 commit_weight = 5 [ json_name = "commit_weight" ]; + int64 fee_per_kw = 6 [ json_name = "fee_per_kw" ]; + } + + message ClosedChannel { + PendingChannel channel = 1; + + string closing_txid = 2 [ json_name = "closing_txid" ]; + } + + message ForceClosedChannel { + PendingChannel channel = 1 [ json_name = "channel" ]; + + // TODO(roasbeef): HTLC's as well? + + string closing_txid = 2 [ json_name = "closing_txid" ]; + + int64 limbo_balance = 3 [ json_name = "limbo_balance" ]; + uint32 maturity_height = 4 [ json_name = "maturity_height" ]; + uint32 blocks_til_maturity = 5 [ json_name = "blocks_til_maturity" ]; + } + + int64 total_limbo_balance = 1 [ json_name = "total_limbo_balance" ]; + repeated PendingOpenChannel pending_open_channels = 2 [ json_name = "pending_open_channels" ]; + repeated ClosedChannel pending_closing_channels = 3 [ json_name = "pending_closing_channels" ]; + repeated ForceClosedChannel pending_force_closing_channels = 4 [ json_name = "pending_force_closing_channels" ]; +} + +message WalletBalanceRequest { + bool witness_only = 1; +} +message WalletBalanceResponse { + int64 balance = 1 [json_name = "balance"]; +} + +message ChannelBalanceRequest { +} +message ChannelBalanceResponse { + int64 balance = 1 [json_name = "balance"]; +} + +message QueryRoutesRequest { + string pub_key = 1; + int64 amt = 2; +} +message QueryRoutesResponse { + repeated Route routes = 1 [ json_name = "routes"]; +} + +message Hop { + uint64 chan_id = 1 [json_name = "chan_id"]; + int64 chan_capacity = 2 [json_name = "chan_capacity"]; + int64 amt_to_forward = 3 [json_name = "amt_to_forward"]; + int64 fee = 4 [json_name = "fee"]; +} + +message Route { + uint32 total_time_lock = 1 [json_name = "total_time_lock"]; + int64 total_fees = 2 [json_name = "total_fees"]; + int64 total_amt = 3 [json_name = "total_amt"]; + + repeated Hop hops = 4 [json_name = "hops"]; +} + +message NodeInfoRequest { + string pub_key = 1; +} + +message NodeInfo { + LightningNode node = 1 [json_name = "node"]; + + uint32 num_channels = 2 [json_name = "num_channels"]; + int64 total_capacity = 3 [json_name = "total_capacity"]; +} + +message LightningNode { + uint32 last_update = 1 [ json_name = "last_update" ]; + string pub_key = 2 [ json_name = "pub_key" ]; + string alias = 3 [ json_name = "alias" ]; + repeated NodeAddress addresses = 4 [ json_name = "addresses" ]; +} + +message NodeAddress { + string network = 1 [ json_name = "network" ]; + string addr = 2 [ json_name = "addr" ]; +} + +message RoutingPolicy { + uint32 time_lock_delta = 1 [json_name = "time_lock_delta"]; + int64 min_htlc = 2 [json_name = "min_htlc"]; + int64 fee_base_msat = 3 [json_name = "fee_base_msat"]; + int64 fee_rate_milli_msat = 4 [json_name = "fee_rate_milli_msat"]; +} + +message ChannelEdge { + uint64 channel_id = 1 [json_name = "channel_id"]; + string chan_point = 2 [json_name = "chan_point"]; + + uint32 last_update = 3 [json_name = "last_update"]; + + string node1_pub = 4 [json_name = "node1_pub"]; + string node2_pub = 5 [json_name = "node2_pub"]; + + int64 capacity = 6 [json_name = "capacity"]; + + RoutingPolicy node1_policy = 7 [json_name = "node1_policy"]; + RoutingPolicy node2_policy = 8 [json_name = "node2_policy"]; +} + +message ChannelGraphRequest { +} + +message ChannelGraph { + repeated LightningNode nodes = 1 [json_name = "nodes"]; + repeated ChannelEdge edges = 2 [json_name = "edges"]; +} + +message ChanInfoRequest { + uint64 chan_id = 1; +} + +message NetworkInfoRequest { +} +message NetworkInfo { + uint32 graph_diameter = 1 [json_name = "graph_diameter"]; + double avg_out_degree = 2 [json_name = "avg_out_degree"]; + uint32 max_out_degree = 3 [json_name = "max_out_degree"]; + + uint32 num_nodes = 4 [json_name = "num_nodes"]; + uint32 num_channels = 5 [json_name = "num_channels"]; + + int64 total_network_capacity = 6 [json_name = "total_network_capacity"]; + + double avg_channel_size = 7 [json_name = "avg_channel_size"]; + int64 min_channel_size = 8 [json_name = "min_channel_size"]; + int64 max_channel_size = 9 [json_name = "max_channel_size"]; + + // TODO(roasbeef): fee rate info, expiry + // * also additional RPC for tracking fee info once in +} + +message StopRequest{} +message StopResponse{} + +message GraphTopologySubscription {} +message GraphTopologyUpdate { + repeated NodeUpdate node_updates = 1; + repeated ChannelEdgeUpdate channel_updates = 2; + repeated ClosedChannelUpdate closed_chans = 3; +} +message NodeUpdate { + repeated string addresses = 1; + string identity_key = 2; + bytes global_features = 3; + string alias = 4; +} +message ChannelEdgeUpdate { + uint64 chan_id = 1; + + ChannelPoint chan_point = 2; + + int64 capacity = 3; + + RoutingPolicy routing_policy = 4; + + string advertising_node = 5; + string connecting_node = 6; +} +message ClosedChannelUpdate { + uint64 chan_id = 1; + int64 capacity = 2; + uint32 closed_height = 3; + ChannelPoint chan_point = 4; +} + +message SetAliasRequest { + string new_alias = 1; +} +message SetAliasResponse { +} + +message Invoice { + string memo = 1 [json_name = "memo"]; + bytes receipt = 2 [json_name = "receipt"]; + + bytes r_preimage = 3 [json_name = "r_preimage"]; + bytes r_hash = 4 [json_name = "r_hash"]; + + int64 value = 5 [json_name = "value"]; + + bool settled = 6 [json_name = "settled"]; + + int64 creation_date = 7 [json_name = "creation_date"]; + int64 settle_date = 8 [json_name = "settle_date"]; + + string payment_request = 9 [json_name = "payment_request"]; +} +message AddInvoiceResponse { + bytes r_hash = 1 [json_name = "r_hash"]; + + string payment_request = 2 [json_name = "payment_request"]; +} +message PaymentHash { + string r_hash_str = 1 [json_name = "r_hash_str"]; + bytes r_hash = 2 [json_name = "r_hash"]; +} +message ListInvoiceRequest { + bool pending_only = 1; +} +message ListInvoiceResponse { + repeated Invoice invoices = 1 [json_name = "invoices"]; +} + +message InvoiceSubscription { +} + + +message Payment { + string payment_hash = 1 [json_name = "payment_hash"]; + int64 value = 2 [json_name = "value"]; + + int64 creation_date = 3 [json_name = "creation_date"]; + + repeated string path = 4 [ json_name = "path" ]; + + int64 fee = 5 [json_name = "fee"]; +} + +message ListPaymentsRequest { +} + +message ListPaymentsResponse { + repeated Payment payments = 1 [json_name = "payments"]; +} + +message DeleteAllPaymentsRequest { +} + +message DeleteAllPaymentsResponse { +} + +message DebugLevelRequest { + bool show = 1; + string level_spec = 2; +} +message DebugLevelResponse { + string sub_systems = 1 [json_name = "sub_systems"]; +} + +message PayReqString { + string pay_req = 1; +} +message PayReq { + string destination = 1 [json_name = "destination"]; + string payment_hash = 2 [json_name = "payment_hash"]; + int64 num_satoshis = 3 [json_name = "num_satoshis"]; +} \ No newline at end of file diff --git a/app/lnd/index.js b/app/lnd/index.js new file mode 100644 index 00000000..7ec9df01 --- /dev/null +++ b/app/lnd/index.js @@ -0,0 +1,18 @@ +import config from './config' +import lightning from './lib/lightning' + +const lnd = lightning(config.lightningRpc, config.lightningHost) + +export function info() { + return new Promise((resolve, reject) => { + lnd.getInfo({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} + +export default { + info +} \ No newline at end of file diff --git a/app/lnd/lib/lightning.js b/app/lnd/lib/lightning.js new file mode 100644 index 00000000..fb8cdc1e --- /dev/null +++ b/app/lnd/lib/lightning.js @@ -0,0 +1,13 @@ +import fs from 'fs' +import grpc from 'grpc' + +module.exports = (path, host, cert) => { + process.env['GRPC_SSL_CIPHER_SUITES'] = 'HIGH+ECDSA' + + const rpc = grpc.load(path) + + const lndCert = fs.readFileSync('/Users/jmow/Library/Application Support/Lnd/tls.cert') + const credentials = grpc.credentials.createSsl(lndCert) + + return new rpc.lnrpc.Lightning(host, credentials) +} \ No newline at end of file diff --git a/app/main.dev.js b/app/main.dev.js index c5f4c8df..b78b0c3f 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -11,8 +11,9 @@ * * @flow */ -import { app, BrowserWindow } from 'electron' +import { app, BrowserWindow, ipcMain } from 'electron' import MenuBuilder from './menu' +import lnd from './lnd' let mainWindow = null; @@ -90,3 +91,16 @@ app.on('ready', async () => { const menuBuilder = new MenuBuilder(mainWindow); menuBuilder.buildMenu(); }); + +ipcMain.on('lnd', (event, { msg, data }) => { + switch(msg) { + case 'info': + lnd.info() + .then(data => event.sender.send('info', data)) + .catch(error => console.log('info error: ', error)) + break + default: + return + } +}) + diff --git a/app/reducers/info.js b/app/reducers/info.js index 2cf7bef4..075e9d8d 100644 --- a/app/reducers/info.js +++ b/app/reducers/info.js @@ -1,3 +1,4 @@ +import { ipcRenderer } from 'electron' import { callApi } from '../api' // ------------------------------------ // Constants @@ -23,8 +24,11 @@ export function receiveInfo(data) { export const fetchInfo = () => async (dispatch) => { dispatch(getInfo()) - const info = await callApi('info') - dispatch(receiveInfo(info.data)) + ipcRenderer.send('lnd', { msg: 'info' }) + ipcRenderer.on('info', (event, info) => { + dispatch(receiveInfo(info)) + }) + // const info = await callApi('info') } // ------------------------------------ diff --git a/app/routes/app/components/App.js b/app/routes/app/components/App.js index e125e70d..61eed04c 100644 --- a/app/routes/app/components/App.js +++ b/app/routes/app/components/App.js @@ -1,3 +1,4 @@ +import { ipcRenderer } from 'electron' import React, { Component } from 'react' import PropTypes from 'prop-types' import Form from './components/Form' @@ -5,11 +6,6 @@ import Nav from './components/Nav' import Socket from './components/Socket' import styles from './App.scss' -export const CHANNEL_DATA = 'CHANNEL_DATA' -export const CHANNEL_END = 'CHANNEL_END' -export const CHANNEL_ERROR = 'CHANNEL_ERROR' -export const CHANNEL_STATUS = 'CHANNEL_STATUS' - class App extends Component { componentWillMount() { const { fetchTicker, fetchBalance } = this.props diff --git a/package.json b/package.json index 334aea19..6c0a05c8 100644 --- a/package.json +++ b/package.json @@ -137,6 +137,7 @@ "electron": "^1.6.10", "electron-builder": "^19.8.0", "electron-devtools-installer": "^2.2.0", + "electron-rebuild": "^1.6.0", "enzyme": "^2.9.1", "enzyme-to-json": "^1.5.1", "eslint": "^4.4.1", @@ -186,6 +187,7 @@ "devtron": "^1.4.0", "electron-debug": "^1.2.0", "font-awesome": "^4.7.0", + "grpc": "^1.4.1", "history": "^4.6.3", "moment-timezone": "^0.5.13", "prop-types": "^15.5.10", diff --git a/yarn.lock b/yarn.lock index 571df020..5368e9ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -246,6 +246,10 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +arguejs@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/arguejs/-/arguejs-0.2.3.tgz#b6f939f5fe0e3cd1f3f93e2aa9262424bf312af7" + aria-query@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-0.7.0.tgz#4af10a1e61573ddea0cf3b99b51c52c05b424d24" @@ -322,6 +326,13 @@ asar-integrity@0.1.1: bluebird-lst "^1.0.2" fs-extra-p "^4.3.0" +ascli@~1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ascli/-/ascli-1.0.1.tgz#bcfa5974a62f18e81cabaeb49732ab4a88f906bc" + dependencies: + colour "~0.7.1" + optjs "~3.2.2" + asn1.js@^4.0.0: version "4.9.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" @@ -1667,6 +1678,12 @@ builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" +bytebuffer@~5: + version "5.0.1" + resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd" + dependencies: + long "~3" + bytes@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.3.0.tgz#d5b680a165b6201739acb611542aabc2d8ceb070" @@ -1703,7 +1720,7 @@ camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" -camelcase@^2.0.0: +camelcase@^2.0.0, camelcase@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -1873,6 +1890,10 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-spinners@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.0.0.tgz#ef987ed3d48391ac3dab9180b406a742180d6e6a" + cli-width@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" @@ -1885,7 +1906,7 @@ cliui@^2.1.0: right-align "^0.1.1" wordwrap "0.0.2" -cliui@^3.2.0: +cliui@^3.0.3, cliui@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" dependencies: @@ -1990,6 +2011,10 @@ colors@^1.1.2, colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" +colour@~0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778" + combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" @@ -2476,7 +2501,7 @@ debug@2.6.7: dependencies: ms "2.0.0" -debug@2.6.8, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.4.5, debug@^2.6.0, debug@^2.6.1, debug@^2.6.3, debug@^2.6.6, debug@^2.6.8: +debug@2.6.8, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.4.5, debug@^2.5.1, debug@^2.6.0, debug@^2.6.1, debug@^2.6.3, debug@^2.6.6, debug@^2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: @@ -2918,6 +2943,20 @@ electron-publish@19.8.0: fs-extra-p "^4.3.0" mime "^1.3.6" +electron-rebuild@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-1.6.0.tgz#e8d26f4d8e9fe5388df35864b3658e5cfd4dcb7e" + dependencies: + colors "^1.1.2" + debug "^2.6.3" + fs-extra "^3.0.1" + node-abi "^2.0.0" + node-gyp "^3.6.0" + ora "^1.2.0" + rimraf "^2.6.1" + spawn-rx "^2.0.10" + yargs "^7.0.2" + electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.11: version "1.3.14" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.14.tgz#64af0f9efd3c3c6acd57d71f83b49ca7ee9c4b43" @@ -4024,6 +4063,16 @@ growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" +grpc@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/grpc/-/grpc-1.4.1.tgz#3ee4a8346a613f2823928c9f8f99081b6368ec7c" + dependencies: + arguejs "^0.2.3" + lodash "^4.15.0" + nan "^2.0.0" + node-pre-gyp "^0.6.35" + protobufjs "^5.0.0" + gulp-util@^3.0.4: version "3.0.8" resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" @@ -5518,6 +5567,10 @@ lolex@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6" +long@~3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" + longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" @@ -5789,7 +5842,7 @@ mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" -nan@^2.3.0, nan@^2.3.2: +nan@^2.0.0, nan@^2.3.0, nan@^2.3.2: version "2.6.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" @@ -5821,6 +5874,10 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" +node-abi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.1.0.tgz#50ad834affcf17440e12bfc5f9ba0946f572d10c" + node-emoji@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.5.1.tgz#fd918e412769bf8c448051238233840b2aff16a1" @@ -5838,7 +5895,7 @@ node-forge@0.6.33: version "0.6.33" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc" -node-gyp@^3.3.1: +node-gyp@^3.3.1, node-gyp@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60" dependencies: @@ -5925,7 +5982,7 @@ node-notifier@^5.0.2: shellwords "^0.1.0" which "^1.2.12" -node-pre-gyp@^0.6.29: +node-pre-gyp@^0.6.29, node-pre-gyp@^0.6.35: version "0.6.36" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" dependencies: @@ -6182,6 +6239,19 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" wordwrap "~1.0.0" +optjs@~3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/optjs/-/optjs-3.2.2.tgz#69a6ce89c442a44403141ad2f9b370bd5bb6f4ee" + +ora@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-1.3.0.tgz#80078dd2b92a934af66a3ad72a5b910694ede51a" + dependencies: + chalk "^1.1.1" + cli-cursor "^2.1.0" + cli-spinners "^1.0.0" + log-symbols "^1.0.2" + original@>=0.0.5: version "1.0.0" resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b" @@ -6828,6 +6898,15 @@ prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8: fbjs "^0.8.9" loose-envify "^1.3.1" +protobufjs@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-5.0.2.tgz#59748d7dcf03d2db22c13da9feb024e16ab80c91" + dependencies: + ascli "~1" + bytebuffer "~5" + glob "^7.0.5" + yargs "^3.10.0" + proxy-addr@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3" @@ -7479,6 +7558,12 @@ rx@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" +rxjs@^5.1.1: + version "5.4.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.3.tgz#0758cddee6033d68e0fd53676f0f3596ce3d483f" + dependencies: + symbol-observable "^1.0.1" + safe-buffer@^5.0.1, safe-buffer@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223" @@ -7793,6 +7878,14 @@ spawn-command@^0.0.2-1: version "0.0.2" resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e" +spawn-rx@^2.0.10: + version "2.0.11" + resolved "https://registry.yarnpkg.com/spawn-rx/-/spawn-rx-2.0.11.tgz#65451ad65662801daea75549832a782de0048dbf" + dependencies: + debug "^2.5.1" + lodash.assign "^4.2.0" + rxjs "^5.1.1" + spdx-correct@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" @@ -8228,7 +8321,7 @@ svgpath@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/svgpath/-/svgpath-2.2.1.tgz#0834bb67c89a76472b2bd06cc101fa7b517b222c" -symbol-observable@^1.0.3: +symbol-observable@^1.0.1, symbol-observable@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" @@ -8948,6 +9041,10 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" +window-size@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" + window-size@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" @@ -9033,7 +9130,7 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -y18n@^3.2.1: +y18n@^3.2.0, y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" @@ -9070,6 +9167,18 @@ yargs@^1.2.6: version "1.3.3" resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.3.3.tgz#054de8b61f22eefdb7207059eaef9d6b83fb931a" +yargs@^3.10.0: + version "3.32.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" + dependencies: + camelcase "^2.0.1" + cliui "^3.0.3" + decamelize "^1.1.1" + os-locale "^1.4.0" + string-width "^1.0.1" + window-size "^0.1.4" + y18n "^3.2.0" + yargs@^3.5.4, yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" From 3cb8d3b52233b78c443d33c191f89bf44e1e0109 Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Thu, 17 Aug 2017 23:54:53 -0500 Subject: [PATCH 02/53] feature(lnd-ipc): add redux-electron-ipc for easier send/receive IPC calls w/ redux --- app/main.dev.js | 2 +- app/reducers/info.js | 9 +++++---- app/reducers/ipc.js | 9 +++++++++ app/store/configureStore.dev.js | 15 ++++++++------- package.json | 1 + yarn.lock | 4 ++++ 6 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 app/reducers/ipc.js diff --git a/app/main.dev.js b/app/main.dev.js index b78b0c3f..de3ef5fd 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -96,7 +96,7 @@ ipcMain.on('lnd', (event, { msg, data }) => { switch(msg) { case 'info': lnd.info() - .then(data => event.sender.send('info', data)) + .then(info => event.sender.send('receivedInfo', info)) .catch(error => console.log('info error: ', error)) break default: diff --git a/app/reducers/info.js b/app/reducers/info.js index 075e9d8d..6b661214 100644 --- a/app/reducers/info.js +++ b/app/reducers/info.js @@ -15,6 +15,7 @@ export function getInfo() { } } + export function receiveInfo(data) { return { type: RECEIVE_INFO, @@ -22,15 +23,15 @@ export function receiveInfo(data) { } } +// Send IPC event for getifno export const fetchInfo = () => async (dispatch) => { dispatch(getInfo()) ipcRenderer.send('lnd', { msg: 'info' }) - ipcRenderer.on('info', (event, info) => { - dispatch(receiveInfo(info)) - }) - // const info = await callApi('info') } +// Receive IPC event for info +export const receivedInfo = (event, data) => dispatch => dispatch({ type: RECEIVE_INFO, data }) + // ------------------------------------ // Action Handlers // ------------------------------------ diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js new file mode 100644 index 00000000..f52e3856 --- /dev/null +++ b/app/reducers/ipc.js @@ -0,0 +1,9 @@ +import createIpc from 'redux-electron-ipc' +import { receivedInfo } from './info' + +// Import all receiving IPC event handlers and pass them into createIpc +const ipc = createIpc({ + 'receivedInfo': receivedInfo +}) + +export default ipc \ No newline at end of file diff --git a/app/store/configureStore.dev.js b/app/store/configureStore.dev.js index d5861fb7..b3b41237 100644 --- a/app/store/configureStore.dev.js +++ b/app/store/configureStore.dev.js @@ -1,9 +1,10 @@ -import { createStore, applyMiddleware, compose } from 'redux'; -import thunk from 'redux-thunk'; -import { createHashHistory } from 'history'; -import { routerMiddleware, routerActions } from 'react-router-redux'; -import { createLogger } from 'redux-logger'; -import rootReducer from '../reducers'; +import { createStore, applyMiddleware, compose } from 'redux' +import thunk from 'redux-thunk' +import { createHashHistory } from 'history' +import { routerMiddleware, routerActions } from 'react-router-redux' +import { createLogger } from 'redux-logger' +import rootReducer from '../reducers' +import ipc from '../reducers/ipc' const history = createHashHistory(); @@ -41,7 +42,7 @@ const configureStore = (initialState?: counterStateType) => { /* eslint-enable no-underscore-dangle */ // Apply Middleware & Compose Enhancers - enhancers.push(applyMiddleware(...middleware)); + enhancers.push(applyMiddleware(...middleware, ipc)); const enhancer = composeEnhancers(...enhancers); // Create Store diff --git a/package.json b/package.json index 6c0a05c8..1cfa0120 100644 --- a/package.json +++ b/package.json @@ -206,6 +206,7 @@ "react-svg-morph": "^0.1.10", "react-websocket": "^1.1.7", "redux": "^3.7.1", + "redux-electron-ipc": "^1.1.10", "redux-thunk": "^2.2.0", "reselect": "^3.0.1", "satoshi-bitcoin": "^1.0.4", diff --git a/yarn.lock b/yarn.lock index 5368e9ec..1ee04b9c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7289,6 +7289,10 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^0.4.2" +redux-electron-ipc@^1.1.10: + version "1.1.10" + resolved "https://registry.yarnpkg.com/redux-electron-ipc/-/redux-electron-ipc-1.1.10.tgz#0e4de0ae30eb8571209f24e75149007e965e65d1" + redux-logger@^3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf" From 51311401faa7509599ca550cf407c4ce9a63ca52 Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Fri, 18 Aug 2017 00:13:34 -0500 Subject: [PATCH 03/53] feature(lnd-ipc): replace API with ipc for listPeers --- app/lnd/index.js | 13 ++++++++++++- app/main.dev.js | 7 ++++++- app/reducers/info.js | 13 ++----------- app/reducers/ipc.js | 6 ++++-- app/reducers/peers.js | 15 ++++++--------- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/app/lnd/index.js b/app/lnd/index.js index 7ec9df01..eaabff61 100644 --- a/app/lnd/index.js +++ b/app/lnd/index.js @@ -13,6 +13,17 @@ export function info() { }) } +export function peers() { + return new Promise((resolve, reject) => { + lnd.listPeers({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} + export default { - info + info, + peers } \ No newline at end of file diff --git a/app/main.dev.js b/app/main.dev.js index de3ef5fd..42dbb633 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -96,7 +96,12 @@ ipcMain.on('lnd', (event, { msg, data }) => { switch(msg) { case 'info': lnd.info() - .then(info => event.sender.send('receivedInfo', info)) + .then(info => event.sender.send('receiveInfo', info)) + .catch(error => console.log('info error: ', error)) + break + case 'peers': + lnd.peers() + .then(peers => event.sender.send('receivePeers', peers)) .catch(error => console.log('info error: ', error)) break default: diff --git a/app/reducers/info.js b/app/reducers/info.js index 6b661214..9be0c5a2 100644 --- a/app/reducers/info.js +++ b/app/reducers/info.js @@ -1,5 +1,4 @@ import { ipcRenderer } from 'electron' -import { callApi } from '../api' // ------------------------------------ // Constants // ------------------------------------ @@ -15,22 +14,14 @@ export function getInfo() { } } - -export function receiveInfo(data) { - return { - type: RECEIVE_INFO, - data - } -} - -// Send IPC event for getifno +// Send IPC event for getinfo export const fetchInfo = () => async (dispatch) => { dispatch(getInfo()) ipcRenderer.send('lnd', { msg: 'info' }) } // Receive IPC event for info -export const receivedInfo = (event, data) => dispatch => dispatch({ type: RECEIVE_INFO, data }) +export const receiveInfo = (event, data) => dispatch => dispatch({ type: RECEIVE_INFO, data }) // ------------------------------------ // Action Handlers diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index f52e3856..02a68711 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -1,9 +1,11 @@ import createIpc from 'redux-electron-ipc' -import { receivedInfo } from './info' +import { receiveInfo } from './info' +import { receivePeers } from './peers' // Import all receiving IPC event handlers and pass them into createIpc const ipc = createIpc({ - 'receivedInfo': receivedInfo + 'receiveInfo': receiveInfo, + 'receivePeers': receivePeers }) export default ipc \ No newline at end of file diff --git a/app/reducers/peers.js b/app/reducers/peers.js index 8862e7e1..b2fb2c59 100644 --- a/app/reducers/peers.js +++ b/app/reducers/peers.js @@ -1,4 +1,5 @@ import { createSelector } from 'reselect' +import { ipcRenderer } from 'electron' import { callApi } from '../api' // ------------------------------------ // Constants @@ -79,19 +80,15 @@ export function getPeers() { } } -export function receivePeers({ peers }) { - return { - type: RECEIVE_PEERS, - peers - } -} - +// Send IPC event for peers export const fetchPeers = () => async (dispatch) => { dispatch(getPeers()) - const peers = await callApi('peers') - dispatch(receivePeers(peers.data)) + ipcRenderer.send('lnd', { msg: 'peers' }) } +// Receive IPC event for peers +export const receivePeers = (event, { peers }) => dispatch => dispatch({ type: RECEIVE_PEERS, peers }) + export const connectRequest = ({ pubkey, host }) => async (dispatch) => { dispatch(connectPeer()) const success = await callApi('connect', 'post', { pubkey, host }) From f9d94eb999787cd967d2221c4983c74a291940cc Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Sun, 20 Aug 2017 11:40:06 -0500 Subject: [PATCH 04/53] feature(lnd-ipc): fetch payments with IPC --- app/lnd/index.js | 42 ++++++++++++++++++++++++++++++++++++++-- app/main.dev.js | 13 +++++++++++++ app/reducers/channels.js | 16 ++++++--------- app/reducers/ipc.js | 6 +++++- app/reducers/payment.js | 22 ++++++--------------- 5 files changed, 70 insertions(+), 29 deletions(-) diff --git a/app/lnd/index.js b/app/lnd/index.js index eaabff61..944cfe3e 100644 --- a/app/lnd/index.js +++ b/app/lnd/index.js @@ -3,6 +3,7 @@ import lightning from './lib/lightning' const lnd = lightning(config.lightningRpc, config.lightningHost) +// LND Get Info export function info() { return new Promise((resolve, reject) => { lnd.getInfo({}, (err, data) => { @@ -13,6 +14,7 @@ export function info() { }) } +// LND List Peers export function peers() { return new Promise((resolve, reject) => { lnd.listPeers({}, (err, data) => { @@ -23,7 +25,43 @@ export function peers() { }) } +// LND List Channels +const channels = new Promise((resolve, reject) => { + lnd.listChannels({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) +}) + +// LND List Pending Channels +const pendingChannels = new Promise((resolve, reject) => { + lnd.pendingChannels({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) +}) + +// LND Get All Channels +export function allChannels() { + return Promise.all([channels, pendingChannels]) +} + +// LND Get Payments +export function payments() { + return new Promise((resolve, reject) => { + lnd.listPayments({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} + export default { info, - peers -} \ No newline at end of file + peers, + allChannels, + payments +} diff --git a/app/main.dev.js b/app/main.dev.js index 42dbb633..70c20b43 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -100,10 +100,23 @@ ipcMain.on('lnd', (event, { msg, data }) => { .catch(error => console.log('info error: ', error)) break case 'peers': + // Data looks like { peers: [] } lnd.peers() .then(peers => event.sender.send('receivePeers', peers)) .catch(error => console.log('info error: ', error)) break + case 'channels': + // Data looks like [ { channels: [channel, channel, channel] }, { total_limbo_balance: 0, pending_open_channels: [], pending_closing_channels: [], pending_force_closing_channels: [] } ] + lnd.allChannels() + .then(data => event.sender.send('receiveChannels', { channels: data[0].channels, pendingChannels: data[1] })) + .catch(error => console.log('info error: ', error)) + break + case 'payments': + // Data looks like { payments: [] } + lnd.payments() + .then(payments => event.sender.send('receivePayments', payments)) + .catch(error => console.log('info error: ', error)) + break default: return } diff --git a/app/reducers/channels.js b/app/reducers/channels.js index 20fef970..33cbd5f3 100644 --- a/app/reducers/channels.js +++ b/app/reducers/channels.js @@ -1,4 +1,5 @@ import { createSelector } from 'reselect' +import { ipcRenderer } from 'electron' import { callApi, callApis } from '../api' // ------------------------------------ // Constants @@ -38,14 +39,6 @@ export function getChannels() { } } -export function receiveChannels(channels) { - return { - type: RECEIVE_CHANNELS, - channels: channels[0].data.channels, - pendingChannels: channels[1].data - } -} - export function openingChannel() { return { type: OPENING_CHANNEL @@ -64,12 +57,15 @@ export function openingFailure() { } } +// Send IPC event for peers export const fetchChannels = () => async (dispatch) => { dispatch(getChannels()) - const channels = await callApis(['channels', 'pending_channels']) - dispatch(receiveChannels(channels)) + ipcRenderer.send('lnd', { msg: 'channels' }) } +// 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 } dispatch(openingChannel()) diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index 02a68711..d750ce7f 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -1,11 +1,15 @@ import createIpc from 'redux-electron-ipc' import { receiveInfo } from './info' import { receivePeers } from './peers' +import { receiveChannels } from './channels' +import { receivePayments } from './payment' // Import all receiving IPC event handlers and pass them into createIpc const ipc = createIpc({ 'receiveInfo': receiveInfo, - 'receivePeers': receivePeers + 'receivePeers': receivePeers, + 'receiveChannels': receiveChannels, + 'receivePayments': receivePayments }) export default ipc \ No newline at end of file diff --git a/app/reducers/payment.js b/app/reducers/payment.js index 2bf3bc96..3d8a3f1e 100644 --- a/app/reducers/payment.js +++ b/app/reducers/payment.js @@ -1,4 +1,5 @@ import { createSelector } from 'reselect' +import { ipcRenderer } from 'electron' import { callApi } from '../api' // ------------------------------------ @@ -29,13 +30,6 @@ export function getPayments() { } } -export function receivePayments(data) { - return { - type: RECEIVE_PAYMENTS, - payments: data.payments.reverse() - } -} - export function sendPayment() { return { type: SEND_PAYMENT @@ -55,19 +49,15 @@ export function paymentFailed() { } } +// Send IPC event for peers export const fetchPayments = () => async (dispatch) => { dispatch(getPayments()) - const payments = await callApi('payments') - - if (payments) { - dispatch(receivePayments(payments.data)) - } else { - dispatch(paymentFailed()) - } - - return payments + ipcRenderer.send('lnd', { msg: 'payments' }) } +// Receive IPC event for peers +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 }) From 6f66a3856accb8a1ada003c3ccfcb8791270246a Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Sun, 20 Aug 2017 13:58:50 -0500 Subject: [PATCH 05/53] feature(lnd-ipc): ipc for invoice + balance --- app/lnd/index.js | 38 +++++++++++++++++++++++++++++++++++++- app/main.dev.js | 12 ++++++++++++ app/reducers/balance.js | 15 +++++---------- app/reducers/invoice.js | 23 +++++++---------------- app/reducers/ipc.js | 6 +++++- app/reducers/payment.js | 4 ++-- 6 files changed, 68 insertions(+), 30 deletions(-) diff --git a/app/lnd/index.js b/app/lnd/index.js index 944cfe3e..7cd730b4 100644 --- a/app/lnd/index.js +++ b/app/lnd/index.js @@ -59,9 +59,45 @@ export function payments() { }) } +// LND Get Invoices +export function invoices() { + return new Promise((resolve, reject) => { + lnd.listInvoices({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} + +// LND Get Wallet Balance +const walletBalance = new Promise((resolve, reject) => { + lnd.walletBalance({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) +}) + +// LND Get Channel Balance +const channelBalance = new Promise((resolve, reject) => { + lnd.channelBalance({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) +}) + +// LND Get Wallet + Channel Balance +export function balance() { + return Promise.all([walletBalance, channelBalance]) +} + export default { info, peers, allChannels, - payments + payments, + invoices, + balance } diff --git a/app/main.dev.js b/app/main.dev.js index 70c20b43..98fa93ef 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -117,6 +117,18 @@ ipcMain.on('lnd', (event, { msg, data }) => { .then(payments => event.sender.send('receivePayments', payments)) .catch(error => console.log('info error: ', error)) break + case 'invoices': + // Data looks like { invoices: [] } + lnd.invoices() + .then(invoices => event.sender.send('receiveInvoices', invoices)) + .catch(error => console.log('info error: ', error)) + break + case 'balance': + // Balance looks like [ { balance: '129477456' }, { balance: '243914' } ] + lnd.balance() + .then(balance => event.sender.send('receiveBalance', { walletBalance: balance[0].balance, channelBalance: balance[1].balance })) + .catch(error => console.log('info error: ', error)) + break default: return } diff --git a/app/reducers/balance.js b/app/reducers/balance.js index 5958f239..8609f2b3 100644 --- a/app/reducers/balance.js +++ b/app/reducers/balance.js @@ -1,3 +1,4 @@ +import { ipcRenderer } from 'electron' import { callApis } from '../api' // ------------------------------------ // Constants @@ -14,20 +15,14 @@ export function getBalance() { } } -export function receiveBalance(data) { - return { - type: RECEIVE_BALANCE, - walletBalance: data[0].data.balance, - channelBalance: data[1].data.balance - } -} - export const fetchBalance = () => async (dispatch) => { dispatch(getBalance()) - const balance = await callApis(['wallet_balance', 'channel_balance']) - dispatch(receiveBalance(balance)) + ipcRenderer.send('lnd', { msg: 'balance' }) } +// Receive IPC event for peers +export const receiveBalance = (event, { walletBalance, channelBalance }) => dispatch => dispatch({ type: RECEIVE_BALANCE, walletBalance, channelBalance }) + // ------------------------------------ // Action Handlers // ------------------------------------ diff --git a/app/reducers/invoice.js b/app/reducers/invoice.js index bed8e818..60988dac 100644 --- a/app/reducers/invoice.js +++ b/app/reducers/invoice.js @@ -1,4 +1,5 @@ import { createSelector } from 'reselect' +import { ipcRenderer } from 'electron' import { callApi } from '../api' import { btc, usd } from '../utils' // ------------------------------------ @@ -63,13 +64,6 @@ export function getInvoices() { } } -export function receiveInvoices(data) { - return { - type: RECEIVE_INVOICES, - invoices: data.invoices.reverse() - } -} - export function sendInvoice() { return { type: SEND_INVOICE @@ -101,18 +95,15 @@ export const fetchInvoice = payreq => async (dispatch) => { return false } +// Send IPC event for invoices export const fetchInvoices = () => async (dispatch) => { - dispatch(getInvoice()) - const invoices = await callApi('invoices') - if (invoices) { - dispatch(receiveInvoices(invoices.data)) - } else { - dispatch(invoiceFailed()) - } - - return invoices + dispatch(getInvoices()) + ipcRenderer.send('lnd', { msg: 'invoices' }) } +// Receive IPC event for invoices +export const receiveInvoices = (event, { invoices }) => dispatch => dispatch({ type: RECEIVE_INVOICES, invoices }) + export const createInvoice = (amount, memo, currency, rate) => async (dispatch) => { const value = currency === 'btc' ? btc.btcToSatoshis(amount) : btc.btcToSatoshis(usd.usdToBtc(amount, rate)) diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index d750ce7f..98645466 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -3,13 +3,17 @@ import { receiveInfo } from './info' import { receivePeers } from './peers' import { receiveChannels } from './channels' import { receivePayments } from './payment' +import { receiveInvoices } from './invoice' +import { receiveBalance } from './balance' // Import all receiving IPC event handlers and pass them into createIpc const ipc = createIpc({ 'receiveInfo': receiveInfo, 'receivePeers': receivePeers, 'receiveChannels': receiveChannels, - 'receivePayments': receivePayments + 'receivePayments': receivePayments, + 'receiveInvoices': receiveInvoices, + 'receiveBalance': receiveBalance }) export default ipc \ No newline at end of file diff --git a/app/reducers/payment.js b/app/reducers/payment.js index 3d8a3f1e..107ed19f 100644 --- a/app/reducers/payment.js +++ b/app/reducers/payment.js @@ -49,13 +49,13 @@ export function paymentFailed() { } } -// Send IPC event for peers +// Send IPC event for payments export const fetchPayments = () => async (dispatch) => { dispatch(getPayments()) ipcRenderer.send('lnd', { msg: 'payments' }) } -// Receive IPC event for peers +// Receive IPC event for payments export const receivePayments = (event, { payments }) => dispatch => dispatch({ type: RECEIVE_PAYMENTS, payments }) export const payInvoice = payment_request => async (dispatch) => { From 6163187c30a0c80e2a1cf46edd3a446a1e5b2f3f Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Sun, 20 Aug 2017 14:27:12 -0500 Subject: [PATCH 06/53] feature(lnd-ipc): ipc create invoice --- app/lnd/index.js | 14 +++++++++++++- app/main.dev.js | 7 +++++++ app/reducers/invoice.js | 18 +++++++----------- app/reducers/ipc.js | 5 +++-- .../app/components/components/Form/Form.js | 8 ++------ 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/app/lnd/index.js b/app/lnd/index.js index 7cd730b4..572dee0c 100644 --- a/app/lnd/index.js +++ b/app/lnd/index.js @@ -93,11 +93,23 @@ export function balance() { return Promise.all([walletBalance, channelBalance]) } +// LND Get Wallet + Channel Balance +export function createInvoice({ memo, value }) { + return new Promise((resolve, reject) => { + lnd.addInvoice({ memo, value }, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} + export default { info, peers, allChannels, payments, invoices, - balance + balance, + createInvoice } diff --git a/app/main.dev.js b/app/main.dev.js index 98fa93ef..4a662192 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -129,6 +129,13 @@ ipcMain.on('lnd', (event, { msg, data }) => { .then(balance => event.sender.send('receiveBalance', { walletBalance: balance[0].balance, channelBalance: balance[1].balance })) .catch(error => console.log('info error: ', error)) break + case 'createInvoice': + // Balance looks like { r_hash: Buffer, payment_request: '' } + const { memo, value } = data + lnd.createInvoice({ memo, value }) + .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('info error: ', error)) + break default: return } diff --git a/app/reducers/invoice.js b/app/reducers/invoice.js index 60988dac..f58f1bcc 100644 --- a/app/reducers/invoice.js +++ b/app/reducers/invoice.js @@ -96,7 +96,7 @@ export const fetchInvoice = payreq => async (dispatch) => { } // Send IPC event for invoices -export const fetchInvoices = () => async (dispatch) => { +export const fetchInvoices = () => dispatch => { dispatch(getInvoices()) ipcRenderer.send('lnd', { msg: 'invoices' }) } @@ -104,19 +104,15 @@ export const fetchInvoices = () => async (dispatch) => { // Receive IPC event for invoices export const receiveInvoices = (event, { invoices }) => dispatch => dispatch({ type: RECEIVE_INVOICES, invoices }) -export const createInvoice = (amount, memo, currency, rate) => async (dispatch) => { +// Send IPC event for creating an invoice +export const createInvoice = (amount, memo, currency, rate) => dispatch => { const value = currency === 'btc' ? btc.btcToSatoshis(amount) : btc.btcToSatoshis(usd.usdToBtc(amount, rate)) - dispatch(sendInvoice()) - const invoice = await callApi('addinvoice', 'post', { value, memo }) - if (invoice) { - dispatch(invoiceSuccessful({ memo, value, payment_request: invoice.data.payment_request })) - } else { - dispatch(invoiceFailed()) - } - - return invoice + ipcRenderer.send('lnd', { msg: 'createInvoice', data: { value, memo } }) } + +// Receive IPC event for newly created invoice +export const createdInvoice = (event, invoice) => dispatch => dispatch({ type: INVOICE_SUCCESSFUL, invoice }) // ------------------------------------ // Action Handlers // ------------------------------------ diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index 98645466..45638d23 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -3,7 +3,7 @@ import { receiveInfo } from './info' import { receivePeers } from './peers' import { receiveChannels } from './channels' import { receivePayments } from './payment' -import { receiveInvoices } from './invoice' +import { receiveInvoices, createdInvoice } from './invoice' import { receiveBalance } from './balance' // Import all receiving IPC event handlers and pass them into createIpc @@ -13,7 +13,8 @@ const ipc = createIpc({ 'receiveChannels': receiveChannels, 'receivePayments': receivePayments, 'receiveInvoices': receiveInvoices, - 'receiveBalance': receiveBalance + 'receiveBalance': receiveBalance, + 'createdInvoice': createdInvoice }) export default ipc \ No newline at end of file diff --git a/app/routes/app/components/components/Form/Form.js b/app/routes/app/components/components/Form/Form.js index 40c56eec..a646f7fd 100644 --- a/app/routes/app/components/components/Form/Form.js +++ b/app/routes/app/components/components/Form/Form.js @@ -20,16 +20,12 @@ const Form = ({ }) => { const requestClicked = () => { createInvoice(amount, message, currency, btcTicker.price_usd) - .then((success) => { - if (success) { close() } - }) + close() } const payClicked = () => { payInvoice(payment_request) - .then((success) => { - if (success) { close() } - }) + close() } const paymentRequestOnChange = (payreq) => { From 192d0e7b21ca6ecaebe9442a80e5041898f8c22e Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Sun, 20 Aug 2017 20:58:06 -0500 Subject: [PATCH 07/53] feature(lnd-ipc): ipc form invoice --- app/lnd/index.js | 9 +++++++++ app/lnd/utils/index.js | 37 +++++++++++++++++++++++++++++++++++++ app/main.dev.js | 9 +++++++++ app/reducers/invoice.js | 29 ++++++----------------------- app/reducers/ipc.js | 3 ++- package.json | 3 ++- yarn.lock | 4 ++++ 7 files changed, 69 insertions(+), 25 deletions(-) create mode 100644 app/lnd/utils/index.js diff --git a/app/lnd/index.js b/app/lnd/index.js index 572dee0c..2f2b5dcb 100644 --- a/app/lnd/index.js +++ b/app/lnd/index.js @@ -1,5 +1,6 @@ import config from './config' import lightning from './lib/lightning' +import { decodeInvoice } from './utils' const lnd = lightning(config.lightningRpc, config.lightningHost) @@ -70,6 +71,13 @@ export function invoices() { }) } +// LND Get Invoice +export function invoice(payreq) { + return new Promise((resolve, reject) => { + resolve(decodeInvoice(payreq)) + }) +} + // LND Get Wallet Balance const walletBalance = new Promise((resolve, reject) => { lnd.walletBalance({}, (err, data) => { @@ -110,6 +118,7 @@ export default { allChannels, payments, invoices, + invoice, balance, createInvoice } diff --git a/app/lnd/utils/index.js b/app/lnd/utils/index.js new file mode 100644 index 00000000..92489035 --- /dev/null +++ b/app/lnd/utils/index.js @@ -0,0 +1,37 @@ +import zbase32 from 'zbase32' + +function convertBigEndianBufferToLong(longBuffer) { + let longValue = 0 + const byteArray = Buffer.from(longBuffer).swap64() + + for (let i = byteArray.length - 1; i >= 0; i--) { + longValue = (longValue * 256) + byteArray[i] + } + + return longValue +} + +export function decodeInvoice(payreq) { + const payreqBase32 = zbase32.decode(payreq) + + const bufferHexRotated = Buffer.from(payreqBase32).toString('hex') + const bufferHex = bufferHexRotated.substr(bufferHexRotated.length - 1, bufferHexRotated.length) + + bufferHexRotated.substr(0, bufferHexRotated.length - 1) + const buffer = Buffer.from(bufferHex, 'hex') + + const pubKeyBuffer = buffer.slice(0, 33) + const pubKeyHex = pubKeyBuffer.toString('hex') + + const paymentHashBuffer = buffer.slice(33, 65) + const paymentHashHex = paymentHashBuffer.toString('hex') + + const valueBuffer = buffer.slice(65, 73) + + const amount = convertBigEndianBufferToLong(valueBuffer) + + return { + payreq, + amount, + r_hash: paymentHashHex, + } +} \ No newline at end of file diff --git a/app/main.dev.js b/app/main.dev.js index 4a662192..a2be9e38 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -123,6 +123,15 @@ ipcMain.on('lnd', (event, { msg, data }) => { .then(invoices => event.sender.send('receiveInvoices', invoices)) .catch(error => console.log('info error: ', error)) break + case 'invoice': + // Data looks like { invoices: [] } + lnd.invoice(data.payreq) + .then(invoice => { + console.log('invoice: ', invoice) + event.sender.send('receiveInvoice', invoice) + }) + .catch(error => console.log('info error: ', error)) + break case 'balance': // Balance looks like [ { balance: '129477456' }, { balance: '243914' } ] lnd.balance() diff --git a/app/reducers/invoice.js b/app/reducers/invoice.js index f58f1bcc..5c6c25f4 100644 --- a/app/reducers/invoice.js +++ b/app/reducers/invoice.js @@ -51,13 +51,6 @@ export function receiveInvoice(invoice) { } } -export function receiveFormInvoice(formInvoice) { - return { - type: RECEIVE_FORM_INVOICE, - formInvoice - } -} - export function getInvoices() { return { type: GET_INVOICES @@ -70,31 +63,21 @@ export function sendInvoice() { } } -export function invoiceSuccessful(invoice) { - return { - type: INVOICE_SUCCESSFUL, - invoice - } -} - export function invoiceFailed() { return { type: INVOICE_FAILED } } -export const fetchInvoice = payreq => async (dispatch) => { +// Send IPC event for a specific invoice +export const fetchInvoice = payreq => dispatch => { dispatch(getInvoice()) - const invoice = await callApi(`invoice/${payreq}`, 'get') - - if (invoice) { - dispatch(receiveFormInvoice(invoice.data)) - return true - } - dispatch(invoiceFailed()) - return false + ipcRenderer.send('lnd', { msg: 'invoice', data: { payreq } }) } +// Receive IPC event for form invoice +export const receiveFormInvoice = (event, formInvoice) => dispatch => dispatch({ type: RECEIVE_FORM_INVOICE, formInvoice }) + // Send IPC event for invoices export const fetchInvoices = () => dispatch => { dispatch(getInvoices()) diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index 45638d23..97f06cee 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -3,7 +3,7 @@ import { receiveInfo } from './info' import { receivePeers } from './peers' import { receiveChannels } from './channels' import { receivePayments } from './payment' -import { receiveInvoices, createdInvoice } from './invoice' +import { receiveInvoices, createdInvoice, receiveFormInvoice } from './invoice' import { receiveBalance } from './balance' // Import all receiving IPC event handlers and pass them into createIpc @@ -13,6 +13,7 @@ const ipc = createIpc({ 'receiveChannels': receiveChannels, 'receivePayments': receivePayments, 'receiveInvoices': receiveInvoices, + 'receiveInvoice': receiveFormInvoice, 'receiveBalance': receiveBalance, 'createdInvoice': createdInvoice }) diff --git a/package.json b/package.json index 1cfa0120..6994a903 100644 --- a/package.json +++ b/package.json @@ -211,7 +211,8 @@ "reselect": "^3.0.1", "satoshi-bitcoin": "^1.0.4", "source-map-support": "^0.4.15", - "xtend": "^4.0.1" + "xtend": "^4.0.1", + "zbase32": "^0.0.2" }, "devEngines": { "node": ">=7.x", diff --git a/yarn.lock b/yarn.lock index 1ee04b9c..daedb418 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9271,6 +9271,10 @@ yauzl@2.4.1: dependencies: fd-slicer "~1.0.1" +zbase32@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/zbase32/-/zbase32-0.0.2.tgz#169c6f2130a6c27a84247017538b56826a54b283" + zip-stream@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.1.1.tgz#5216b48bbb4d2651f64d5c6e6f09eb4a7399d557" From c751dce4bd81f3aa8ddd4909bfd74ebc4d82ba33 Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Mon, 21 Aug 2017 19:21:05 -0500 Subject: [PATCH 08/53] fix(lnd-ipc): refactor methods and pass lnd to functions --- app/lnd/index.js | 121 +---------------------------- app/lnd/methods/allchannels.js | 3 + app/lnd/methods/channelbalance.js | 10 +++ app/lnd/methods/channels.js | 10 +++ app/lnd/methods/createinvoice.js | 10 +++ app/lnd/methods/index.js | 74 ++++++++++++++++++ app/lnd/methods/info.js | 10 +++ app/lnd/methods/invoice.js | 8 ++ app/lnd/methods/invoices.js | 10 +++ app/lnd/methods/payinvoice.js | 10 +++ app/lnd/methods/payments.js | 10 +++ app/lnd/methods/peers.js | 10 +++ app/lnd/methods/pendingchannels.js | 10 +++ app/lnd/methods/walletbalance.js | 10 +++ app/main.dev.js | 56 +------------ app/reducers/invoice.js | 1 - app/reducers/ipc.js | 5 +- app/reducers/payment.js | 28 ++++--- 18 files changed, 209 insertions(+), 187 deletions(-) create mode 100644 app/lnd/methods/allchannels.js create mode 100644 app/lnd/methods/channelbalance.js create mode 100644 app/lnd/methods/channels.js create mode 100644 app/lnd/methods/createinvoice.js create mode 100644 app/lnd/methods/index.js create mode 100644 app/lnd/methods/info.js create mode 100644 app/lnd/methods/invoice.js create mode 100644 app/lnd/methods/invoices.js create mode 100644 app/lnd/methods/payinvoice.js create mode 100644 app/lnd/methods/payments.js create mode 100644 app/lnd/methods/peers.js create mode 100644 app/lnd/methods/pendingchannels.js create mode 100644 app/lnd/methods/walletbalance.js diff --git a/app/lnd/index.js b/app/lnd/index.js index 2f2b5dcb..c2a87208 100644 --- a/app/lnd/index.js +++ b/app/lnd/index.js @@ -1,124 +1,7 @@ import config from './config' import lightning from './lib/lightning' -import { decodeInvoice } from './utils' +import methods from './methods' const lnd = lightning(config.lightningRpc, config.lightningHost) -// LND Get Info -export function info() { - return new Promise((resolve, reject) => { - lnd.getInfo({}, (err, data) => { - if (err) { reject(err) } - - resolve(data) - }) - }) -} - -// LND List Peers -export function peers() { - return new Promise((resolve, reject) => { - lnd.listPeers({}, (err, data) => { - if (err) { reject(err) } - - resolve(data) - }) - }) -} - -// LND List Channels -const channels = new Promise((resolve, reject) => { - lnd.listChannels({}, (err, data) => { - if (err) { reject(err) } - - resolve(data) - }) -}) - -// LND List Pending Channels -const pendingChannels = new Promise((resolve, reject) => { - lnd.pendingChannels({}, (err, data) => { - if (err) { reject(err) } - - resolve(data) - }) -}) - -// LND Get All Channels -export function allChannels() { - return Promise.all([channels, pendingChannels]) -} - -// LND Get Payments -export function payments() { - return new Promise((resolve, reject) => { - lnd.listPayments({}, (err, data) => { - if (err) { reject(err) } - - resolve(data) - }) - }) -} - -// LND Get Invoices -export function invoices() { - return new Promise((resolve, reject) => { - lnd.listInvoices({}, (err, data) => { - if (err) { reject(err) } - - resolve(data) - }) - }) -} - -// LND Get Invoice -export function invoice(payreq) { - return new Promise((resolve, reject) => { - resolve(decodeInvoice(payreq)) - }) -} - -// LND Get Wallet Balance -const walletBalance = new Promise((resolve, reject) => { - lnd.walletBalance({}, (err, data) => { - if (err) { reject(err) } - - resolve(data) - }) -}) - -// LND Get Channel Balance -const channelBalance = new Promise((resolve, reject) => { - lnd.channelBalance({}, (err, data) => { - if (err) { reject(err) } - - resolve(data) - }) -}) - -// LND Get Wallet + Channel Balance -export function balance() { - return Promise.all([walletBalance, channelBalance]) -} - -// LND Get Wallet + Channel Balance -export function createInvoice({ memo, value }) { - return new Promise((resolve, reject) => { - lnd.addInvoice({ memo, value }, (err, data) => { - if (err) { reject(err) } - - resolve(data) - }) - }) -} - -export default { - info, - peers, - allChannels, - payments, - invoices, - invoice, - balance, - createInvoice -} +export default (event, msg, data) => methods(lnd, event, msg, data) \ No newline at end of file diff --git a/app/lnd/methods/allchannels.js b/app/lnd/methods/allchannels.js new file mode 100644 index 00000000..3754e973 --- /dev/null +++ b/app/lnd/methods/allchannels.js @@ -0,0 +1,3 @@ +export default function(channels, pendingchannels) { + return Promise.all([channels, pendingchannels]) +} \ No newline at end of file diff --git a/app/lnd/methods/channelbalance.js b/app/lnd/methods/channelbalance.js new file mode 100644 index 00000000..c3c65413 --- /dev/null +++ b/app/lnd/methods/channelbalance.js @@ -0,0 +1,10 @@ +// LND Get Channel Balance +export default function channelbalance(lnd) { + return new Promise((resolve, reject) => { + lnd.channelBalance({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} \ No newline at end of file diff --git a/app/lnd/methods/channels.js b/app/lnd/methods/channels.js new file mode 100644 index 00000000..6b274762 --- /dev/null +++ b/app/lnd/methods/channels.js @@ -0,0 +1,10 @@ +// LND List Channels +export default function channels(lnd) { + return new Promise((resolve, reject) => { + lnd.listChannels({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} \ No newline at end of file diff --git a/app/lnd/methods/createinvoice.js b/app/lnd/methods/createinvoice.js new file mode 100644 index 00000000..44e37e8a --- /dev/null +++ b/app/lnd/methods/createinvoice.js @@ -0,0 +1,10 @@ +// LND Create an invoice +export function createInvoice(lnd, { memo, value }) { + return new Promise((resolve, reject) => { + lnd.addInvoice({ memo, value }, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} \ No newline at end of file diff --git a/app/lnd/methods/index.js b/app/lnd/methods/index.js new file mode 100644 index 00000000..d047f041 --- /dev/null +++ b/app/lnd/methods/index.js @@ -0,0 +1,74 @@ +import allchannels from './allchannels' +import channelbalance from './channelbalance' +import channels from './channels' +import createinvoice from './createinvoice' +import info from './info' +import invoice from './invoice' +import invoices from './invoices' +import payinvoice from './payinvoice' +import payments from './payments' +import peers from './peers' +import pendingchannels from './pendingchannels' +import walletbalance from './walletbalance' + +export default function(lnd, event, msg, data) { + switch(msg) { + case 'info': + info(lnd) + .then(info =>event.sender.send('receiveInfo', info)) + .catch(error => console.log('info error: ', error)) + break + case 'peers': + // Data looks like { peers: [] } + peers(lnd) + .then(peers => event.sender.send('receivePeers', peers)) + .catch(error => console.log('peers error: ', error)) + break + case 'channels': + // Data looks like [ { channels: [channel, channel, channel] }, { total_limbo_balance: 0, pending_open_channels: [], pending_closing_channels: [], pending_force_closing_channels: [] } ] + Promise.all([channels, pendingchannels].map(func => func(lnd))) + .then(data => event.sender.send('receiveChannels', { channels: data[0].channels, pendingChannels: data[1] })) + .catch(error => console.log('channels error: ', error)) + break + case 'payments': + // Data looks like { payments: [] } + payments(lnd) + .then(payments => event.sender.send('receivePayments', payments)) + .catch(error => console.log('payments error: ', error)) + break + case 'invoices': + // Data looks like { invoices: [] } + invoices(lnd) + .then(invoices => event.sender.send('receiveInvoices', invoices)) + .catch(error => console.log('invoices error: ', error)) + break + case 'invoice': + // Data looks like { invoices: [] } + invoice(data.payreq) + .then(invoice => event.sender.send('receiveInvoice', invoice)) + .catch(error => console.log('invoice error: ', error)) + break + case 'balance': + // Balance looks like [ { balance: '129477456' }, { balance: '243914' } ] + Promise.all([walletbalance, channelbalance].map(func => func(lnd))) + .then(balance => event.sender.send('receiveBalance', { walletBalance: balance[0].balance, channelBalance: balance[1].balance })) + .catch(error => console.log('balance error: ', error)) + break + case 'createInvoice': + // Invoice looks like { r_hash: Buffer, payment_request: '' } + const { memo, value } = data + createInvoice(lnd, { memo, value }) + .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 }) + .then(payment => event.sender.send('paymentSuccessful')) + .catch(error => console.log('sendPayment error: ', error)) + break + default: + return + } +} \ No newline at end of file diff --git a/app/lnd/methods/info.js b/app/lnd/methods/info.js new file mode 100644 index 00000000..8b704980 --- /dev/null +++ b/app/lnd/methods/info.js @@ -0,0 +1,10 @@ +// LND Get Info +export default function info(lnd) { + return new Promise((resolve, reject) => { + lnd.getInfo({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} diff --git a/app/lnd/methods/invoice.js b/app/lnd/methods/invoice.js new file mode 100644 index 00000000..b738b436 --- /dev/null +++ b/app/lnd/methods/invoice.js @@ -0,0 +1,8 @@ +import { decodeInvoice } from '../utils' + +// LND Get Invoice +export default function invoice(payreq) { + return new Promise((resolve, reject) => { + resolve(decodeInvoice(payreq)) + }) +} \ No newline at end of file diff --git a/app/lnd/methods/invoices.js b/app/lnd/methods/invoices.js new file mode 100644 index 00000000..682aeb02 --- /dev/null +++ b/app/lnd/methods/invoices.js @@ -0,0 +1,10 @@ +// LND Get Invoices +export default function invoices(lnd) { + return new Promise((resolve, reject) => { + lnd.listInvoices({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} \ No newline at end of file diff --git a/app/lnd/methods/payinvoice.js b/app/lnd/methods/payinvoice.js new file mode 100644 index 00000000..8c514afc --- /dev/null +++ b/app/lnd/methods/payinvoice.js @@ -0,0 +1,10 @@ +// LND Pay an invoice +export function payinvoice(lnd, { paymentRequest }) { + return new Promise((resolve, reject) => { + lnd.sendPaymentSync({ payment_request: paymentRequest }, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} \ No newline at end of file diff --git a/app/lnd/methods/payments.js b/app/lnd/methods/payments.js new file mode 100644 index 00000000..2182b1dc --- /dev/null +++ b/app/lnd/methods/payments.js @@ -0,0 +1,10 @@ +// LND Get Payments +export default function payments(lnd) { + return new Promise((resolve, reject) => { + lnd.listPayments({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} \ No newline at end of file diff --git a/app/lnd/methods/peers.js b/app/lnd/methods/peers.js new file mode 100644 index 00000000..a7231ef0 --- /dev/null +++ b/app/lnd/methods/peers.js @@ -0,0 +1,10 @@ +// LND List Peers +export default function peers(lnd) { + return new Promise((resolve, reject) => { + lnd.listPeers({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} \ No newline at end of file diff --git a/app/lnd/methods/pendingchannels.js b/app/lnd/methods/pendingchannels.js new file mode 100644 index 00000000..cc7d4a15 --- /dev/null +++ b/app/lnd/methods/pendingchannels.js @@ -0,0 +1,10 @@ +// LND Get Pending Channels +export default function channels(lnd) { + return new Promise((resolve, reject) => { + lnd.pendingChannels({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} \ No newline at end of file diff --git a/app/lnd/methods/walletbalance.js b/app/lnd/methods/walletbalance.js new file mode 100644 index 00000000..7a830747 --- /dev/null +++ b/app/lnd/methods/walletbalance.js @@ -0,0 +1,10 @@ +// LND Get Wallet Balance +export default function walletbalance(lnd) { + return new Promise((resolve, reject) => { + lnd.walletBalance({}, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} \ No newline at end of file diff --git a/app/main.dev.js b/app/main.dev.js index a2be9e38..bce530ae 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -93,60 +93,6 @@ app.on('ready', async () => { }); ipcMain.on('lnd', (event, { msg, data }) => { - switch(msg) { - case 'info': - lnd.info() - .then(info => event.sender.send('receiveInfo', info)) - .catch(error => console.log('info error: ', error)) - break - case 'peers': - // Data looks like { peers: [] } - lnd.peers() - .then(peers => event.sender.send('receivePeers', peers)) - .catch(error => console.log('info error: ', error)) - break - case 'channels': - // Data looks like [ { channels: [channel, channel, channel] }, { total_limbo_balance: 0, pending_open_channels: [], pending_closing_channels: [], pending_force_closing_channels: [] } ] - lnd.allChannels() - .then(data => event.sender.send('receiveChannels', { channels: data[0].channels, pendingChannels: data[1] })) - .catch(error => console.log('info error: ', error)) - break - case 'payments': - // Data looks like { payments: [] } - lnd.payments() - .then(payments => event.sender.send('receivePayments', payments)) - .catch(error => console.log('info error: ', error)) - break - case 'invoices': - // Data looks like { invoices: [] } - lnd.invoices() - .then(invoices => event.sender.send('receiveInvoices', invoices)) - .catch(error => console.log('info error: ', error)) - break - case 'invoice': - // Data looks like { invoices: [] } - lnd.invoice(data.payreq) - .then(invoice => { - console.log('invoice: ', invoice) - event.sender.send('receiveInvoice', invoice) - }) - .catch(error => console.log('info error: ', error)) - break - case 'balance': - // Balance looks like [ { balance: '129477456' }, { balance: '243914' } ] - lnd.balance() - .then(balance => event.sender.send('receiveBalance', { walletBalance: balance[0].balance, channelBalance: balance[1].balance })) - .catch(error => console.log('info error: ', error)) - break - case 'createInvoice': - // Balance looks like { r_hash: Buffer, payment_request: '' } - const { memo, value } = data - lnd.createInvoice({ memo, value }) - .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('info error: ', error)) - break - default: - return - } + lnd(event, msg, data) }) diff --git a/app/reducers/invoice.js b/app/reducers/invoice.js index 5c6c25f4..aceb5735 100644 --- a/app/reducers/invoice.js +++ b/app/reducers/invoice.js @@ -1,6 +1,5 @@ import { createSelector } from 'reselect' import { ipcRenderer } from 'electron' -import { callApi } from '../api' import { btc, usd } from '../utils' // ------------------------------------ // Constants diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index 97f06cee..bea6c641 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -2,7 +2,7 @@ import createIpc from 'redux-electron-ipc' import { receiveInfo } from './info' import { receivePeers } from './peers' import { receiveChannels } from './channels' -import { receivePayments } from './payment' +import { receivePayments, paymentSuccessful } from './payment' import { receiveInvoices, createdInvoice, receiveFormInvoice } from './invoice' import { receiveBalance } from './balance' @@ -15,7 +15,8 @@ const ipc = createIpc({ 'receiveInvoices': receiveInvoices, 'receiveInvoice': receiveFormInvoice, 'receiveBalance': receiveBalance, - 'createdInvoice': createdInvoice + 'createdInvoice': createdInvoice, + 'paymentSuccessful': paymentSuccessful }) export default ipc \ No newline at end of file diff --git a/app/reducers/payment.js b/app/reducers/payment.js index 107ed19f..547d5ffa 100644 --- a/app/reducers/payment.js +++ b/app/reducers/payment.js @@ -50,7 +50,7 @@ export function paymentFailed() { } // Send IPC event for payments -export const fetchPayments = () => async (dispatch) => { +export const fetchPayments = () => dispatch => { dispatch(getPayments()) ipcRenderer.send('lnd', { msg: 'payments' }) } @@ -58,19 +58,27 @@ export const fetchPayments = () => async (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 }) +// export const payInvoice = payment_request => async (dispatch) => { +// dispatch(sendPayment()) +// const payment = await callApi('sendpayment', 'post', { payment_request }) - if (payment) { - dispatch(fetchPayments()) - } else { - dispatch(paymentFailed()) - } +// if (payment) { +// dispatch(fetchPayments()) +// } else { +// dispatch(paymentFailed()) +// } + +// return payment +// } - return payment +export const payInvoice = paymentRequest => dispatch => { + dispatch(sendPayment()) + ipcRenderer.send('lnd', { msg: 'sendPayment', data: { paymentRequest } }) } +// Receive IPC event for successful payment +export const paymentSuccessful = () => fetchPayments() + // ------------------------------------ // Action Handlers From 2553622cbc0545811f56fcf56dd7caf0d18f2a17 Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Tue, 22 Aug 2017 01:14:27 -0500 Subject: [PATCH 09/53] feature(lnd-ipa): refactor out nodejs completely --- app/lnd/methods/connectpeer.js | 10 ++++ app/lnd/methods/disconnectpeer.js | 10 ++++ app/lnd/methods/index.js | 41 ++++++++++++++-- app/lnd/methods/openchannel.js | 18 +++++++ app/lnd/push/channel.js | 16 ++++++ app/reducers/balance.js | 1 - app/reducers/channels.js | 37 ++++++++++---- app/reducers/ipc.js | 20 ++++++-- app/reducers/payment.js | 13 ----- app/reducers/peers.js | 49 ++++++------------- app/routes/app/components/App.js | 3 -- .../app/components/components/Socket.js | 20 -------- .../components/ChannelForm/ChannelForm.js | 6 +-- .../Peers/components/PeerForm/PeerForm.js | 5 +- .../Peers/components/PeerModal/PeerModal.js | 7 +-- package.json | 1 + yarn.lock | 42 +++++++++++++++- 17 files changed, 196 insertions(+), 103 deletions(-) create mode 100644 app/lnd/methods/connectpeer.js create mode 100644 app/lnd/methods/disconnectpeer.js create mode 100644 app/lnd/methods/openchannel.js create mode 100644 app/lnd/push/channel.js delete mode 100644 app/routes/app/components/components/Socket.js 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" From fc0876b8933b58dd4ab9653af253263c6d02646d Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Tue, 22 Aug 2017 13:03:10 -0500 Subject: [PATCH 10/53] fix(lnd-ipc): fix create + pay invoice bug --- app/lnd/methods/createinvoice.js | 2 +- app/lnd/methods/index.js | 10 +++++----- app/lnd/methods/payinvoice.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/lnd/methods/createinvoice.js b/app/lnd/methods/createinvoice.js index 44e37e8a..bd8b21bc 100644 --- a/app/lnd/methods/createinvoice.js +++ b/app/lnd/methods/createinvoice.js @@ -1,5 +1,5 @@ // LND Create an invoice -export function createInvoice(lnd, { memo, value }) { +export default function createInvoice(lnd, { memo, value }) { return new Promise((resolve, reject) => { lnd.addInvoice({ memo, value }, (err, data) => { if (err) { reject(err) } diff --git a/app/lnd/methods/index.js b/app/lnd/methods/index.js index d41cd17d..1b728e9b 100644 --- a/app/lnd/methods/index.js +++ b/app/lnd/methods/index.js @@ -60,16 +60,16 @@ export default function(lnd, event, msg, data) { case 'createInvoice': // Invoice looks like { r_hash: Buffer, payment_request: '' } // { 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') }))) + createinvoice(lnd, data) + .then(invoice => event.sender.send('createdInvoice', Object.assign(invoice, { memo: data.memo, value: data.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 } // { paymentRequest } = data - sendPayment(lnd, data) - .then(payment => event.sender.send('paymentSuccessful')) - .catch(error => console.log('sendPayment error: ', error)) + payinvoice(lnd, data) + .then(({ payment_route }) => event.sender.send('paymentSuccessful', Object.assign(data, { payment_route }))) + .catch(error => console.log('payinvoice error: ', error)) break case 'openChannel': // Response is empty. Streaming updates on channel status and updates diff --git a/app/lnd/methods/payinvoice.js b/app/lnd/methods/payinvoice.js index 8c514afc..3ae8e097 100644 --- a/app/lnd/methods/payinvoice.js +++ b/app/lnd/methods/payinvoice.js @@ -1,5 +1,5 @@ // LND Pay an invoice -export function payinvoice(lnd, { paymentRequest }) { +export default function payinvoice(lnd, { paymentRequest }) { return new Promise((resolve, reject) => { lnd.sendPaymentSync({ payment_request: paymentRequest }, (err, data) => { if (err) { reject(err) } From 6c87563aa2895078e92fe063f75e18ed767add95 Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Tue, 22 Aug 2017 16:58:31 -0500 Subject: [PATCH 11/53] fix(lnd-ipc): fix linting errors --- app/api/index.js | 34 +--- app/lnd/config/index.js | 7 +- app/lnd/index.js | 2 +- app/lnd/lib/lightning.js | 9 +- app/lnd/methods/allchannels.js | 3 - app/lnd/methods/channelbalance.js | 2 +- app/lnd/methods/channels.js | 2 +- app/lnd/methods/createinvoice.js | 4 +- app/lnd/methods/index.js | 156 +++++++++--------- app/lnd/methods/invoice.js | 8 +- app/lnd/methods/invoices.js | 4 +- app/lnd/methods/openchannel.js | 23 +-- app/lnd/methods/payinvoice.js | 4 +- app/lnd/methods/payments.js | 4 +- app/lnd/methods/peers.js | 2 +- app/lnd/methods/pendingchannels.js | 2 +- app/lnd/methods/walletbalance.js | 2 +- app/lnd/push/channel.js | 6 +- app/lnd/utils/index.js | 15 +- app/reducers/activity.js | 54 ------ app/reducers/balance.js | 7 +- app/reducers/channels.js | 12 +- app/reducers/index.js | 4 +- app/reducers/invoice.js | 6 +- app/reducers/ipc.js | 34 ++-- app/reducers/payment.js | 6 +- app/reducers/peers.js | 12 +- app/reducers/ticker.js | 2 +- app/routes/app/components/App.js | 3 - .../components/ChannelForm/ChannelForm.js | 1 - 30 files changed, 180 insertions(+), 250 deletions(-) delete mode 100644 app/lnd/methods/allchannels.js delete mode 100644 app/reducers/activity.js diff --git a/app/api/index.js b/app/api/index.js index a83ab3c0..f1b5bb2f 100644 --- a/app/api/index.js +++ b/app/api/index.js @@ -1,38 +1,6 @@ import axios from 'axios' -export function callApi(endpoint, method = 'get', data = null) { - const BASE_URL = 'http://localhost:3000/api/' - let payload - - if (data) { - payload = { - headers: { - 'Content-Type': 'application/json' - }, - method, - data, - url: `${BASE_URL}${endpoint}` - } - } else { - payload = { - headers: { - 'Content-Type': 'application/json' - }, - method, - url: `${BASE_URL}${endpoint}` - } - } - - return axios(payload) - .then(response => response.data) - .catch(error => error) -} - -export function callApis(endpoints) { - return axios.all(endpoints.map(endpoint => callApi(endpoint))) -} - -export function requestTicker() { +export default function requestTicker() { const BASE_URL = 'https://api.coinmarketcap.com/v1/ticker/bitcoin/' return axios({ method: 'get', diff --git a/app/lnd/config/index.js b/app/lnd/config/index.js index e409c073..3c0f7d2f 100644 --- a/app/lnd/config/index.js +++ b/app/lnd/config/index.js @@ -1,4 +1,9 @@ +// Cert will be located depending on your machine +// Mac OS X: /Users/user/Library/Application Support/Lnd/tls.cert +// Linux: ~/.lnd/tls.cert +// Windows: TODO find out where cert is located for windows machine export default { lightningRpc: `${__dirname}/rpc.proto`, - lightningHost: 'localhost:10009' + lightningHost: 'localhost:10009', + cert: '/Users/jmow/Library/Application Support/Lnd/tls.cert' } diff --git a/app/lnd/index.js b/app/lnd/index.js index c2a87208..5660126a 100644 --- a/app/lnd/index.js +++ b/app/lnd/index.js @@ -4,4 +4,4 @@ import methods from './methods' const lnd = lightning(config.lightningRpc, config.lightningHost) -export default (event, msg, data) => methods(lnd, event, msg, data) \ No newline at end of file +export default (event, msg, data) => methods(lnd, event, msg, data) diff --git a/app/lnd/lib/lightning.js b/app/lnd/lib/lightning.js index fb8cdc1e..7162f537 100644 --- a/app/lnd/lib/lightning.js +++ b/app/lnd/lib/lightning.js @@ -1,13 +1,14 @@ import fs from 'fs' import grpc from 'grpc' +import config from '../config' -module.exports = (path, host, cert) => { - process.env['GRPC_SSL_CIPHER_SUITES'] = 'HIGH+ECDSA' +module.exports = (path, host) => { + process.env.GRPC_SSL_CIPHER_SUITES = 'HIGH+ECDSA' const rpc = grpc.load(path) - const lndCert = fs.readFileSync('/Users/jmow/Library/Application Support/Lnd/tls.cert') + const lndCert = fs.readFileSync(config.cert) const credentials = grpc.credentials.createSsl(lndCert) return new rpc.lnrpc.Lightning(host, credentials) -} \ No newline at end of file +} diff --git a/app/lnd/methods/allchannels.js b/app/lnd/methods/allchannels.js deleted file mode 100644 index 3754e973..00000000 --- a/app/lnd/methods/allchannels.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function(channels, pendingchannels) { - return Promise.all([channels, pendingchannels]) -} \ No newline at end of file diff --git a/app/lnd/methods/channelbalance.js b/app/lnd/methods/channelbalance.js index c3c65413..135a6b6e 100644 --- a/app/lnd/methods/channelbalance.js +++ b/app/lnd/methods/channelbalance.js @@ -7,4 +7,4 @@ export default function channelbalance(lnd) { resolve(data) }) }) -} \ No newline at end of file +} diff --git a/app/lnd/methods/channels.js b/app/lnd/methods/channels.js index 6b274762..512fec27 100644 --- a/app/lnd/methods/channels.js +++ b/app/lnd/methods/channels.js @@ -7,4 +7,4 @@ export default function channels(lnd) { resolve(data) }) }) -} \ No newline at end of file +} diff --git a/app/lnd/methods/createinvoice.js b/app/lnd/methods/createinvoice.js index bd8b21bc..c7fbba35 100644 --- a/app/lnd/methods/createinvoice.js +++ b/app/lnd/methods/createinvoice.js @@ -3,8 +3,8 @@ export default function createInvoice(lnd, { memo, value }) { return new Promise((resolve, reject) => { lnd.addInvoice({ memo, value }, (err, data) => { if (err) { reject(err) } - + resolve(data) }) }) -} \ No newline at end of file +} diff --git a/app/lnd/methods/index.js b/app/lnd/methods/index.js index 1b728e9b..6636bb6f 100644 --- a/app/lnd/methods/index.js +++ b/app/lnd/methods/index.js @@ -1,4 +1,3 @@ -import allchannels from './allchannels' import channelbalance from './channelbalance' import channels from './channels' import connectpeer from './connectpeer' @@ -14,94 +13,101 @@ import peers from './peers' import pendingchannels from './pendingchannels' import walletbalance from './walletbalance' -export default function(lnd, event, msg, data) { - switch(msg) { +export default function (lnd, event, msg, data) { + switch (msg) { case 'info': - info(lnd) - .then(info =>event.sender.send('receiveInfo', info)) - .catch(error => console.log('info error: ', error)) - break - case 'peers': + info(lnd) + .then(infoData => event.sender.send('receiveInfo', infoData)) + .catch(error => console.log('info error: ', error)) + break + case 'peers': // Data looks like { peers: [] } - peers(lnd) - .then(peers => event.sender.send('receivePeers', peers)) - .catch(error => console.log('peers error: ', error)) - break - case 'channels': - // Data looks like [ { channels: [channel, channel, channel] }, { total_limbo_balance: 0, pending_open_channels: [], pending_closing_channels: [], pending_force_closing_channels: [] } ] - Promise.all([channels, pendingchannels].map(func => func(lnd))) - .then(data => event.sender.send('receiveChannels', { channels: data[0].channels, pendingChannels: data[1] })) - .catch(error => console.log('channels error: ', error)) - break - case 'payments': + peers(lnd) + .then(peersData => event.sender.send('receivePeers', peersData)) + .catch(error => console.log('peers error: ', error)) + break + case 'channels': + // Data looks like + // [ { channels: [] }, { total_limbo_balance: 0, pending_open_channels: [], pending_closing_channels: [], pending_force_closing_channels: [] } ] + Promise.all([channels, pendingchannels].map(func => func(lnd))) + .then(channelsData => + event.sender.send('receiveChannels', { channels: channelsData[0].channels, pendingChannels: channelsData[1] }) + ) + .catch(error => console.log('channels error: ', error)) + break + case 'payments': // Data looks like { payments: [] } - payments(lnd) - .then(payments => event.sender.send('receivePayments', payments)) - .catch(error => console.log('payments error: ', error)) - break - case 'invoices': + payments(lnd) + .then(paymentsData => event.sender.send('receivePayments', paymentsData)) + .catch(error => console.log('payments error: ', error)) + break + case 'invoices': // Data looks like { invoices: [] } - invoices(lnd) - .then(invoices => event.sender.send('receiveInvoices', invoices)) - .catch(error => console.log('invoices error: ', error)) - break - case 'invoice': + invoices(lnd) + .then(invoicesData => event.sender.send('receiveInvoices', invoicesData)) + .catch(error => console.log('invoices error: ', error)) + break + case 'invoice': // Data looks like { invoices: [] } - invoice(data.payreq) - .then(invoice => event.sender.send('receiveInvoice', invoice)) - .catch(error => console.log('invoice error: ', error)) - break - case 'balance': + invoice(data.payreq) + .then(invoiceData => event.sender.send('receiveInvoice', invoiceData)) + .catch(error => console.log('invoice error: ', error)) + break + case 'balance': // Balance looks like [ { balance: '129477456' }, { balance: '243914' } ] - Promise.all([walletbalance, channelbalance].map(func => func(lnd))) - .then(balance => event.sender.send('receiveBalance', { walletBalance: balance[0].balance, channelBalance: balance[1].balance })) - .catch(error => console.log('balance error: ', error)) - break - case 'createInvoice': + Promise.all([walletbalance, channelbalance].map(func => func(lnd))) + .then(balance => event.sender.send('receiveBalance', { walletBalance: balance[0].balance, channelBalance: balance[1].balance })) + .catch(error => console.log('balance error: ', error)) + break + case 'createInvoice': // Invoice looks like { r_hash: Buffer, payment_request: '' } // { memo, value } = data - createinvoice(lnd, data) - .then(invoice => event.sender.send('createdInvoice', Object.assign(invoice, { memo: data.memo, value: data.value, r_hash: new Buffer(invoice.r_hash,'hex').toString('hex') }))) - .catch(error => console.log('createInvoice error: ', error)) - break - case 'sendPayment': + createinvoice(lnd, data) + .then(newinvoice => + event.sender.send( + 'createdInvoice', + Object.assign(newinvoice, { memo: data.memo, value: data.value, r_hash: new Buffer(newinvoice.r_hash, 'hex').toString('hex') }) + ) + ) + .catch(error => console.log('createInvoice error: ', error)) + break + case 'sendPayment': // Payment looks like { payment_preimage: Buffer, payment_route: Object } // { paymentRequest } = data - payinvoice(lnd, data) - .then(({ payment_route }) => event.sender.send('paymentSuccessful', Object.assign(data, { payment_route }))) - .catch(error => console.log('payinvoice error: ', error)) - break - case 'openChannel': + payinvoice(lnd, data) + .then(({ payment_route }) => event.sender.send('paymentSuccessful', Object.assign(data, { payment_route }))) + .catch(error => console.log('payinvoice 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': + 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': + 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 + 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: } -} \ No newline at end of file +} diff --git a/app/lnd/methods/invoice.js b/app/lnd/methods/invoice.js index b738b436..2c1f4efa 100644 --- a/app/lnd/methods/invoice.js +++ b/app/lnd/methods/invoice.js @@ -3,6 +3,10 @@ import { decodeInvoice } from '../utils' // LND Get Invoice export default function invoice(payreq) { return new Promise((resolve, reject) => { - resolve(decodeInvoice(payreq)) + try { + resolve(decodeInvoice(payreq)) + } catch (error) { + reject(error) + } }) -} \ No newline at end of file +} diff --git a/app/lnd/methods/invoices.js b/app/lnd/methods/invoices.js index 682aeb02..9cad0746 100644 --- a/app/lnd/methods/invoices.js +++ b/app/lnd/methods/invoices.js @@ -3,8 +3,8 @@ export default function invoices(lnd) { return new Promise((resolve, reject) => { lnd.listInvoices({}, (err, data) => { if (err) { reject(err) } - + resolve(data) }) }) -} \ No newline at end of file +} diff --git a/app/lnd/methods/openchannel.js b/app/lnd/methods/openchannel.js index a75f7400..1282ee87 100644 --- a/app/lnd/methods/openchannel.js +++ b/app/lnd/methods/openchannel.js @@ -1,18 +1,19 @@ -import pushchannel from '../push/channel' import bitcore from 'bitcore-lib' +import pushchannel from '../push/channel' + const BufferUtil = bitcore.util.buffer -export default function openchannel(lnd, event, data) { - const { pubkey, localamt, pushamt } = data - const payload = { +export default function openchannel(lnd, event, payload) { + const { pubkey, localamt, pushamt } = payload + const res = { node_pubkey: BufferUtil.hexToBuffer(pubkey), - local_funding_amount: Number(localamt), - push_sat: Number(pushamt) + 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)) + return new Promise((resolve, reject) => + pushchannel(lnd, event, res) + .then(data => resolve(data)) + .catch(error => reject(error)) ) -} \ No newline at end of file +} diff --git a/app/lnd/methods/payinvoice.js b/app/lnd/methods/payinvoice.js index 3ae8e097..43bfa3b8 100644 --- a/app/lnd/methods/payinvoice.js +++ b/app/lnd/methods/payinvoice.js @@ -3,8 +3,8 @@ export default function payinvoice(lnd, { paymentRequest }) { return new Promise((resolve, reject) => { lnd.sendPaymentSync({ payment_request: paymentRequest }, (err, data) => { if (err) { reject(err) } - + resolve(data) }) }) -} \ No newline at end of file +} diff --git a/app/lnd/methods/payments.js b/app/lnd/methods/payments.js index 2182b1dc..a276c3bd 100644 --- a/app/lnd/methods/payments.js +++ b/app/lnd/methods/payments.js @@ -3,8 +3,8 @@ export default function payments(lnd) { return new Promise((resolve, reject) => { lnd.listPayments({}, (err, data) => { if (err) { reject(err) } - + resolve(data) }) }) -} \ No newline at end of file +} diff --git a/app/lnd/methods/peers.js b/app/lnd/methods/peers.js index a7231ef0..1b604c1c 100644 --- a/app/lnd/methods/peers.js +++ b/app/lnd/methods/peers.js @@ -7,4 +7,4 @@ export default function peers(lnd) { resolve(data) }) }) -} \ No newline at end of file +} diff --git a/app/lnd/methods/pendingchannels.js b/app/lnd/methods/pendingchannels.js index cc7d4a15..4408e750 100644 --- a/app/lnd/methods/pendingchannels.js +++ b/app/lnd/methods/pendingchannels.js @@ -7,4 +7,4 @@ export default function channels(lnd) { resolve(data) }) }) -} \ No newline at end of file +} diff --git a/app/lnd/methods/walletbalance.js b/app/lnd/methods/walletbalance.js index 7a830747..e6db48a6 100644 --- a/app/lnd/methods/walletbalance.js +++ b/app/lnd/methods/walletbalance.js @@ -7,4 +7,4 @@ export default function walletbalance(lnd) { resolve(data) }) }) -} \ No newline at end of file +} diff --git a/app/lnd/push/channel.js b/app/lnd/push/channel.js index 7b48da38..4a115dec 100644 --- a/app/lnd/push/channel.js +++ b/app/lnd/push/channel.js @@ -2,15 +2,15 @@ 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/lnd/utils/index.js b/app/lnd/utils/index.js index 92489035..39cf69c0 100644 --- a/app/lnd/utils/index.js +++ b/app/lnd/utils/index.js @@ -3,8 +3,8 @@ import zbase32 from 'zbase32' function convertBigEndianBufferToLong(longBuffer) { let longValue = 0 const byteArray = Buffer.from(longBuffer).swap64() - - for (let i = byteArray.length - 1; i >= 0; i--) { + + for (let i = byteArray.length - 1; i >= 0; i -= 1) { longValue = (longValue * 256) + byteArray[i] } @@ -19,9 +19,6 @@ export function decodeInvoice(payreq) { + bufferHexRotated.substr(0, bufferHexRotated.length - 1) const buffer = Buffer.from(bufferHex, 'hex') - const pubKeyBuffer = buffer.slice(0, 33) - const pubKeyHex = pubKeyBuffer.toString('hex') - const paymentHashBuffer = buffer.slice(33, 65) const paymentHashHex = paymentHashBuffer.toString('hex') @@ -32,6 +29,10 @@ export function decodeInvoice(payreq) { return { payreq, amount, - r_hash: paymentHashHex, + r_hash: paymentHashHex } -} \ No newline at end of file +} + +export default { + decodeInvoice +} diff --git a/app/reducers/activity.js b/app/reducers/activity.js deleted file mode 100644 index cb391f4d..00000000 --- a/app/reducers/activity.js +++ /dev/null @@ -1,54 +0,0 @@ -import { callApis } from '../api' -// ------------------------------------ -// Constants -// ------------------------------------ -export const GET_ACTIVITY = 'GET_ACTIVITY' -export const RECEIVE_ACTIVITY = 'RECEIVE_ACTIVITY' - -// ------------------------------------ -// Actions -// ------------------------------------ -export function getActivity() { - return { - type: GET_ACTIVITY - } -} - -export function receiveActvity(data) { - return { - type: RECEIVE_ACTIVITY, - payments: data[0].data.payments.reverse(), - invoices: data[1].data.invoices.reverse() - } -} - -export const fetchActivity = () => async (dispatch) => { - dispatch(getActivity()) - const activity = await callApis(['payments', 'invoices']) - dispatch(receiveActvity(activity)) -} - -// ------------------------------------ -// Action Handlers -// ------------------------------------ -const ACTION_HANDLERS = { - [GET_ACTIVITY]: state => ({ ...state, activityLoading: true }), - [RECEIVE_ACTIVITY]: (state, { payments, invoices }) => ( - { ...state, activityLoading: false, payments, invoices } - ) -} - -// ------------------------------------ -// Reducer -// ------------------------------------ -const initialState = { - activityLoading: false, - payments: [], - invoices: [] -} - -export default function activityReducer(state = initialState, action) { - const handler = ACTION_HANDLERS[action.type] - - return handler ? handler(state, action) : state -} diff --git a/app/reducers/balance.js b/app/reducers/balance.js index ab5a3a13..db49a64c 100644 --- a/app/reducers/balance.js +++ b/app/reducers/balance.js @@ -14,13 +14,16 @@ export function getBalance() { } } +// Send IPC event for balance export const fetchBalance = () => async (dispatch) => { dispatch(getBalance()) ipcRenderer.send('lnd', { msg: 'balance' }) } -// Receive IPC event for peers -export const receiveBalance = (event, { walletBalance, channelBalance }) => dispatch => dispatch({ type: RECEIVE_BALANCE, walletBalance, channelBalance }) +// Receive IPC event for balance +export const receiveBalance = (event, { walletBalance, channelBalance }) => dispatch => ( + dispatch({ type: RECEIVE_BALANCE, walletBalance, channelBalance }) +) // ------------------------------------ // Action Handlers diff --git a/app/reducers/channels.js b/app/reducers/channels.js index 898876f6..9003c5b5 100644 --- a/app/reducers/channels.js +++ b/app/reducers/channels.js @@ -66,34 +66,34 @@ export const fetchChannels = () => async (dispatch) => { export const receiveChannels = (event, { channels, pendingChannels }) => dispatch => dispatch({ type: RECEIVE_CHANNELS, channels, pendingChannels }) // Send IPC event for opening a channel -export const openChannel = ({ pubkey, localamt, pushamt }) => dispatch => { +export const openChannel = ({ pubkey, localamt, pushamt }) => (dispatch) => { dispatch(openingChannel()) ipcRenderer.send('lnd', { msg: 'openChannel', data: { pubkey, localamt, pushamt } }) } // TODO: Decide how to handle streamed updates for channels // Receive IPC event for openChannel -export const channelSuccessful = (event, { channel }) => dispatch => { +export const channelSuccessful = () => (dispatch) => { dispatch(fetchChannels()) } // Receive IPC event for updated channel -export const pushchannelupdated = (event, data) => dispatch => { +export const pushchannelupdated = () => (dispatch) => { dispatch(fetchChannels()) } // Receive IPC event for channel end -export const pushchannelend = (event, data) => dispatch => { +export const pushchannelend = () => (dispatch) => { dispatch(fetchChannels()) } // Receive IPC event for channel error -export const pushchannelerror = (event, data) => dispatch => { +export const pushchannelerror = () => (dispatch) => { dispatch(fetchChannels()) } // Receive IPC event for channel status -export const pushchannelstatus = (event, data) => dispatch => { +export const pushchannelstatus = () => (dispatch) => { dispatch(fetchChannels()) } diff --git a/app/reducers/index.js b/app/reducers/index.js index a77099ec..d7df67f8 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -9,7 +9,6 @@ import peers from './peers' import channels from './channels' import form from './form' import invoice from './invoice' -import activity from './activity' const rootReducer = combineReducers({ router, @@ -20,8 +19,7 @@ const rootReducer = combineReducers({ peers, channels, form, - invoice, - activity + invoice }) export default rootReducer diff --git a/app/reducers/invoice.js b/app/reducers/invoice.js index aceb5735..8cedcc2b 100644 --- a/app/reducers/invoice.js +++ b/app/reducers/invoice.js @@ -69,7 +69,7 @@ export function invoiceFailed() { } // Send IPC event for a specific invoice -export const fetchInvoice = payreq => dispatch => { +export const fetchInvoice = payreq => (dispatch) => { dispatch(getInvoice()) ipcRenderer.send('lnd', { msg: 'invoice', data: { payreq } }) } @@ -78,7 +78,7 @@ export const fetchInvoice = payreq => dispatch => { export const receiveFormInvoice = (event, formInvoice) => dispatch => dispatch({ type: RECEIVE_FORM_INVOICE, formInvoice }) // Send IPC event for invoices -export const fetchInvoices = () => dispatch => { +export const fetchInvoices = () => (dispatch) => { dispatch(getInvoices()) ipcRenderer.send('lnd', { msg: 'invoices' }) } @@ -87,7 +87,7 @@ export const fetchInvoices = () => dispatch => { export const receiveInvoices = (event, { invoices }) => dispatch => dispatch({ type: RECEIVE_INVOICES, invoices }) // Send IPC event for creating an invoice -export const createInvoice = (amount, memo, currency, rate) => dispatch => { +export const createInvoice = (amount, memo, currency, rate) => (dispatch) => { const value = currency === 'btc' ? btc.btcToSatoshis(amount) : btc.btcToSatoshis(usd.usdToBtc(amount, rate)) dispatch(sendInvoice()) ipcRenderer.send('lnd', { msg: 'createInvoice', data: { value, memo } }) diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index 4980ed05..3d2ade7c 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -15,22 +15,22 @@ import { receiveBalance } from './balance' // Import all receiving IPC event handlers and pass them into createIpc const ipc = createIpc({ - 'receiveInfo': receiveInfo, - 'receivePeers': receivePeers, - 'receiveChannels': receiveChannels, - 'receivePayments': receivePayments, - 'receiveInvoices': receiveInvoices, - 'receiveInvoice': receiveFormInvoice, - 'receiveBalance': receiveBalance, - 'createdInvoice': createdInvoice, - 'paymentSuccessful': paymentSuccessful, - 'channelSuccessful': channelSuccessful, - 'pushchannelupdated': pushchannelupdated, - 'pushchannelend': pushchannelend, - 'pushchannelerror': pushchannelerror, - 'pushchannelstatus': pushchannelstatus, - 'connectSuccess': connectSuccess, - 'disconnectSuccess': disconnectSuccess + receiveInfo, + receivePeers, + receiveChannels, + receivePayments, + receiveInvoices, + receiveInvoice: receiveFormInvoice, + receiveBalance, + createdInvoice, + paymentSuccessful, + channelSuccessful, + pushchannelupdated, + pushchannelend, + pushchannelerror, + pushchannelstatus, + connectSuccess, + disconnectSuccess }) -export default ipc \ No newline at end of file +export default ipc diff --git a/app/reducers/payment.js b/app/reducers/payment.js index 99378969..448214f4 100644 --- a/app/reducers/payment.js +++ b/app/reducers/payment.js @@ -1,6 +1,5 @@ import { createSelector } from 'reselect' import { ipcRenderer } from 'electron' -import { callApi } from '../api' // ------------------------------------ // Constants @@ -50,7 +49,7 @@ export function paymentFailed() { } // Send IPC event for payments -export const fetchPayments = () => dispatch => { +export const fetchPayments = () => (dispatch) => { dispatch(getPayments()) ipcRenderer.send('lnd', { msg: 'payments' }) } @@ -58,12 +57,13 @@ export const fetchPayments = () => dispatch => { // Receive IPC event for payments export const receivePayments = (event, { payments }) => dispatch => dispatch({ type: RECEIVE_PAYMENTS, payments }) -export const payInvoice = paymentRequest => dispatch => { +export const payInvoice = paymentRequest => (dispatch) => { dispatch(sendPayment()) ipcRenderer.send('lnd', { msg: 'sendPayment', data: { paymentRequest } }) } // Receive IPC event for successful payment +// TODO: Add payment to state, not a total re-fetch export const paymentSuccessful = () => fetchPayments() diff --git a/app/reducers/peers.js b/app/reducers/peers.js index f75e8505..e6aec70e 100644 --- a/app/reducers/peers.js +++ b/app/reducers/peers.js @@ -75,7 +75,7 @@ export const fetchPeers = () => async (dispatch) => { export const receivePeers = (event, { peers }) => dispatch => dispatch({ type: RECEIVE_PEERS, peers }) // Send IPC event for connecting to a peer -export const connectRequest = ({ pubkey, host }) => dispatch => { +export const connectRequest = ({ pubkey, host }) => (dispatch) => { dispatch(connectPeer()) ipcRenderer.send('lnd', { msg: 'connectPeer', data: { pubkey, host } }) } @@ -84,7 +84,7 @@ export const connectRequest = ({ pubkey, host }) => dispatch => { export const connectSuccess = (event, peer) => dispatch => dispatch({ type: CONNECT_SUCCESS, peer }) // Send IPC send for disconnecting from a peer -export const disconnectRequest = ({ pubkey }) => dispatch => { +export const disconnectRequest = ({ pubkey }) => (dispatch) => { dispatch(disconnectPeer()) ipcRenderer.send('lnd', { msg: 'disconnectPeer', data: { pubkey } }) } @@ -97,11 +97,15 @@ export const disconnectSuccess = (event, { pubkey }) => dispatch => dispatch({ t // ------------------------------------ const ACTION_HANDLERS = { [DISCONNECT_PEER]: state => ({ ...state, disconnecting: true }), - [DISCONNECT_SUCCESS]: (state, { pubkey }) => ({ ...state, disconnecting: false, peer: null, 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, peerForm: { pubkey: '', host: '', isOpen: 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/reducers/ticker.js b/app/reducers/ticker.js index 552d98b2..6e9a5fdb 100644 --- a/app/reducers/ticker.js +++ b/app/reducers/ticker.js @@ -1,4 +1,4 @@ -import { requestTicker } from '../api' +import requestTicker from '../api' // ------------------------------------ // Constants // ------------------------------------ diff --git a/app/routes/app/components/App.js b/app/routes/app/components/App.js index 8c06d483..1571f911 100644 --- a/app/routes/app/components/App.js +++ b/app/routes/app/components/App.js @@ -1,4 +1,3 @@ -import { ipcRenderer } from 'electron' import React, { Component } from 'react' import PropTypes from 'prop-types' import Form from './components/Form' @@ -29,7 +28,6 @@ class App extends Component { setForm, createInvoice, payInvoice, - fetchChannels, fetchInvoice, children } = this.props @@ -83,7 +81,6 @@ App.propTypes = { setForm: PropTypes.func.isRequired, createInvoice: PropTypes.func.isRequired, payInvoice: PropTypes.func.isRequired, - fetchChannels: PropTypes.func.isRequired, fetchInvoice: PropTypes.func.isRequired, children: PropTypes.object.isRequired } 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 01329542..fdc3160e 100644 --- a/app/routes/wallet/components/components/Channels/components/ChannelForm/ChannelForm.js +++ b/app/routes/wallet/components/components/Channels/components/ChannelForm/ChannelForm.js @@ -14,7 +14,6 @@ const ChannelForm = ({ form, setForm, ticker, peers, openChannel }) => { openChannel({ pubkey: node_key, localamt, pushamt }) setForm({ isOpen: false }) - } const customStyles = { From 4dc86d982c553df510f3da65fb5bd9523af0176c Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Tue, 22 Aug 2017 19:32:32 -0500 Subject: [PATCH 12/53] fix(lnd-ipc): allow console logs in lnd methods for now --- app/lnd/methods/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/lnd/methods/index.js b/app/lnd/methods/index.js index 6636bb6f..3f35be1b 100644 --- a/app/lnd/methods/index.js +++ b/app/lnd/methods/index.js @@ -1,3 +1,5 @@ +/* eslint no-console: 0 */ // --> OFF + import channelbalance from './channelbalance' import channels from './channels' import connectpeer from './connectpeer' From 993e263eacad7c6cbb69ded4dd50c5cd578a1d4e Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Tue, 22 Aug 2017 19:44:32 -0500 Subject: [PATCH 13/53] fix(lnd-ipc): remove old api tests --- test/api/index.spec.js | 55 ------------------------------------------ 1 file changed, 55 deletions(-) delete mode 100644 test/api/index.spec.js diff --git a/test/api/index.spec.js b/test/api/index.spec.js deleted file mode 100644 index 927b9b5f..00000000 --- a/test/api/index.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -import { callApi } from '../../app/api' - -describe('API', () => { - describe('getinfo', () => { - it('is synced to the chain', async () => { - const info = await callApi('info') - expect(info.data.synced_to_chain).toEqual(true) - }) - - it('only supports 1 chain at a time', async () => { - const info = await callApi('info') - expect(info.data.chains.length).toEqual(1) - }) - }) - - describe('balance', () => { - it('returns wallet balance', async () => { - const wallet_balances = await callApi('wallet_balance') - expect(typeof (wallet_balances.data.balance)).toEqual('string') - }) - - it('returns channel balance', async () => { - const channel_balances = await callApi('channel_balance') - expect(typeof (channel_balances.data.balance)).toEqual('string') - }) - }) - - describe('peers', () => { - it('peers is an array', async () => { - const peers = await callApi('peers') - expect(Array.isArray(peers.data.peers)).toEqual(true) - }) - }) - - describe('channels', () => { - it('channels is an array', async () => { - const channels = await callApi('channels') - expect(Array.isArray(channels.data.channels)).toEqual(true) - }) - }) - - describe('invoices', () => { - it('invoices is an array', async () => { - const invoices = await callApi('invoices') - expect(Array.isArray(invoices.data.invoices)).toEqual(true) - }) - }) - - describe('payments', () => { - it('payments is an array', async () => { - const payments = await callApi('payments') - expect(Array.isArray(payments.data.payments)).toEqual(true) - }) - }) -}) From a95488d7bc08270c3855a7766060e4e41b3ae897 Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Tue, 22 Aug 2017 20:51:50 -0500 Subject: [PATCH 14/53] fix(README): update readme to reflect the new changes (removing node/express dependancy) --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c8e0da24..78877ec2 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,9 @@ Join us on [slack](https://join.slack.com/t/zaphq/shared_invite/MjI2MTY4NTcwMDUy * **An up and running BTCD** * **An up and running LND** - see [install.md](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md) -* **Zap Node.js** - see [Zap Node.js](https://github.com/LN-Zap/zap-nodejs) * **Node.js version >= 7 and npm version >= 4.** -*For now Zap assumes you are running BTCD, LND and Zap Node.js (will change soon).* +*For now Zap assumes you are running BTCD, LND* ## Install @@ -32,6 +31,18 @@ After installing the above requirements, clone the repo via git: git clone https://github.com/LN-Zap/zap-desktop.git ``` +After the repo is cloned, paste the path to your cert in app/lnd/config/index.js: +```bash +// Cert will be located depending on your machine +// Mac OS X: /Users/user/Library/Application Support/Lnd/tls.cert +// Linux: ~/.lnd/tls.cert +// Windows: TODO find out where cert is located for windows machine +export default { + ..., + cert: '/path/to/cert/tls.cert' +} +``` + And then install dependencies with yarn ```bash @@ -61,7 +72,7 @@ Please see the [contributing guide](https://github.com/LN-Zap/zap-desktop/blob/m Join us on [slack](https://join.slack.com/t/zaphq/shared_invite/MjI2MTY4NTcwMDUyLTE1MDI2OTA0ODAtNTRjMTY4YTNjNA) before tackling a todo to avoid duplicate work. This list will be updated daily to show what todos are being worked on ### Refactor -- [ ] Move Node.js proxy to [ipcRenderer](https://electron.atom.io/docs/api/ipc-renderer/) (roasbeef recommendation) +- [x] Move Node.js proxy to ipcRenderer. [Done](https://github.com/LN-Zap/zap-desktop/pull/4) - [ ] Use two package.json [structure](https://github.com/electron-userland/electron-builder/wiki/Two-package.json-Structure) - [ ] General refactor (I know this TODO sucks but the code is a bit sloppy still) From b90c480d2286520265be517e30983aa101965a8b Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Tue, 22 Aug 2017 20:53:01 -0500 Subject: [PATCH 15/53] fix(README): make the MAC OS X path more clear --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78877ec2..27b415de 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ git clone https://github.com/LN-Zap/zap-desktop.git After the repo is cloned, paste the path to your cert in app/lnd/config/index.js: ```bash // Cert will be located depending on your machine -// Mac OS X: /Users/user/Library/Application Support/Lnd/tls.cert +// Mac OS X: /Users/{your_user_name}/Library/Application Support/Lnd/tls.cert // Linux: ~/.lnd/tls.cert // Windows: TODO find out where cert is located for windows machine export default { From 7dcde1b64cefbb827c55c5e493b8ae7be84c7c0f Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Wed, 23 Aug 2017 14:32:51 -0500 Subject: [PATCH 16/53] feature(address): hard code basic address html mock w/ basic reducer --- app/reducers/address.js | 46 ++++++++++++++++++++++++ app/reducers/index.js | 4 ++- app/routes/wallet/components/Wallet.js | 14 ++++++-- app/routes/wallet/components/Wallet.scss | 44 +++++++++++++++++++++-- 4 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 app/reducers/address.js diff --git a/app/reducers/address.js b/app/reducers/address.js new file mode 100644 index 00000000..426119a5 --- /dev/null +++ b/app/reducers/address.js @@ -0,0 +1,46 @@ +import { ipcRenderer } from 'electron' +// ------------------------------------ +// Constants +// ------------------------------------ +export const GET_ADDRESS = 'GET_ADDRESS' +export const RECEIVE_ADDRESS = 'RECEIVE_ADDRESS' + +// ------------------------------------ +// Actions +// ------------------------------------ +export function getAddress() { + return { + type: GET_ADDRESS + } +} + +// Send IPC event for getinfo +export const newAddress = (type) => async (dispatch) => { + dispatch(getAddress()) + ipcRenderer.send('lnd', { msg: 'newaddress' }) +} + +// Receive IPC event for info +export const receiveAddress = (event, address) => dispatch => dispatch({ type: RECEIVE_INFO, address }) + +// ------------------------------------ +// Action Handlers +// ------------------------------------ +const ACTION_HANDLERS = { + [GET_ADDRESS]: state => ({ ...state, infoLoading: true }), + [RECEIVE_ADDRESS]: (state, { address }) => ({ ...state, addressLoading: false, address }) +} + +// ------------------------------------ +// Reducer +// ------------------------------------ +const initialState = { + addressLoading: false, + address: '' +} + +export default function addressReducer(state = initialState, action) { + const handler = ACTION_HANDLERS[action.type] + + return handler ? handler(state, action) : state +} diff --git a/app/reducers/index.js b/app/reducers/index.js index d7df67f8..9126ea8b 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -9,6 +9,7 @@ import peers from './peers' import channels from './channels' import form from './form' import invoice from './invoice' +import address from './address' const rootReducer = combineReducers({ router, @@ -19,7 +20,8 @@ const rootReducer = combineReducers({ peers, channels, form, - invoice + invoice, + address }) export default rootReducer diff --git a/app/routes/wallet/components/Wallet.js b/app/routes/wallet/components/Wallet.js index b6c69a58..6c01efbe 100644 --- a/app/routes/wallet/components/Wallet.js +++ b/app/routes/wallet/components/Wallet.js @@ -35,8 +35,18 @@ class Wallet extends Component { return (
- -

{info.data.identity_pubkey}

+
+ +

{info.data.identity_pubkey}

+
+ New wallet address +
+ p2wkh + np2wkh + p2pkh +
+
+
Date: Wed, 23 Aug 2017 18:00:31 -0500 Subject: [PATCH 17/53] feature(newaddress): tie frontend to LND, creating and displaying real addresses --- app/lnd/methods/index.js | 6 ++++++ app/lnd/methods/newaddress.js | 10 ++++++++++ app/reducers/address.js | 13 ++++++++++--- app/reducers/ipc.js | 4 +++- app/routes/wallet/components/Wallet.js | 10 +++++++--- app/routes/wallet/components/Wallet.scss | 11 +++++++++-- app/routes/wallet/containers/WalletContainer.js | 4 ++++ 7 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 app/lnd/methods/newaddress.js diff --git a/app/lnd/methods/index.js b/app/lnd/methods/index.js index 3f35be1b..ec863547 100644 --- a/app/lnd/methods/index.js +++ b/app/lnd/methods/index.js @@ -8,6 +8,7 @@ import disconnectpeer from './disconnectpeer' import info from './info' import invoice from './invoice' import invoices from './invoices' +import newaddress from './newaddress' import openchannel from './openchannel' import payinvoice from './payinvoice' import payments from './payments' @@ -22,6 +23,11 @@ export default function (lnd, event, msg, data) { .then(infoData => event.sender.send('receiveInfo', infoData)) .catch(error => console.log('info error: ', error)) break + case 'newaddress': + newaddress(lnd, data.type) + .then(({ address }) => event.sender.send('receiveAddress', address)) + .catch(error => console.log('newaddress error: ', error)) + break case 'peers': // Data looks like { peers: [] } peers(lnd) diff --git a/app/lnd/methods/newaddress.js b/app/lnd/methods/newaddress.js new file mode 100644 index 00000000..d2a91d1f --- /dev/null +++ b/app/lnd/methods/newaddress.js @@ -0,0 +1,10 @@ +// LND Generate New Address +export default function info(lnd, type) { + return new Promise((resolve, reject) => { + lnd.newAddress({ type }, (err, data) => { + if (err) { reject(err) } + + resolve(data) + }) + }) +} diff --git a/app/reducers/address.js b/app/reducers/address.js index 426119a5..51bc3258 100644 --- a/app/reducers/address.js +++ b/app/reducers/address.js @@ -5,6 +5,13 @@ import { ipcRenderer } from 'electron' export const GET_ADDRESS = 'GET_ADDRESS' export const RECEIVE_ADDRESS = 'RECEIVE_ADDRESS' +// LND expects types to be sent as int, so this object will allow mapping from string to int +const addressTypes = { + p2wkh: 0, + np2wkh: 1, + p2pkh: 2 +} + // ------------------------------------ // Actions // ------------------------------------ @@ -17,17 +24,17 @@ export function getAddress() { // Send IPC event for getinfo export const newAddress = (type) => async (dispatch) => { dispatch(getAddress()) - ipcRenderer.send('lnd', { msg: 'newaddress' }) + ipcRenderer.send('lnd', { msg: 'newaddress', data: { type: addressTypes[type] } }) } // Receive IPC event for info -export const receiveAddress = (event, address) => dispatch => dispatch({ type: RECEIVE_INFO, address }) +export const receiveAddress = (event, address) => dispatch => dispatch({ type: RECEIVE_ADDRESS, address }) // ------------------------------------ // Action Handlers // ------------------------------------ const ACTION_HANDLERS = { - [GET_ADDRESS]: state => ({ ...state, infoLoading: true }), + [GET_ADDRESS]: state => ({ ...state, addressLoading: true }), [RECEIVE_ADDRESS]: (state, { address }) => ({ ...state, addressLoading: false, address }) } diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index 3d2ade7c..a0823a20 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -1,5 +1,6 @@ import createIpc from 'redux-electron-ipc' import { receiveInfo } from './info' +import { receiveAddress } from './address' import { receivePeers, connectSuccess, disconnectSuccess } from './peers' import { receiveChannels, @@ -30,7 +31,8 @@ const ipc = createIpc({ pushchannelerror, pushchannelstatus, connectSuccess, - disconnectSuccess + disconnectSuccess, + receiveAddress }) export default ipc diff --git a/app/routes/wallet/components/Wallet.js b/app/routes/wallet/components/Wallet.js index 6c01efbe..e64069f6 100644 --- a/app/routes/wallet/components/Wallet.js +++ b/app/routes/wallet/components/Wallet.js @@ -7,16 +7,18 @@ import styles from './Wallet.scss' class Wallet extends Component { componentWillMount() { - const { fetchInfo, fetchPeers, fetchChannels } = this.props + const { fetchInfo, fetchPeers, fetchChannels, newAddress } = this.props fetchInfo() fetchPeers() fetchChannels() + newAddress('p2pkh') } render() { const { info, + address: { addressLoading, address }, ticker, peers: { peersLoading, peers, peer, peerForm }, channels: { channelsLoading, channels, channel, channelForm, pendingChannels }, @@ -29,7 +31,8 @@ class Wallet extends Component { connectRequest, disconnectRequest, allChannels, - openChannel + openChannel, + newAddress } = this.props return ( @@ -38,13 +41,14 @@ class Wallet extends Component {

{info.data.identity_pubkey}

-
+
New wallet address
p2wkh np2wkh p2pkh
+
{address}
diff --git a/app/routes/wallet/components/Wallet.scss b/app/routes/wallet/components/Wallet.scss index 35681006..d44a0e66 100644 --- a/app/routes/wallet/components/Wallet.scss +++ b/app/routes/wallet/components/Wallet.scss @@ -24,12 +24,11 @@ h1 { margin: 20px 0; font-weight: 200; - font-size: 20px; color: $black; text-align: left; } - .address { + .addressContainer { text-align: left; margin-top: 30px; @@ -57,6 +56,14 @@ margin: 0 5px; } } + + .address { + padding: 10px; + background: $darkgrey; + text-align: center; + border-radius: 7px; + margin-top: 10px; + } } } diff --git a/app/routes/wallet/containers/WalletContainer.js b/app/routes/wallet/containers/WalletContainer.js index 92e919a1..d2baf253 100644 --- a/app/routes/wallet/containers/WalletContainer.js +++ b/app/routes/wallet/containers/WalletContainer.js @@ -1,4 +1,5 @@ import { connect } from 'react-redux' +import { newAddress } from '../../../reducers/address' import { fetchInfo } from '../../../reducers/info' import { fetchPeers, @@ -19,6 +20,8 @@ import { import Wallet from '../components/Wallet' const mapDispatchToProps = { + newAddress, + fetchInfo, fetchPeers, @@ -37,6 +40,7 @@ const mapDispatchToProps = { const mapStateToProps = state => ({ info: state.info, + address: state.address, ticker: state.ticker, peers: state.peers, From 69c8042cb3c4127c201fcbad30796d7fc3c387bb Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Thu, 24 Aug 2017 11:26:41 -0500 Subject: [PATCH 18/53] fix(wallet-address): simply display generated address for now. add advanced settings later --- app/routes/wallet/components/Wallet.js | 18 ++++----- app/routes/wallet/components/Wallet.scss | 49 +++++++----------------- 2 files changed, 22 insertions(+), 45 deletions(-) diff --git a/app/routes/wallet/components/Wallet.js b/app/routes/wallet/components/Wallet.js index e64069f6..f71d8504 100644 --- a/app/routes/wallet/components/Wallet.js +++ b/app/routes/wallet/components/Wallet.js @@ -40,16 +40,14 @@ class Wallet extends Component {
-

{info.data.identity_pubkey}

-
- New wallet address -
- p2wkh - np2wkh - p2pkh -
-
{address}
-
+

{info.data.identity_pubkey}

+

+ +

diff --git a/app/routes/wallet/components/Wallet.scss b/app/routes/wallet/components/Wallet.scss index d44a0e66..a987b3ad 100644 --- a/app/routes/wallet/components/Wallet.scss +++ b/app/routes/wallet/components/Wallet.scss @@ -11,7 +11,7 @@ text-align: center; .walletInfo { - width: 50%; + width: 75%; margin: 0 auto; } @@ -22,47 +22,26 @@ } h1 { + font-size: 20px; margin: 20px 0; - font-weight: 200; + font-weight: bold; color: $black; - text-align: left; + text-align: center; } - .addressContainer { - text-align: left; - margin-top: 30px; - - .addressButton { - display: inline-block; - border: 1px solid $main; - border-radius: 7px; - font-size: 10px; - cursor: pointer; - padding: 10px; - color: $main; - transition: all 0.25s; - - &:hover { - color: $white; - background: $main; - } - } - - .addressOptions { - display: inline-block; - margin-left: 30px; - - span { - margin: 0 5px; - } - } + .address { + text-align: center; + margin-top: 10px; - .address { - padding: 10px; - background: $darkgrey; + input[type=text] { + width: 50%; text-align: center; + font-size: 14px; + font-weight: 200; border-radius: 7px; - margin-top: 10px; + background: $lightgrey; + border: 1px solid $main; + padding: 10px; } } } From c6d7fdea271974880346f1250a8caac0df5c5fdc Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Thu, 24 Aug 2017 11:35:42 -0500 Subject: [PATCH 19/53] fix(wallet-address): fix small linting errors --- app/lnd/methods/index.js | 1 + app/reducers/address.js | 2 +- app/routes/wallet/components/Wallet.js | 9 +++++---- app/routes/wallet/containers/WalletContainer.js | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/lnd/methods/index.js b/app/lnd/methods/index.js index ec863547..6782a3d1 100644 --- a/app/lnd/methods/index.js +++ b/app/lnd/methods/index.js @@ -24,6 +24,7 @@ export default function (lnd, event, msg, data) { .catch(error => console.log('info error: ', error)) break case 'newaddress': + // Data looks like { address: '' } newaddress(lnd, data.type) .then(({ address }) => event.sender.send('receiveAddress', address)) .catch(error => console.log('newaddress error: ', error)) diff --git a/app/reducers/address.js b/app/reducers/address.js index 51bc3258..7ecb63f5 100644 --- a/app/reducers/address.js +++ b/app/reducers/address.js @@ -22,7 +22,7 @@ export function getAddress() { } // Send IPC event for getinfo -export const newAddress = (type) => async (dispatch) => { +export const newAddress = type => async (dispatch) => { dispatch(getAddress()) ipcRenderer.send('lnd', { msg: 'newaddress', data: { type: addressTypes[type] } }) } diff --git a/app/routes/wallet/components/Wallet.js b/app/routes/wallet/components/Wallet.js index f71d8504..f152203a 100644 --- a/app/routes/wallet/components/Wallet.js +++ b/app/routes/wallet/components/Wallet.js @@ -18,7 +18,7 @@ class Wallet extends Component { render() { const { info, - address: { addressLoading, address }, + address: { address }, ticker, peers: { peersLoading, peers, peer, peerForm }, channels: { channelsLoading, channels, channel, channelForm, pendingChannels }, @@ -31,8 +31,7 @@ class Wallet extends Component { connectRequest, disconnectRequest, allChannels, - openChannel, - newAddress + openChannel } = this.props return ( @@ -99,7 +98,9 @@ Wallet.propTypes = { connectRequest: PropTypes.func.isRequired, disconnectRequest: PropTypes.func.isRequired, allChannels: PropTypes.array.isRequired, - openChannel: PropTypes.func.isRequired + openChannel: PropTypes.func.isRequired, + newAddress: PropTypes.func.isRequired, + address: PropTypes.string.isRequired } diff --git a/app/routes/wallet/containers/WalletContainer.js b/app/routes/wallet/containers/WalletContainer.js index d2baf253..c48a6794 100644 --- a/app/routes/wallet/containers/WalletContainer.js +++ b/app/routes/wallet/containers/WalletContainer.js @@ -21,7 +21,7 @@ import Wallet from '../components/Wallet' const mapDispatchToProps = { newAddress, - + fetchInfo, fetchPeers, From b52d1bc2cfee06a2b38f3541171807ecb7af3128 Mon Sep 17 00:00:00 2001 From: funyug Date: Fri, 25 Aug 2017 19:07:04 +0530 Subject: [PATCH 20/53] Added location to windows tls certificate file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27b415de..51e3fef0 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ After the repo is cloned, paste the path to your cert in app/lnd/config/index.js // Cert will be located depending on your machine // Mac OS X: /Users/{your_user_name}/Library/Application Support/Lnd/tls.cert // Linux: ~/.lnd/tls.cert -// Windows: TODO find out where cert is located for windows machine +// Windows: C:\Users\{your_user_name}\AppData\Local\Lnd\tls.cert export default { ..., cert: '/path/to/cert/tls.cert' From 0b626bf10838b88f0d148c5cb2d7444c28a4eb6a Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Sat, 26 Aug 2017 17:39:08 -0500 Subject: [PATCH 21/53] fix(readme): fix cert instructions and electron builder --- README.md | 25 ++++++++++++++++++++++++- resources/zap_2.icns | Bin 0 -> 19130 bytes resources/zap_2.ico | Bin 0 -> 10440 bytes 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 resources/zap_2.icns create mode 100644 resources/zap_2.ico diff --git a/README.md b/README.md index 51e3fef0..b6b78d5a 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,29 @@ After installing the above requirements, clone the repo via git: git clone https://github.com/LN-Zap/zap-desktop.git ``` -After the repo is cloned, paste the path to your cert in app/lnd/config/index.js: +After the repo is cloned, you'll want to generate a Node.js compatible cert +```bash +# For Linux +$ cd ~/.lnd +# For Mac +$ cd /Users/{your_user_name}/Library/Application Support/Lnd +# For Windows +$ cd \Users\{your_user_name}\AppData\Local\Lnd + +# Then generate the cert +$ openssl ecparam -genkey -name prime256v1 -out tls.key +$ openssl req -new -sha256 -key tls.key -out csr.csr -subj '/CN=localhost/O=lnd' +$ openssl req -x509 -sha256 -days 3650 -key tls.key -in csr.csr -out tls.cert +$ rm csr.csr +``` + +Once you've created the Node.js compatible cert, paste the path to your cert in app/lnd/config/index.js: ```bash // Cert will be located depending on your machine // Mac OS X: /Users/{your_user_name}/Library/Application Support/Lnd/tls.cert // Linux: ~/.lnd/tls.cert // Windows: C:\Users\{your_user_name}\AppData\Local\Lnd\tls.cert + export default { ..., cert: '/path/to/cert/tls.cert' @@ -48,6 +65,12 @@ And then install dependencies with yarn ```bash $ cd zap-desktop $ yarn + +# For Mac & Linux +$ ./node_modules/.bin/electron-rebuild + +# For Windows +$ .\node_modules\.bin\electron-rebuild.cmd ``` Then to start it: diff --git a/resources/zap_2.icns b/resources/zap_2.icns new file mode 100644 index 0000000000000000000000000000000000000000..e2efdc3e7e198dac540899ed96cde069112c97a0 GIT binary patch literal 19130 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 KtyrUA75+aVEYFhw literal 0 HcmV?d00001 From 5a307dfba8c2225f830d2e7e67e290f7a68dce2d Mon Sep 17 00:00:00 2001 From: Jack Mallers Date: Sat, 26 Aug 2017 22:58:35 -0500 Subject: [PATCH 22/53] feature(ticker): replace hard coded ticker with currentTicker --- app/api/index.js | 11 ++- app/reducers/ticker.js | 76 ++++++++++++++----- app/routes/activity/components/Activity.js | 5 +- .../components/components/Invoices.js | 7 +- .../components/components/Payments.js | 9 ++- .../activity/containers/ActivityContainer.js | 5 +- app/routes/app/components/App.js | 3 + .../app/components/components/Form/Form.js | 7 +- app/routes/app/components/components/Nav.js | 8 +- app/routes/app/containers/AppContainer.js | 6 +- app/routes/wallet/components/Wallet.js | 4 +- .../components/Channels/Channels.js | 20 ++++- .../Channels/components/Channel/Channel.js | 6 +- .../components/ChannelForm/ChannelForm.js | 6 +- .../ClosedPendingChannel.js | 8 +- .../OpenPendingChannel/OpenPendingChannel.js | 8 +- .../wallet/containers/WalletContainer.js | 5 +- 17 files changed, 136 insertions(+), 58 deletions(-) diff --git a/app/api/index.js b/app/api/index.js index f1b5bb2f..3873274d 100644 --- a/app/api/index.js +++ b/app/api/index.js @@ -1,7 +1,7 @@ import axios from 'axios' -export default function requestTicker() { - const BASE_URL = 'https://api.coinmarketcap.com/v1/ticker/bitcoin/' +export function requestTicker(id) { + const BASE_URL = `https://api.coinmarketcap.com/v1/ticker/${id}/` return axios({ method: 'get', url: BASE_URL @@ -9,3 +9,10 @@ export default function requestTicker() { .then(response => response.data) .catch(error => error) } + +export function requestTickers(ids) { + return axios.all(ids.map(id => requestTicker(id))) + .then(axios.spread((btcTicker, ltcTicker) => { + return { btcTicker: btcTicker[0], ltcTicker: ltcTicker[0] } + })) +} \ No newline at end of file diff --git a/app/reducers/ticker.js b/app/reducers/ticker.js index 6e9a5fdb..da9ae95f 100644 --- a/app/reducers/ticker.js +++ b/app/reducers/ticker.js @@ -1,10 +1,18 @@ -import requestTicker from '../api' +import { createSelector } from 'reselect' +import { requestTickers } from '../api' // ------------------------------------ // Constants // ------------------------------------ export const SET_CURRENCY = 'SET_CURRENCY' -export const GET_TICKER = 'GET_TICKER' -export const RECIEVE_TICKER = 'RECIEVE_TICKER' +export const SET_CRYPTO = 'SET_CRYPTO' +export const GET_TICKERS = 'GET_TICKERS' +export const RECIEVE_TICKERS = 'RECIEVE_TICKERS' + +// Map for crypto names to crypto tickers +const cryptoTickers = { + bitcoin: 'btc', + litecoin: 'ltc' +} // ------------------------------------ // Actions @@ -16,38 +24,69 @@ export function setCurrency(currency) { } } -export function getTicker() { +export function setCrypto(crypto) { return { - type: GET_TICKER + type: SET_CRYPTO, + crypto } } -export function recieveTicker(ticker) { +export function getTickers() { return { - type: RECIEVE_TICKER, - ticker + type: GET_TICKERS } } -export const fetchTicker = () => async (dispatch) => { - dispatch(getTicker()) - const ticker = await requestTicker() - dispatch(recieveTicker(ticker)) +export function recieveTickers({ btcTicker, ltcTicker }) { + return { + type: RECIEVE_TICKERS, + btcTicker, + ltcTicker + } +} + +export const fetchTicker = (id) => async (dispatch) => { + dispatch(getTickers()) + const tickers = await requestTickers(['bitcoin', 'litecoin']) + dispatch(recieveTickers(tickers)) - return ticker + return tickers } +// Receive IPC event for receiveCryptocurrency +export const receiveCryptocurrency = (event, currency) => dispatch => { + dispatch({ type: SET_CURRENCY, currency: cryptoTickers[currency] }) + dispatch({ type: SET_CRYPTO, crypto: cryptoTickers[currency] }) +} + + // ------------------------------------ // Action Handlers // ------------------------------------ const ACTION_HANDLERS = { [SET_CURRENCY]: (state, { currency }) => ({ ...state, currency }), - [GET_TICKER]: state => ({ ...state, tickerLoading: true }), - [RECIEVE_TICKER]: (state, { ticker }) => ( - { ...state, tickerLoading: false, btcTicker: ticker[0] } + [SET_CRYPTO]: (state, { crypto }) => ({ ...state, crypto }), + [GET_TICKERS]: state => ({ ...state, tickerLoading: true }), + [RECIEVE_TICKERS]: (state, { btcTicker, ltcTicker }) => ( + { ...state, tickerLoading: false, btcTicker, ltcTicker } ) } +// Selectors +const tickerSelectors = {} +const cryptoSelector = state => state.ticker.crypto +const bitcoinTickerSelector = state => state.ticker.btcTicker +const litecoinTickerSelector = state => state.ticker.ltcTicker + +tickerSelectors.currentTicker = createSelector( + cryptoSelector, + bitcoinTickerSelector, + litecoinTickerSelector, + (crypto, btcTicker, ltcTicker) => crypto === 'btc' ? btcTicker : ltcTicker +) + +export { tickerSelectors } + // ------------------------------------ // Reducer // ------------------------------------ @@ -55,11 +94,12 @@ const initialState = { tickerLoading: false, currency: 'btc', crypto: 'btc', - btcTicker: null + btcTicker: null, + ltcTicker: null } export default function tickerReducer(state = initialState, action) { const handler = ACTION_HANDLERS[action.type] return handler ? handler(state, action) : state -} +} \ No newline at end of file diff --git a/app/routes/activity/components/Activity.js b/app/routes/activity/components/Activity.js index f2dddbec..f22ad27a 100644 --- a/app/routes/activity/components/Activity.js +++ b/app/routes/activity/components/Activity.js @@ -31,7 +31,8 @@ class Activity extends Component { setPayment, setInvoice, paymentModalOpen, - invoiceModalOpen + invoiceModalOpen, + currentTicker } = this.props if (invoiceLoading || paymentLoading) { return
Loading...
} @@ -75,6 +76,7 @@ class Activity extends Component { ticker={ticker} setPayment={setPayment} paymentModalOpen={paymentModalOpen} + currentTicker={currentTicker} /> : }
diff --git a/app/routes/activity/components/components/Invoices.js b/app/routes/activity/components/components/Invoices.js index f6a46c18..443f3440 100644 --- a/app/routes/activity/components/components/Invoices.js +++ b/app/routes/activity/components/components/Invoices.js @@ -14,7 +14,8 @@ const Invoices = ({ invoices, ticker, setInvoice, - invoiceModalOpen + invoiceModalOpen, + currentTicker }) => (
@@ -34,7 +35,7 @@ const Invoices = ({ ticker.currency === 'btc' ? btc.satoshisToBtc(invoice.value) : - btc.satoshisToUsd(invoice.value, ticker.btcTicker.price_usd) + btc.satoshisToUsd(invoice.value, currentTicker.price_usd) } @@ -91,7 +92,7 @@ const Invoices = ({ ticker.currency === 'btc' ? btc.satoshisToBtc(invoiceItem.value) : - btc.satoshisToUsd(invoiceItem.value, ticker.btcTicker.price_usd) + btc.satoshisToUsd(invoiceItem.value, currentTicker.price_usd) }
diff --git a/app/routes/activity/components/components/Payments.js b/app/routes/activity/components/components/Payments.js index f286453c..442a1996 100644 --- a/app/routes/activity/components/components/Payments.js +++ b/app/routes/activity/components/components/Payments.js @@ -12,7 +12,8 @@ const Payments = ({ payments, ticker, setPayment, - paymentModalOpen + paymentModalOpen, + currentTicker }) => (
@@ -32,7 +33,7 @@ const Payments = ({ ticker.currency === 'btc' ? btc.satoshisToBtc(payment.value) : - btc.satoshisToUsd(payment.value, ticker.btcTicker.price_usd) + btc.satoshisToUsd(payment.value, currentTicker.price_usd) } @@ -81,7 +82,7 @@ const Payments = ({ ticker.currency === 'btc' ? btc.satoshisToBtc(paymentItem.fee) : - btc.satoshisToUsd(paymentItem.fee, ticker.btcTicker.price_usd) + btc.satoshisToUsd(paymentItem.fee, currentTicker.price_usd) }
@@ -91,7 +92,7 @@ const Payments = ({ ticker.currency === 'btc' ? btc.satoshisToBtc(paymentItem.value) : - btc.satoshisToUsd(paymentItem.value, ticker.btcTicker.price_usd) + btc.satoshisToUsd(paymentItem.value, currentTicker.price_usd) } diff --git a/app/routes/activity/containers/ActivityContainer.js b/app/routes/activity/containers/ActivityContainer.js index 6ecbb3e2..1b3cba45 100644 --- a/app/routes/activity/containers/ActivityContainer.js +++ b/app/routes/activity/containers/ActivityContainer.js @@ -1,4 +1,5 @@ import { connect } from 'react-redux' +import { tickerSelectors } from '../../../reducers/ticker' import { fetchInvoices, searchInvoices, @@ -31,7 +32,9 @@ const mapStateToProps = state => ({ ticker: state.ticker, paymentModalOpen: paymentSelectors.paymentModalOpen(state), - invoiceModalOpen: invoiceSelectors.invoiceModalOpen(state) + invoiceModalOpen: invoiceSelectors.invoiceModalOpen(state), + + currentTicker: tickerSelectors.currentTicker(state) }) export default connect(mapStateToProps, mapDispatchToProps)(Activity) diff --git a/app/routes/app/components/App.js b/app/routes/app/components/App.js index 1571f911..30d51a02 100644 --- a/app/routes/app/components/App.js +++ b/app/routes/app/components/App.js @@ -29,6 +29,7 @@ class App extends Component { createInvoice, payInvoice, fetchInvoice, + currentTicker, children } = this.props @@ -49,12 +50,14 @@ class App extends Component { payInvoice={payInvoice} fetchInvoice={fetchInvoice} formInvoice={formInvoice} + currentTicker={currentTicker} />