From 7990b5802684d4a7929fcedaa8ea8e363286c164 Mon Sep 17 00:00:00 2001 From: emad-salah Date: Sun, 31 May 2020 13:02:49 +0100 Subject: [PATCH] [WIP] LND 10 migration --- config/defaults.js | 3 + config/router.proto | 581 ++++++++++++++++++++++++++++++++++++ config/walletunlocker.proto | 214 +++++++++++++ services/lnd/lightning.js | 54 +++- utils/lightningServices.js | 27 +- 5 files changed, 856 insertions(+), 23 deletions(-) create mode 100644 config/router.proto create mode 100644 config/walletunlocker.proto diff --git a/config/defaults.js b/config/defaults.js index b6be4d6d..d30c9ad4 100644 --- a/config/defaults.js +++ b/config/defaults.js @@ -36,6 +36,8 @@ module.exports = (mainnet = false) => { lndAddress: "127.0.0.1:9735", maxNumRoutesToQuery: 20, lndProto: parsePath(`${__dirname}/rpc.proto`), + routerProto: parsePath(`${__dirname}/router.proto`), + walletUnlockerProto: parsePath(`${__dirname}/walletunlocker.proto`), lndHost: "localhost:10009", lndCertPath: parsePath(`${lndDirectory}/tls.cert`), macaroonPath: parsePath( @@ -47,6 +49,7 @@ module.exports = (mainnet = false) => { lndLogFile: parsePath(`${lndDirectory}/logs/bitcoin/${network}/lnd.log`), lndDirPath: lndDirectory, peers: ["http://gun.shock.network:8765/gun"], + useTLS: false, tokenExpirationMS: 4500000 }; }; diff --git a/config/router.proto b/config/router.proto new file mode 100644 index 00000000..25bc765a --- /dev/null +++ b/config/router.proto @@ -0,0 +1,581 @@ +syntax = "proto3"; + +import "rpc.proto"; + +package routerrpc; + +option go_package = "github.com/lightningnetwork/lnd/lnrpc/routerrpc"; + +// Router is a service that offers advanced interaction with the router +// subsystem of the daemon. +service Router { + /* + SendPaymentV2 attempts to route a payment described by the passed + PaymentRequest to the final destination. The call returns a stream of + payment updates. + */ + rpc SendPaymentV2 (SendPaymentRequest) returns (stream lnrpc.Payment); + + /* + TrackPaymentV2 returns an update stream for the payment identified by the + payment hash. + */ + rpc TrackPaymentV2 (TrackPaymentRequest) returns (stream lnrpc.Payment); + + /* + EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it + may cost to send an HTLC to the target end destination. + */ + rpc EstimateRouteFee (RouteFeeRequest) returns (RouteFeeResponse); + + /* + Deprecated, use SendToRouteV2. SendToRoute attempts to make a payment via + the specified route. This method differs from SendPayment in that it + allows users to specify a full route manually. This can be used for + things like rebalancing, and atomic swaps. It differs from the newer + SendToRouteV2 in that it doesn't return the full HTLC information. + */ + rpc SendToRoute (SendToRouteRequest) returns (SendToRouteResponse) { + option deprecated = true; + } + + /* + SendToRouteV2 attempts to make a payment via the specified route. This method + differs from SendPayment in that it allows users to specify a full route + manually. This can be used for things like rebalancing, and atomic swaps. + */ + rpc SendToRouteV2 (SendToRouteRequest) returns (lnrpc.HTLCAttempt); + + /* + ResetMissionControl clears all mission control state and starts with a clean + slate. + */ + rpc ResetMissionControl (ResetMissionControlRequest) + returns (ResetMissionControlResponse); + + /* + QueryMissionControl exposes the internal mission control state to callers. + It is a development feature. + */ + rpc QueryMissionControl (QueryMissionControlRequest) + returns (QueryMissionControlResponse); + + /* + QueryProbability returns the current success probability estimate for a + given node pair and amount. + */ + rpc QueryProbability (QueryProbabilityRequest) + returns (QueryProbabilityResponse); + + /* + BuildRoute builds a fully specified route based on a list of hop public + keys. It retrieves the relevant channel policies from the graph in order to + calculate the correct fees and time locks. + */ + rpc BuildRoute (BuildRouteRequest) returns (BuildRouteResponse); + + /* + SubscribeHtlcEvents creates a uni-directional stream from the server to + the client which delivers a stream of htlc events. + */ + rpc SubscribeHtlcEvents (SubscribeHtlcEventsRequest) + returns (stream HtlcEvent); + + /* + Deprecated, use SendPaymentV2. SendPayment attempts to route a payment + described by the passed PaymentRequest to the final destination. The call + returns a stream of payment status updates. + */ + rpc SendPayment (SendPaymentRequest) returns (stream PaymentStatus) { + option deprecated = true; + } + + /* + Deprecated, use TrackPaymentV2. TrackPayment returns an update stream for + the payment identified by the payment hash. + */ + rpc TrackPayment (TrackPaymentRequest) returns (stream PaymentStatus) { + option deprecated = true; + } +} + +message SendPaymentRequest { + // The identity pubkey of the payment recipient + bytes dest = 1; + + /* + Number of satoshis to send. + + The fields amt and amt_msat are mutually exclusive. + */ + int64 amt = 2; + + /* + Number of millisatoshis to send. + + The fields amt and amt_msat are mutually exclusive. + */ + int64 amt_msat = 12; + + // The hash to use within the payment's HTLC + bytes payment_hash = 3; + + /* + The CLTV delta from the current height that should be used to set the + timelock for the final hop. + */ + int32 final_cltv_delta = 4; + + /* + A bare-bones invoice for a payment within the Lightning Network. With the + details of the invoice, the sender has all the data necessary to send a + payment to the recipient. The amount in the payment request may be zero. In + that case it is required to set the amt field as well. If no payment request + is specified, the following fields are required: dest, amt and payment_hash. + */ + string payment_request = 5; + + /* + An upper limit on the amount of time we should spend when attempting to + fulfill the payment. This is expressed in seconds. If we cannot make a + successful payment within this time frame, an error will be returned. + This field must be non-zero. + */ + int32 timeout_seconds = 6; + + /* + The maximum number of satoshis that will be paid as a fee of the payment. + If this field is left to the default value of 0, only zero-fee routes will + be considered. This usually means single hop routes connecting directly to + the destination. To send the payment without a fee limit, use max int here. + + The fields fee_limit_sat and fee_limit_msat are mutually exclusive. + */ + int64 fee_limit_sat = 7; + + /* + The maximum number of millisatoshis that will be paid as a fee of the + payment. If this field is left to the default value of 0, only zero-fee + routes will be considered. This usually means single hop routes connecting + directly to the destination. To send the payment without a fee limit, use + max int here. + + The fields fee_limit_sat and fee_limit_msat are mutually exclusive. + */ + int64 fee_limit_msat = 13; + + /* + Deprecated, use outgoing_chan_ids. The channel id of the channel that must + be taken to the first hop. If zero, any channel may be used (unless + outgoing_chan_ids are set). + */ + uint64 outgoing_chan_id = 8 [jstype = JS_STRING, deprecated = true]; + + /* + The channel ids of the channels are allowed for the first hop. If empty, + any channel may be used. + */ + repeated uint64 outgoing_chan_ids = 19; + + /* + The pubkey of the last hop of the route. If empty, any hop may be used. + */ + bytes last_hop_pubkey = 14; + + /* + An optional maximum total time lock for the route. This should not exceed + lnd's `--max-cltv-expiry` setting. If zero, then the value of + `--max-cltv-expiry` is enforced. + */ + int32 cltv_limit = 9; + + /* + Optional route hints to reach the destination through private channels. + */ + repeated lnrpc.RouteHint route_hints = 10; + + /* + An optional field that can be used to pass an arbitrary set of TLV records + to a peer which understands the new records. This can be used to pass + application specific data during the payment attempt. Record types are + required to be in the custom range >= 65536. When using REST, the values + must be encoded as base64. + */ + map dest_custom_records = 11; + + // If set, circular payments to self are permitted. + bool allow_self_payment = 15; + + /* + Features assumed to be supported by the final node. All transitive feature + dependencies must also be set properly. For a given feature bit pair, either + optional or remote may be set, but not both. If this field is nil or empty, + the router will try to load destination features from the graph as a + fallback. + */ + repeated lnrpc.FeatureBit dest_features = 16; + + /* + The maximum number of partial payments that may be use to complete the full + amount. + */ + uint32 max_parts = 17; + + /* + If set, only the final payment update is streamed back. Intermediate updates + that show which htlcs are still in flight are suppressed. + */ + bool no_inflight_updates = 18; +} + +message TrackPaymentRequest { + // The hash of the payment to look up. + bytes payment_hash = 1; + + /* + If set, only the final payment update is streamed back. Intermediate updates + that show which htlcs are still in flight are suppressed. + */ + bool no_inflight_updates = 2; +} + +message RouteFeeRequest { + /* + The destination once wishes to obtain a routing fee quote to. + */ + bytes dest = 1; + + /* + The amount one wishes to send to the target destination. + */ + int64 amt_sat = 2; +} + +message RouteFeeResponse { + /* + A lower bound of the estimated fee to the target destination within the + network, expressed in milli-satoshis. + */ + int64 routing_fee_msat = 1; + + /* + An estimate of the worst case time delay that can occur. Note that callers + will still need to factor in the final CLTV delta of the last hop into this + value. + */ + int64 time_lock_delay = 2; +} + +message SendToRouteRequest { + // The payment hash to use for the HTLC. + bytes payment_hash = 1; + + // Route that should be used to attempt to complete the payment. + lnrpc.Route route = 2; +} + +message SendToRouteResponse { + // The preimage obtained by making the payment. + bytes preimage = 1; + + // The failure message in case the payment failed. + lnrpc.Failure failure = 2; +} + +message ResetMissionControlRequest { +} + +message ResetMissionControlResponse { +} + +message QueryMissionControlRequest { +} + +// QueryMissionControlResponse contains mission control state. +message QueryMissionControlResponse { + reserved 1; + + // Node pair-level mission control state. + repeated PairHistory pairs = 2; +} + +// PairHistory contains the mission control state for a particular node pair. +message PairHistory { + // The source node pubkey of the pair. + bytes node_from = 1; + + // The destination node pubkey of the pair. + bytes node_to = 2; + + reserved 3, 4, 5, 6; + + PairData history = 7; +} + +message PairData { + // Time of last failure. + int64 fail_time = 1; + + /* + Lowest amount that failed to forward rounded to whole sats. This may be + set to zero if the failure is independent of amount. + */ + int64 fail_amt_sat = 2; + + /* + Lowest amount that failed to forward in millisats. This may be + set to zero if the failure is independent of amount. + */ + int64 fail_amt_msat = 4; + + reserved 3; + + // Time of last success. + int64 success_time = 5; + + // Highest amount that we could successfully forward rounded to whole sats. + int64 success_amt_sat = 6; + + // Highest amount that we could successfully forward in millisats. + int64 success_amt_msat = 7; +} + +message QueryProbabilityRequest { + // The source node pubkey of the pair. + bytes from_node = 1; + + // The destination node pubkey of the pair. + bytes to_node = 2; + + // The amount for which to calculate a probability. + int64 amt_msat = 3; +} + +message QueryProbabilityResponse { + // The success probability for the requested pair. + double probability = 1; + + // The historical data for the requested pair. + PairData history = 2; +} + +message BuildRouteRequest { + /* + The amount to send expressed in msat. If set to zero, the minimum routable + amount is used. + */ + int64 amt_msat = 1; + + /* + CLTV delta from the current height that should be used for the timelock + of the final hop + */ + int32 final_cltv_delta = 2; + + /* + The channel id of the channel that must be taken to the first hop. If zero, + any channel may be used. + */ + uint64 outgoing_chan_id = 3 [jstype = JS_STRING]; + + /* + A list of hops that defines the route. This does not include the source hop + pubkey. + */ + repeated bytes hop_pubkeys = 4; +} + +message BuildRouteResponse { + /* + Fully specified route that can be used to execute the payment. + */ + lnrpc.Route route = 1; +} + +message SubscribeHtlcEventsRequest { +} + +/* +HtlcEvent contains the htlc event that was processed. These are served on a +best-effort basis; events are not persisted, delivery is not guaranteed +(in the event of a crash in the switch, forward events may be lost) and +some events may be replayed upon restart. Events consumed from this package +should be de-duplicated by the htlc's unique combination of incoming and +outgoing channel id and htlc id. [EXPERIMENTAL] +*/ +message HtlcEvent { + /* + The short channel id that the incoming htlc arrived at our node on. This + value is zero for sends. + */ + uint64 incoming_channel_id = 1; + + /* + The short channel id that the outgoing htlc left our node on. This value + is zero for receives. + */ + uint64 outgoing_channel_id = 2; + + /* + Incoming id is the index of the incoming htlc in the incoming channel. + This value is zero for sends. + */ + uint64 incoming_htlc_id = 3; + + /* + Outgoing id is the index of the outgoing htlc in the outgoing channel. + This value is zero for receives. + */ + uint64 outgoing_htlc_id = 4; + + /* + The time in unix nanoseconds that the event occurred. + */ + uint64 timestamp_ns = 5; + + enum EventType { + UNKNOWN = 0; + SEND = 1; + RECEIVE = 2; + FORWARD = 3; + } + + /* + The event type indicates whether the htlc was part of a send, receive or + forward. + */ + EventType event_type = 6; + + oneof event { + ForwardEvent forward_event = 7; + ForwardFailEvent forward_fail_event = 8; + SettleEvent settle_event = 9; + LinkFailEvent link_fail_event = 10; + } +} + +message HtlcInfo { + // The timelock on the incoming htlc. + uint32 incoming_timelock = 1; + + // The timelock on the outgoing htlc. + uint32 outgoing_timelock = 2; + + // The amount of the incoming htlc. + uint64 incoming_amt_msat = 3; + + // The amount of the outgoing htlc. + uint64 outgoing_amt_msat = 4; +} + +message ForwardEvent { + // Info contains details about the htlc that was forwarded. + HtlcInfo info = 1; +} + +message ForwardFailEvent { +} + +message SettleEvent { +} + +message LinkFailEvent { + // Info contains details about the htlc that we failed. + HtlcInfo info = 1; + + // FailureCode is the BOLT error code for the failure. + lnrpc.Failure.FailureCode wire_failure = 2; + + /* + FailureDetail provides additional information about the reason for the + failure. This detail enriches the information provided by the wire message + and may be 'no detail' if the wire message requires no additional metadata. + */ + FailureDetail failure_detail = 3; + + // A string representation of the link failure. + string failure_string = 4; +} + +enum FailureDetail { + UNKNOWN = 0; + NO_DETAIL = 1; + ONION_DECODE = 2; + LINK_NOT_ELIGIBLE = 3; + ON_CHAIN_TIMEOUT = 4; + HTLC_EXCEEDS_MAX = 5; + INSUFFICIENT_BALANCE = 6; + INCOMPLETE_FORWARD = 7; + HTLC_ADD_FAILED = 8; + FORWARDS_DISABLED = 9; + INVOICE_CANCELED = 10; + INVOICE_UNDERPAID = 11; + INVOICE_EXPIRY_TOO_SOON = 12; + INVOICE_NOT_OPEN = 13; + MPP_INVOICE_TIMEOUT = 14; + ADDRESS_MISMATCH = 15; + SET_TOTAL_MISMATCH = 16; + SET_TOTAL_TOO_LOW = 17; + SET_OVERPAID = 18; + UNKNOWN_INVOICE = 19; + INVALID_KEYSEND = 20; + MPP_IN_PROGRESS = 21; + CIRCULAR_ROUTE = 22; +} + +enum PaymentState { + /* + Payment is still in flight. + */ + IN_FLIGHT = 0; + + /* + Payment completed successfully. + */ + SUCCEEDED = 1; + + /* + There are more routes to try, but the payment timeout was exceeded. + */ + FAILED_TIMEOUT = 2; + + /* + All possible routes were tried and failed permanently. Or were no + routes to the destination at all. + */ + FAILED_NO_ROUTE = 3; + + /* + A non-recoverable error has occured. + */ + FAILED_ERROR = 4; + + /* + Payment details incorrect (unknown hash, invalid amt or + invalid final cltv delta) + */ + FAILED_INCORRECT_PAYMENT_DETAILS = 5; + + /* + Insufficient local balance. + */ + FAILED_INSUFFICIENT_BALANCE = 6; +} + +message PaymentStatus { + // Current state the payment is in. + PaymentState state = 1; + + /* + The pre-image of the payment when state is SUCCEEDED. + */ + bytes preimage = 2; + + reserved 3; + + /* + The HTLCs made in attempt to settle the payment [EXPERIMENTAL]. + */ + repeated lnrpc.HTLCAttempt htlcs = 4; +} + diff --git a/config/walletunlocker.proto b/config/walletunlocker.proto new file mode 100644 index 00000000..858e6ae3 --- /dev/null +++ b/config/walletunlocker.proto @@ -0,0 +1,214 @@ +syntax = "proto3"; + +import "google/api/annotations.proto"; +import "rpc.proto"; + +package lnrpc; + +option go_package = "github.com/lightningnetwork/lnd/lnrpc"; + +/** + * Comments in this file will be directly parsed into the API + * Documentation as descriptions of the associated method, message, or field. + * These descriptions should go right above the definition of the object, and + * can be in either block or /// comment format. + * + * One edge case exists where a // comment followed by a /// comment in the + * next line will cause the description not to show up in the documentation. In + * that instance, simply separate the two comments with a blank line. + * + * An RPC method can be matched to an lncli command by placing a line in the + * beginning of the description in exactly the following format: + * lncli: `methodname` + * + * Failure to specify the exact name of the command will cause documentation + * generation to fail. + * + * More information on how exactly the gRPC documentation is generated from + * this proto file can be found here: + * https://github.com/lightninglabs/lightning-api + */ + +// WalletUnlocker is a service that is used to set up a wallet password for +// lnd at first startup, and unlock a previously set up wallet. +service WalletUnlocker { + /** + GenSeed is the first method that should be used to instantiate a new lnd + instance. This method allows a caller to generate a new aezeed cipher seed + given an optional passphrase. If provided, the passphrase will be necessary + to decrypt the cipherseed to expose the internal wallet seed. + Once the cipherseed is obtained and verified by the user, the InitWallet + method should be used to commit the newly generated seed, and create the + wallet. + */ + rpc GenSeed (GenSeedRequest) returns (GenSeedResponse) { + option (google.api.http) = { + get: "/v1/genseed" + }; + } + + /** + InitWallet is used when lnd is starting up for the first time to fully + initialize the daemon and its internal wallet. At the very least a wallet + password must be provided. This will be used to encrypt sensitive material + on disk. + In the case of a recovery scenario, the user can also specify their aezeed + mnemonic and passphrase. If set, then the daemon will use this prior state + to initialize its internal wallet. + Alternatively, this can be used along with the GenSeed RPC to obtain a + seed, then present it to the user. Once it has been verified by the user, + the seed can be fed into this RPC in order to commit the new wallet. + */ + rpc InitWallet (InitWalletRequest) returns (InitWalletResponse) { + option (google.api.http) = { + post: "/v1/initwallet" + body: "*" + }; + } + + /** lncli: `unlock` + UnlockWallet is used at startup of lnd to provide a password to unlock + the wallet database. + */ + rpc UnlockWallet (UnlockWalletRequest) returns (UnlockWalletResponse) { + option (google.api.http) = { + post: "/v1/unlockwallet" + body: "*" + }; + } + + /** lncli: `changepassword` + ChangePassword changes the password of the encrypted wallet. This will + automatically unlock the wallet database if successful. + */ + rpc ChangePassword (ChangePasswordRequest) + returns (ChangePasswordResponse) { + option (google.api.http) = { + post: "/v1/changepassword" + body: "*" + }; + } +} + +message GenSeedRequest { + /** + aezeed_passphrase is an optional user provided passphrase that will be used + to encrypt the generated aezeed cipher seed. When using REST, this field + must be encoded as base64. + */ + bytes aezeed_passphrase = 1; + + /** + seed_entropy is an optional 16-bytes generated via CSPRNG. If not + specified, then a fresh set of randomness will be used to create the seed. + When using REST, this field must be encoded as base64. + */ + bytes seed_entropy = 2; +} +message GenSeedResponse { + /** + cipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed + cipher seed obtained by the user. This field is optional, as if not + provided, then the daemon will generate a new cipher seed for the user. + Otherwise, then the daemon will attempt to recover the wallet state linked + to this cipher seed. + */ + repeated string cipher_seed_mnemonic = 1; + + /** + enciphered_seed are the raw aezeed cipher seed bytes. This is the raw + cipher text before run through our mnemonic encoding scheme. + */ + bytes enciphered_seed = 2; +} + +message InitWalletRequest { + /** + wallet_password is the passphrase that should be used to encrypt the + wallet. This MUST be at least 8 chars in length. After creation, this + password is required to unlock the daemon. When using REST, this field + must be encoded as base64. + */ + bytes wallet_password = 1; + + /** + cipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed + cipher seed obtained by the user. This may have been generated by the + GenSeed method, or be an existing seed. + */ + repeated string cipher_seed_mnemonic = 2; + + /** + aezeed_passphrase is an optional user provided passphrase that will be used + to encrypt the generated aezeed cipher seed. When using REST, this field + must be encoded as base64. + */ + bytes aezeed_passphrase = 3; + + /** + recovery_window is an optional argument specifying the address lookahead + when restoring a wallet seed. The recovery window applies to each + individual branch of the BIP44 derivation paths. Supplying a recovery + window of zero indicates that no addresses should be recovered, such after + the first initialization of the wallet. + */ + int32 recovery_window = 4; + + /** + channel_backups is an optional argument that allows clients to recover the + settled funds within a set of channels. This should be populated if the + user was unable to close out all channels and sweep funds before partial or + total data loss occurred. If specified, then after on-chain recovery of + funds, lnd begin to carry out the data loss recovery protocol in order to + recover the funds in each channel from a remote force closed transaction. + */ + ChanBackupSnapshot channel_backups = 5; +} +message InitWalletResponse { +} + +message UnlockWalletRequest { + /** + wallet_password should be the current valid passphrase for the daemon. This + will be required to decrypt on-disk material that the daemon requires to + function properly. When using REST, this field must be encoded as base64. + */ + bytes wallet_password = 1; + + /** + recovery_window is an optional argument specifying the address lookahead + when restoring a wallet seed. The recovery window applies to each + individual branch of the BIP44 derivation paths. Supplying a recovery + window of zero indicates that no addresses should be recovered, such after + the first initialization of the wallet. + */ + int32 recovery_window = 2; + + /** + channel_backups is an optional argument that allows clients to recover the + settled funds within a set of channels. This should be populated if the + user was unable to close out all channels and sweep funds before partial or + total data loss occurred. If specified, then after on-chain recovery of + funds, lnd begin to carry out the data loss recovery protocol in order to + recover the funds in each channel from a remote force closed transaction. + */ + ChanBackupSnapshot channel_backups = 3; +} +message UnlockWalletResponse { +} + +message ChangePasswordRequest { + /** + current_password should be the current valid passphrase used to unlock the + daemon. When using REST, this field must be encoded as base64. + */ + bytes current_password = 1; + + /** + new_password should be the new passphrase that will be needed to unlock the + daemon. When using REST, this field must be encoded as base64. + */ + bytes new_password = 2; +} +message ChangePasswordResponse { +} \ No newline at end of file diff --git a/services/lnd/lightning.js b/services/lnd/lightning.js index ccacfbd6..165024d3 100644 --- a/services/lnd/lightning.js +++ b/services/lnd/lightning.js @@ -1,3 +1,5 @@ + + const grpc = require("grpc"); const protoLoader = require("@grpc/proto-loader"); const fs = require("../../utils/fs"); @@ -6,25 +8,49 @@ const errorConstants = require("../../constants/errors"); // expose the routes to our app with module.exports /** - * @param {string} protoPath - * @param {string} lndHost - * @param {string} lndCertPath - * @param {string|null} macaroonPath - * @returns {Promise} + * @typedef LightningConfig + * @prop {string} lnrpcProtoPath + * @prop {string} routerProtoPath + * @prop {string} walletUnlockerProtoPath + * @prop {string} lndHost + * @prop {string} lndCertPath + * @prop {string?} macaroonPath */ -module.exports = async (protoPath, lndHost, lndCertPath, macaroonPath) => { + +/** + * @typedef LightningServices + * @prop {any} lightning + * @prop {any} walletUnlocker + * @prop {any} router + */ + +/** + * @param {LightningConfig} args0 + * @returns {Promise} + */ +module.exports = async ({ + lnrpcProtoPath, + routerProtoPath, + walletUnlockerProtoPath, + lndHost, + lndCertPath, + macaroonPath +}) => { try { process.env.GRPC_SSL_CIPHER_SUITES = "HIGH+ECDSA"; - - const packageDefinition = await protoLoader.load(protoPath, { + const protoLoaderConfig = { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, includeDirs: ["node_modules/google-proto-files", "proto"] - }); - const { lnrpc } = grpc.loadPackageDefinition(packageDefinition); + } + + const [lnrpcProto, routerProto, walletUnlockerProto] = await Promise.all([protoLoader.load(lnrpcProtoPath, protoLoaderConfig), protoLoader.load(routerProtoPath, protoLoaderConfig), protoLoader.load(walletUnlockerProtoPath, protoLoaderConfig)]); + const { lnrpc } = grpc.loadPackageDefinition(lnrpcProto); + const { routerrpc } = grpc.loadPackageDefinition(routerProto); + const { walletunlockerrpc } = grpc.loadPackageDefinition(walletUnlockerProto); const getCredentials = async () => { const lndCert = await fs.readFile(lndCertPath); @@ -65,11 +91,14 @@ module.exports = async (protoPath, lndHost, lndCertPath, macaroonPath) => { // @ts-ignore const lightning = new lnrpc.Lightning(lndHost, credentials); // @ts-ignore - const walletUnlocker = new lnrpc.WalletUnlocker(lndHost, credentials); + const walletUnlocker = new walletunlockerrpc.WalletUnlocker(lndHost, credentials); + // @ts-ignore + const router = new routerrpc.Router(lndHost, credentials); return { lightning, - walletUnlocker + walletUnlocker, + router }; } @@ -90,5 +119,6 @@ module.exports = async (protoPath, lndHost, lndCertPath, macaroonPath) => { "Failed to connect to LND server, make sure it's up and running." }; } + throw err; } }; diff --git a/utils/lightningServices.js b/utils/lightningServices.js index cd78f7a2..a1a4ead1 100644 --- a/utils/lightningServices.js +++ b/utils/lightningServices.js @@ -14,6 +14,8 @@ const lnrpc = require("../services/lnd/lightning"); * @prop {string} lndCertPath * @prop {string} macaroonPath * @prop {string} lndProto + * @prop {string} routerProto + * @prop {string} walletUnlockerProto */ class LightningServices { @@ -41,21 +43,18 @@ class LightningServices { /** * @type {Config} */ - const newDefaults = { - ...require("../config/defaults")(program.mainnet), - useTLS: false, - } + const newDefaults = require("../config/defaults")(program.mainnet) this.defaults = newDefaults; this._config = { + ...newDefaults, useTLS: program.usetls, serverPort: program.serverport || newDefaults.serverPort, serverHost: program.serverhost || newDefaults.serverHost, lndHost: program.lndhost || newDefaults.lndHost, lndCertPath: program.lndCertPath || newDefaults.lndCertPath, - macaroonPath: program.macaroonPath || newDefaults.macaroonPath, - lndProto: newDefaults.lndProto + macaroonPath: program.macaroonPath || newDefaults.macaroonPath }; } @@ -67,6 +66,7 @@ class LightningServices { return { lightning: this.lightning, walletUnlocker: this.walletUnlocker, + router: this.router }; } @@ -108,17 +108,22 @@ class LightningServices { const { macaroonPath, lndHost, lndCertPath } = this.config; const macaroonExists = await FS.access(macaroonPath); const lnServices = await lnrpc( - this.defaults.lndProto, - lndHost, - lndCertPath, - macaroonExists ? macaroonPath : null + { + lnrpcProtoPath: this.defaults.lndProto, + routerProtoPath: this.defaults.routerProto, + walletUnlockerProtoPath: this.defaults.walletUnlockerProto, + lndHost, + lndCertPath, + macaroonPath: macaroonExists ? macaroonPath : null + } ); if (!lnServices) { throw new Error(`Could not init lnServices`) } - const { lightning, walletUnlocker } = lnServices; + const { lightning, walletUnlocker, router } = lnServices; this.lightning = lightning; this.walletUnlocker = walletUnlocker + this.router = router; this.lnServicesData = { lndProto: this.defaults.lndProto, lndHost,