├── .nvmrc ├── .vscode └── settings.json ├── src ├── types │ ├── index.ts │ ├── streams │ │ ├── index.ts │ │ ├── duplex.ts │ │ └── readable.ts │ ├── rpc │ │ ├── index.ts │ │ ├── watchtower-rpc.ts │ │ ├── autopilot-rpc.ts │ │ ├── invoices-rpc.ts │ │ ├── wt-client-rpc.ts │ │ ├── wallet-unlocker-rpc.ts │ │ ├── chain-rpc.ts │ │ ├── sign-rpc.ts │ │ ├── wallet-rpc.ts │ │ └── router-rpc.ts │ ├── generated │ │ ├── google │ │ │ └── api │ │ │ │ ├── annotations_pb.d.ts │ │ │ │ └── http_pb.d.ts │ │ ├── watchtowerrpc │ │ │ └── watchtower_pb.d.ts │ │ ├── invoicesrpc │ │ │ └── invoices_pb.d.ts │ │ └── autopilotrpc │ │ │ └── autopilot_pb.d.ts │ └── general.ts ├── rpc │ ├── index.ts │ ├── defaults.ts │ ├── create-grpc-object.ts │ ├── sign-rpc.ts │ ├── autopilot-rpc.ts │ ├── chain-rpc.ts │ ├── watchtower-rpc.ts │ ├── wt-client-rpc.ts │ ├── router-rpc.ts │ ├── wallet-rpc.ts │ ├── create-credentials.ts │ ├── invoices-rpc.ts │ └── ln-rpc.ts ├── services │ ├── index.ts │ ├── signer.ts │ ├── autopilot.ts │ ├── wallet-kit.ts │ ├── watchtower.ts │ ├── wallet-unlocker.ts │ ├── watchtower-client.ts │ ├── invoices.ts │ ├── chain-notifier.ts │ ├── router.ts │ ├── lightning.ts │ └── create-service-client.ts ├── index.ts └── default-empty-arg.ts ├── .gitignore ├── tsconfig.build.json ├── .npmignore ├── test ├── mocha.opts ├── release.test.ts ├── helpers │ └── grpc-stub.ts ├── services │ ├── wallet-unlocker.test.ts │ └── lightning.test.ts └── rpc │ ├── signer-rpc.test.ts │ ├── router-rpc.test.ts │ ├── wallet-rpc.test.ts │ └── chain-rpc.test.ts ├── tsconfig.json ├── tslint.json ├── lnd └── v0.11.1-beta │ ├── watchtowerrpc │ └── watchtower.proto │ ├── autopilotrpc │ └── autopilot.proto │ ├── invoicesrpc │ └── invoices.proto │ ├── wtclientrpc │ └── wtclient.proto │ ├── chainrpc │ └── chainnotifier.proto │ ├── walletunlocker.proto │ └── signrpc │ └── signer.proto ├── LICENSE ├── google └── api │ └── annotations.proto ├── scripts ├── clean-repeated.ts ├── proto-sanitizer.ts ├── update_protos.sh └── generate_types.sh ├── .circleci └── config.yml ├── package.json └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | v8.11.3 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './general'; 2 | export * from './rpc'; 3 | export * from './streams'; 4 | -------------------------------------------------------------------------------- /src/types/streams/index.ts: -------------------------------------------------------------------------------- 1 | export { Duplex } from './duplex'; 2 | export { Readable } from './readable'; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn-error.log 3 | .esm-cache 4 | /rpc.proto 5 | *.macaroon 6 | dist 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "test/**/*.ts" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .circleci 2 | .vscode 3 | test/ 4 | src/ 5 | scripts/ 6 | lnd/ 7 | google/ 8 | tsconfig.json 9 | tsconfig.*.json 10 | tslint.json 11 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --ui mocha-typescript 2 | --require ts-node/register/type-check 3 | --require source-map-support/register 4 | --exit 5 | --timeout=30000 6 | -------------------------------------------------------------------------------- /src/rpc/index.ts: -------------------------------------------------------------------------------- 1 | export * from './autopilot-rpc'; 2 | export * from './chain-rpc'; 3 | export * from './invoices-rpc'; 4 | export * from './ln-rpc'; 5 | export * from './router-rpc'; 6 | export * from './sign-rpc'; 7 | export * from './wallet-rpc'; 8 | export * from './watchtower-rpc'; 9 | export * from './wt-client-rpc'; 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "lib": ["es2016"], 6 | "declaration": true, 7 | "outDir": "dist", 8 | "resolveJsonModule": true, 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true 11 | }, 12 | "include":["src/**/*", "test/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /src/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './autopilot'; 2 | export * from './invoices'; 3 | export * from './chain-notifier'; 4 | export * from './lightning'; 5 | export * from './router'; 6 | export * from './signer'; 7 | export * from './wallet-kit'; 8 | export * from './wallet-unlocker'; 9 | export * from './watchtower'; 10 | export * from './watchtower-client'; 11 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { createLnRpc } from './rpc'; 2 | 3 | export * from './types'; 4 | export { 5 | createAutopilotRpc, 6 | createChainRpc, 7 | createInvoicesRpc, 8 | createLnRpc, 9 | createRouterRpc, 10 | createSignRpc, 11 | createWalletRpc, 12 | createWatchtowerRpc, 13 | createWtClientRpc, 14 | } from './rpc'; 15 | export default createLnRpc; 16 | -------------------------------------------------------------------------------- /src/types/rpc/index.ts: -------------------------------------------------------------------------------- 1 | export * from './autopilot-rpc'; 2 | export * from './chain-rpc'; 3 | export * from './invoices-rpc'; 4 | export * from './ln-rpc'; 5 | export * from './router-rpc'; 6 | export * from './sign-rpc'; 7 | export * from './wallet-rpc'; 8 | export * from './wallet-unlocker-rpc'; 9 | export * from './watchtower-rpc'; 10 | export * from './wt-client-rpc'; 11 | -------------------------------------------------------------------------------- /src/types/generated/google/api/annotations_pb.d.ts: -------------------------------------------------------------------------------- 1 | // package: google.api 2 | // file: google/api/annotations.proto 3 | 4 | import * as jspb from "google-protobuf"; 5 | import * as google_api_http_pb from "../../google/api/http_pb"; 6 | import * as google_protobuf_descriptor_pb from "google-protobuf/google/protobuf/descriptor_pb"; 7 | 8 | export const http: jspb.ExtensionFieldInfo; 9 | 10 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "linterOptions": { 7 | "exclude": [ 8 | "node_modules", 9 | "src/types/generated/**" 10 | ] 11 | }, 12 | "rules": { 13 | "interface-name": [ 14 | true, 15 | "never-prefix" 16 | ], 17 | "quotemark": [true, "single"], 18 | "curly": false, 19 | "object-literal-sort-keys": false 20 | }, 21 | "rulesDirectory": [] 22 | } 23 | -------------------------------------------------------------------------------- /src/rpc/defaults.ts: -------------------------------------------------------------------------------- 1 | import * as grpc from '@grpc/grpc-js'; 2 | import * as grpcLoader from '@grpc/proto-loader'; 3 | import os from 'os'; 4 | 5 | const homeDir = os.homedir(); 6 | 7 | // RPC client shared default values 8 | export const defaults = { 9 | grpc, 10 | grpcLoader, 11 | server: 'localhost:10001', 12 | macaroonPath: '', 13 | certEncoding: 'utf8', 14 | tls: /^darwin/.test(process.platform) // is macOS? 15 | ? `${homeDir}/Library/Application Support/Lnd/tls.cert` 16 | : `${homeDir}/.lnd/tls.cert`, 17 | }; 18 | -------------------------------------------------------------------------------- /src/types/rpc/watchtower-rpc.ts: -------------------------------------------------------------------------------- 1 | export interface GetInfoResp { 2 | pubkey: Buffer | string; 3 | listeners: string[]; 4 | uris: string[]; 5 | } 6 | 7 | /** 8 | * LND Watchtower gRPC API Client 9 | */ 10 | export interface WatchtowerRpc { 11 | /** 12 | * getInfo returns general information concerning the companion watchtower 13 | * including it's public key and URIs where the server is currently 14 | * listening for clients. 15 | * lncli: tower info 16 | */ 17 | getInfo(args?: {}): Promise; 18 | } 19 | -------------------------------------------------------------------------------- /src/default-empty-arg.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Add an empty object as the first argument if no argument was passed 3 | * @param {Function} func The function being called 4 | * @param {boolean} hasCallback Whether or not the passed function has a callback 5 | * @return {any} 6 | */ 7 | export function defaultEmptyArg(func: (...args: any[]) => any, hasCallback: boolean = true): any { 8 | return function(...args: any[]) { 9 | if (args.length < (hasCallback ? 2 : 1)) { 10 | args.unshift({}); 11 | } 12 | return func.apply(this, args); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /src/services/signer.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionConfig } from '../types'; 2 | import { createServiceClient } from './create-service-client'; 3 | 4 | /** 5 | * Create a Signer GRPC service client proxy 6 | * @param config The grpc service configuration 7 | */ 8 | export function createSigner(config: ConnectionConfig): any { 9 | try { 10 | const { grpcPkgObj, server, credentials } = config; 11 | const signer = new grpcPkgObj.signrpc.Signer( 12 | server, 13 | credentials, 14 | ); 15 | 16 | return createServiceClient({ 17 | ...config, 18 | service: signer, 19 | }); 20 | } catch (e) { 21 | if (!e.code) e.code = 'GRPC_SIGNER_SERVICE_ERR'; 22 | throw e; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/services/autopilot.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionConfig } from '../types'; 2 | import { createServiceClient } from './create-service-client'; 3 | 4 | /** 5 | * Create a Autopilot GRPC service client proxy 6 | * @param config The grpc service configuration 7 | */ 8 | export function createAutopilot(config: ConnectionConfig): any { 9 | try { 10 | const { grpcPkgObj, server, credentials } = config; 11 | const autopilot = new grpcPkgObj.autopilotrpc.Autopilot( 12 | server, 13 | credentials, 14 | ); 15 | 16 | return createServiceClient({ 17 | ...config, 18 | service: autopilot, 19 | }); 20 | } catch (e) { 21 | if (!e.code) e.code = 'GRPC_AUTOPILOT_SERVICE_ERR'; 22 | throw e; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/services/wallet-kit.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionConfig } from '../types'; 2 | import { createServiceClient } from './create-service-client'; 3 | 4 | /** 5 | * Create a WalletKit GRPC service client proxy 6 | * @param config The grpc service configuration 7 | */ 8 | export function createWalletKit(config: ConnectionConfig): any { 9 | try { 10 | const { grpcPkgObj, server, credentials } = config; 11 | const walletKit = new grpcPkgObj.walletrpc.WalletKit( 12 | server, 13 | credentials, 14 | ); 15 | 16 | return createServiceClient({ 17 | ...config, 18 | service: walletKit, 19 | }); 20 | } catch (e) { 21 | if (!e.code) e.code = 'GRPC_WALLET_KIT_SERVICE_ERR'; 22 | throw e; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/services/watchtower.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionConfig } from '../types'; 2 | import { createServiceClient } from './create-service-client'; 3 | 4 | /** 5 | * Create a Watchtower GRPC service client proxy 6 | * @param config The grpc service configuration 7 | */ 8 | export function createWatchtower(config: ConnectionConfig): any { 9 | try { 10 | const { grpcPkgObj, server, credentials } = config; 11 | const watchtower = new grpcPkgObj.watchtowerrpc.Watchtower( 12 | server, 13 | credentials, 14 | ); 15 | 16 | return createServiceClient({ 17 | ...config, 18 | service: watchtower, 19 | }); 20 | } catch (e) { 21 | if (!e.code) e.code = 'GRPC_WATCHTOWER_SERVICE_ERR'; 22 | throw e; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/services/wallet-unlocker.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionConfig } from '../types'; 2 | import { createServiceClient } from './create-service-client'; 3 | 4 | /** 5 | * Create a wallet unlocker GRPC service client proxy 6 | * @param config The grpc service configuration 7 | */ 8 | export function createWalletUnlocker(config: ConnectionConfig): any { 9 | try { 10 | const { grpcPkgObj, server, credentials } = config; 11 | const walletUnlocker = new grpcPkgObj.lnrpc.WalletUnlocker( 12 | server, 13 | credentials, 14 | ); 15 | 16 | return createServiceClient({ 17 | ...config, 18 | service: walletUnlocker, 19 | }); 20 | } catch (e) { 21 | if (!e.code) e.code = 'GRPC_WALLET_UNLOCKER_SERVICE_ERR'; 22 | throw e; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/rpc/create-grpc-object.ts: -------------------------------------------------------------------------------- 1 | import { GrpcObjectConfig, NestedGrpcObject } from '../types'; 2 | 3 | /** 4 | * Create RPC from proto and return the GRPC package object 5 | * @param config The configuration necessary to create the grpc object 6 | */ 7 | export function createGrpcObject(config: GrpcObjectConfig) { 8 | try { 9 | const { protoFilePath, includeDirs, grpcLoader, grpc, includeDefaults } = config; 10 | const packageDefinition = grpcLoader.loadSync(protoFilePath, { 11 | longs: String, 12 | defaults: includeDefaults ?? false, 13 | includeDirs, 14 | }); 15 | return grpc.loadPackageDefinition(packageDefinition) as NestedGrpcObject; 16 | } catch (e) { 17 | if (!e.code) e.code = 'GRPC_LOAD_ERR'; 18 | throw e; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/services/watchtower-client.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionConfig } from '../types'; 2 | import { createServiceClient } from './create-service-client'; 3 | 4 | /** 5 | * Create a WatchtowerClient service client proxy 6 | * @param config The grpc service configuration 7 | */ 8 | export function createWatchtowerClient(config: ConnectionConfig): any { 9 | try { 10 | const { grpcPkgObj, server, credentials } = config; 11 | const watchtowerClient = new grpcPkgObj.wtclientrpc.WatchtowerClient( 12 | server, 13 | credentials, 14 | ); 15 | 16 | return createServiceClient({ 17 | ...config, 18 | service: watchtowerClient, 19 | }); 20 | } catch (e) { 21 | if (!e.code) e.code = 'GRPC_WATCHTOWER_CLIENT_SERVICE_ERR'; 22 | throw e; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/services/invoices.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionConfig } from '../types'; 2 | import { createServiceClient } from './create-service-client'; 3 | 4 | /** 5 | * Create a Invoices GRPC service client proxy 6 | * @param config The grpc service configuration 7 | */ 8 | export function createInvoices(config: ConnectionConfig): any { 9 | try { 10 | const { grpcPkgObj, server, credentials } = config; 11 | const invoices = new grpcPkgObj.invoicesrpc.Invoices( 12 | server, 13 | credentials, 14 | ); 15 | const subscriptionMethods = [ 16 | 'subscribeSingleInvoice', 17 | ]; 18 | 19 | return createServiceClient({ 20 | ...config, 21 | subscriptionMethods, 22 | service: invoices, 23 | }); 24 | } catch (e) { 25 | if (!e.code) e.code = 'GRPC_INVOICES_SERVICE_ERR'; 26 | throw e; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lnd/v0.11.1-beta/watchtowerrpc/watchtower.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package watchtowerrpc; 4 | 5 | option go_package = "github.com/lightningnetwork/lnd/lnrpc/watchtowerrpc"; 6 | 7 | // Watchtower is a service that grants access to the watchtower server 8 | // functionality of the daemon. 9 | service Watchtower { 10 | /* lncli: tower info 11 | GetInfo returns general information concerning the companion watchtower 12 | including its public key and URIs where the server is currently 13 | listening for clients. 14 | */ 15 | rpc GetInfo (GetInfoRequest) returns (GetInfoResponse); 16 | } 17 | 18 | message GetInfoRequest { 19 | } 20 | 21 | message GetInfoResponse { 22 | // The public key of the watchtower. 23 | bytes pubkey = 1; 24 | 25 | // The listening addresses of the watchtower. 26 | repeated string listeners = 2; 27 | 28 | // The URIs of the watchtower. 29 | repeated string uris = 3; 30 | } 31 | -------------------------------------------------------------------------------- /src/services/chain-notifier.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionConfig } from '../types'; 2 | import { createServiceClient } from './create-service-client'; 3 | 4 | /** 5 | * Create a Chain Notifier GRPC service client proxy 6 | * @param config The grpc service configuration 7 | */ 8 | export function createChainNotifier(config: ConnectionConfig): any { 9 | try { 10 | const { grpcPkgObj, server, credentials } = config; 11 | const chainNotifier = new grpcPkgObj.chainrpc.ChainNotifier( 12 | server, 13 | credentials, 14 | ); 15 | const subscriptionMethods = [ 16 | 'registerConfirmationsNtfn', 17 | 'registerSpendNtfn', 18 | 'registerBlockEpochNtfn', 19 | ]; 20 | 21 | return createServiceClient({ 22 | ...config, 23 | subscriptionMethods, 24 | service: chainNotifier, 25 | }); 26 | } catch (e) { 27 | if (!e.code) e.code = 'GRPC_CHAIN_NOTIFIER_SERVICE_ERR'; 28 | throw e; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/services/router.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionConfig } from '../types'; 2 | import { createServiceClient } from './create-service-client'; 3 | 4 | /** 5 | * Create a Router GRPC service client proxy 6 | * @param config The grpc service configuration 7 | */ 8 | export function createRouter(config: ConnectionConfig): any { 9 | try { 10 | const { grpcPkgObj, server, credentials } = config; 11 | const router = new grpcPkgObj.routerrpc.Router( 12 | server, 13 | credentials, 14 | ); 15 | const subscriptionMethods = [ 16 | 'sendPaymentV2', 17 | 'trackPaymentV2', 18 | 'subscribeHtlcEvents', 19 | 'sendPayment', 20 | 'trackPayment', 21 | 'htlcInterceptor', 22 | ]; 23 | 24 | return createServiceClient({ 25 | ...config, 26 | subscriptionMethods, 27 | service: router, 28 | }); 29 | } catch (e) { 30 | if (!e.code) e.code = 'GRPC_ROUTER_SERVICE_ERR'; 31 | throw e; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 RadarTech 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /google/api/annotations.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | import "google/api/http.proto"; 20 | import "google/protobuf/descriptor.proto"; 21 | 22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; 23 | option java_multiple_files = true; 24 | option java_outer_classname = "AnnotationsProto"; 25 | option java_package = "com.google.api"; 26 | option objc_class_prefix = "GAPI"; 27 | 28 | extend google.protobuf.MethodOptions { 29 | // See `HttpRule`. 30 | HttpRule http = 72295728; 31 | } 32 | -------------------------------------------------------------------------------- /scripts/clean-repeated.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import glob from 'glob'; 3 | import os from 'os'; 4 | import readline from 'readline'; 5 | 6 | /** 7 | * Read the generated rpc types and remove 8 | * 'List' from all Array type names. 9 | * More info: 10 | * https://github.com/improbable-eng/ts-protoc-gen/issues/86 11 | * https://github.com/protocolbuffers/protobuf/issues/4518 12 | */ 13 | 14 | const files = glob.sync('src/types/generated/**/*.ts'); 15 | files.forEach((file, i) => { 16 | const tempFile = `src/types/generated/temp-${i}.d.ts`; 17 | 18 | const reader = readline.createInterface({ 19 | input: fs.createReadStream(file), 20 | }); 21 | const writer = fs.createWriteStream(tempFile, { 22 | flags: 'a', 23 | }); 24 | 25 | reader.on('line', (line) => { 26 | if (/List.*Array<.*>,/.test(line)) { 27 | writer.write(line.replace('List', '')); 28 | } else if (/Map.*Array<.*>,/.test(line)) { 29 | writer.write(line.replace('Map', '')); 30 | } else { 31 | writer.write(line); 32 | } 33 | writer.write(os.EOL); 34 | }); 35 | 36 | reader.on('close', () => { 37 | writer.end(); 38 | 39 | fs.renameSync(tempFile, file); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | dependencies: &dependencies 4 | run: 5 | name: Install Dependencies 6 | command: yarn install 7 | 8 | run_tests: &run_tests 9 | working_directory: ~/project 10 | steps: 11 | - checkout 12 | - *dependencies 13 | - run: 14 | name: Run Tests 15 | command: yarn test 16 | 17 | jobs: 18 | lint: 19 | docker: 20 | - image: circleci/node:10 21 | working_directory: ~/project 22 | steps: 23 | - checkout 24 | - *dependencies 25 | - run: 26 | name: Run Linter 27 | command: yarn lint 28 | audit: 29 | docker: 30 | - image: circleci/node:10 31 | working_directory: ~/project 32 | steps: 33 | - checkout 34 | - *dependencies 35 | - run: 36 | name: Run Audit 37 | command: | 38 | sudo npm install -g yarn 39 | yarn audit --groups dependencies 40 | test-node-10: 41 | docker: 42 | - image: circleci/node:10 43 | <<: *run_tests 44 | test-node-11: 45 | docker: 46 | - image: circleci/node:11 47 | <<: *run_tests 48 | 49 | workflows: 50 | version: 2 51 | default: 52 | jobs: 53 | - lint 54 | - test-node-10 55 | - test-node-11 56 | -------------------------------------------------------------------------------- /src/services/lightning.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionConfig } from '../types'; 2 | import { createServiceClient } from './create-service-client'; 3 | 4 | /** 5 | * Create a Lightning GRPC service client proxy 6 | * @param config The grpc service configuration 7 | */ 8 | export function createLightning(config: ConnectionConfig): any { 9 | try { 10 | const { grpcPkgObj, server, credentials } = config; 11 | const lightning = new grpcPkgObj.lnrpc.Lightning(server, credentials, { 12 | // Increase max receive message size for describegraph 13 | 'grpc.max_receive_message_length': 50 * 1024 * 1024, 14 | }); 15 | const subscriptionMethods = [ 16 | 'subscribeInvoices', 17 | 'subscribeTransactions', 18 | 'subscribeChannelGraph', 19 | 'subscribePeerEvents', 20 | 'subscribeChannelEvents', 21 | 'subscribeChannelBackups', 22 | 'sendToRoute', 23 | 'sendPayment', 24 | 'openChannel', 25 | 'closeChannel', 26 | ]; 27 | 28 | return createServiceClient({ 29 | ...config, 30 | subscriptionMethods, 31 | service: lightning, 32 | }); 33 | } catch (e) { 34 | if (!e.code) e.code = 'GRPC_LIGHTNING_SERVICE_ERR'; 35 | throw e; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /scripts/proto-sanitizer.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import glob from 'glob'; 3 | import os from 'os'; 4 | import path from 'path'; 5 | import readline from 'readline'; 6 | 7 | /** 8 | * The intended use of this program is to 9 | * to read target proto files and sanitize them 10 | * before typescript definitions are generated. 11 | * ie. uint64 values can overflow Number types 12 | * --> add a tag jstype = JS_STRING to cast to string 13 | */ 14 | 15 | if (!process.argv[2]) { 16 | throw new Error('Usage: ts-node scripts/proto-sanitizer.ts' + 17 | './file.proto (globs accepted)'); 18 | } 19 | 20 | const readFiles = glob.sync(process.argv[2]); 21 | 22 | for (const file of readFiles) { 23 | const writeFile = path.basename(file); 24 | 25 | const reader = readline.createInterface({ 26 | input: fs.createReadStream(file), 27 | }); 28 | const writer = fs.createWriteStream(writeFile, { 29 | flags: 'a', 30 | }); 31 | const payload = 'jstype = JS_STRING'; 32 | reader.on('line', (line) => { 33 | if (/^\s*u?int64.*$/.test(line)) { 34 | if (/^.*];$/.test(line)) { // existing tag 35 | writer.write(line.replace(/\s*];$/, `, ${payload}];`)); 36 | } else { 37 | writer.write(line.replace(/;$/, ` [${payload}];`)); 38 | } 39 | } else { 40 | writer.write(line); 41 | } 42 | writer.write(os.EOL); 43 | }); 44 | reader.on('close', () => { 45 | writer.end(); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /test/release.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import createLnRpc from '../src'; 3 | import { grpcStub, LightningStub } from './helpers/grpc-stub'; 4 | 5 | const { equal } = assert; 6 | 7 | describe('Release API', () => { 8 | const certStub = 'cert'; 9 | 10 | it('should expose a lnrpc factory function as default', () => { 11 | equal(typeof createLnRpc, 'function'); 12 | }); 13 | 14 | it('should create instance exposing top-level lighting methods', () => { 15 | /** 16 | * Custom LightningCustomStub 17 | * @constructor 18 | */ 19 | function LightningCustomStub() { /* noop */ } 20 | LightningCustomStub.prototype.walletBalance = () => { /* noop */ }; 21 | 22 | return createLnRpc({ 23 | grpc: grpcStub({}, LightningCustomStub), 24 | cert: certStub, 25 | }).then((lnrpc) => { 26 | equal(typeof lnrpc.walletBalance, 'function'); 27 | }); 28 | }); 29 | 30 | it('should provide walletUnlocker methods', () => { 31 | /** 32 | * Custom WalletUnlockerStub 33 | * @constructor 34 | */ 35 | function WalletUnlockerStub() { /* noop */ } 36 | WalletUnlockerStub.prototype.initWallet = () => { /* noop */ }; 37 | 38 | return createLnRpc<{ walletUnlocker: { initWallet: () => void } }>({ 39 | grpc: grpcStub({}, LightningStub, WalletUnlockerStub), 40 | cert: certStub, 41 | }).then((lnrpc) => { 42 | equal(typeof lnrpc.walletUnlocker.initWallet, 'function'); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/services/create-service-client.ts: -------------------------------------------------------------------------------- 1 | import { promisify } from 'util'; 2 | import { defaultEmptyArg } from '../default-empty-arg'; 3 | import { GrpcServiceConfig } from '../types'; 4 | 5 | /** 6 | * Create a GRPC service client proxy 7 | * The Proxy serves two purposes: 8 | * - Wrap non-subscription methods in promises 9 | * - Immediately return subscription methods and properties 10 | * @param config The GRPC service configuration 11 | */ 12 | export function createServiceClient(config: GrpcServiceConfig) { 13 | // create a list of function names on the service class to prevent 14 | // promisifying internal functions on the base class grpc.Client 15 | const ownProps = Object.getOwnPropertyNames( 16 | Object.getPrototypeOf(config.service), 17 | ); 18 | 19 | return new Proxy(config.service, { 20 | /** 21 | * Promisify any requested (non-subscription) lightning RPC method 22 | * @param target 23 | * @param key 24 | */ 25 | get(target: unknown, key: string) { 26 | const method = target[key]; 27 | 28 | if (typeof method !== 'function' || !ownProps.includes(key)) { 29 | // forward non-function properties and base class functions 30 | return target[key]; 31 | } else if (config.subscriptionMethods) { 32 | if (config.subscriptionMethods.includes(key)) { 33 | return defaultEmptyArg(method, false); 34 | } 35 | } 36 | return promisify(defaultEmptyArg(method)); 37 | }, 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /src/types/rpc/autopilot-rpc.ts: -------------------------------------------------------------------------------- 1 | export interface StatusResponse { 2 | active: boolean; 3 | } 4 | 5 | export interface ModifyStatusRequest { 6 | enable: boolean; 7 | } 8 | 9 | export interface QueryScoresRequest { 10 | pubkeys: string[]; 11 | ignoreLocalState?: boolean; 12 | } 13 | 14 | export interface HeuristicResult { 15 | heuristic: string; 16 | scores: Array<[string, number]>; 17 | } 18 | 19 | export interface QueryScoresResponse { 20 | results: HeuristicResult[]; 21 | } 22 | 23 | export interface SetScoresRequest { 24 | heuristic: string; 25 | scores: Array<[string, number]>; 26 | } 27 | 28 | /** 29 | * LND Autopilot gRPC API Client 30 | */ 31 | export interface AutopilotRpc { 32 | /** 33 | * status returns whether the daemon's autopilot agent is active. 34 | */ 35 | status(args?: {}): Promise; 36 | 37 | /** 38 | * modifyStatus is used to modify the status of the autopilot agent, like 39 | * enabling or disabling it. 40 | */ 41 | modifyStatus(args: ModifyStatusRequest): Promise<{}>; 42 | 43 | /** 44 | * queryScores queries all available autopilot heuristics, in addition to any 45 | * active combination of these heuristics, for the scores they would give to 46 | * the given nodes. 47 | */ 48 | queryScores(args: QueryScoresRequest): Promise; 49 | 50 | /** 51 | * setScores attempts to set the scores used by the running autopilot agent, 52 | * if the external scoring heuristic is enabled. 53 | */ 54 | setScores(args: SetScoresRequest): Promise<{}>; 55 | } 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@radar/lnrpc", 3 | "version": "v0.11.1-beta.1", 4 | "description": "A Typescript gRPC client for LND with support for all LND sub-servers", 5 | "main": "dist/src/index.js", 6 | "types": "dist/src/index.d.ts", 7 | "config": { 8 | "lnd-release-tag": "v0.11.1-beta", 9 | "lnd-url": "https://raw.githubusercontent.com/lightningnetwork/lnd", 10 | "protoc-version": "3.5.1" 11 | }, 12 | "scripts": { 13 | "test": "mocha \"test/**/*.test.ts\"", 14 | "build": "tsc -p tsconfig.build.json && cp -r lnd/. dist/lnd", 15 | "clean": "rimraf dist", 16 | "lint": "tslint -c tslint.json '{src,test}/**/*.ts' -e 'src/types/generated/**/*.ts' --fix", 17 | "generate": "sh scripts/generate_types.sh ${npm_package_config_lnd_release_tag} ${npm_package_config_protoc_version}", 18 | "update-protos": "sh scripts/update_protos.sh" 19 | }, 20 | "repository": "https://github.com/RadarTech/lnrpc", 21 | "keywords": [ 22 | "lnd", 23 | "lightning network", 24 | "grpc", 25 | "client", 26 | "bitcoin" 27 | ], 28 | "author": "RadarTech", 29 | "license": "MIT", 30 | "dependencies": { 31 | "@grpc/grpc-js": "^1.2.4", 32 | "@grpc/proto-loader": "^0.4.0", 33 | "@types/google-protobuf": "^3.2.7", 34 | "pkg-dir": "^2.0.0", 35 | "ts-protoc-gen": "^0.8.0" 36 | }, 37 | "devDependencies": { 38 | "@types/glob": "^7.1.1", 39 | "@types/node": "^10.14.19", 40 | "@types/pkg-dir": "^2.0.1", 41 | "glob": "^7.1.5", 42 | "mocha": "^5.2.0", 43 | "mocha-typescript": "^1.1.17", 44 | "rimraf": "^3.0.0", 45 | "ts-node": "^8.4.1", 46 | "tslint": "^5.20.1", 47 | "typescript": "^4.1.3" 48 | }, 49 | "engines": { 50 | "node": ">= 10.*" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/types/rpc/invoices-rpc.ts: -------------------------------------------------------------------------------- 1 | import { Readable } from '../streams'; 2 | import { Invoice, RouteHint } from './ln-rpc'; 3 | 4 | export interface SubscribeSingleInvoiceRequest { 5 | rHash: Buffer | string; 6 | } 7 | 8 | export interface CancelInvoiceMsg { 9 | paymentHash: Buffer | string; 10 | } 11 | 12 | export interface AddHoldInvoiceRequest { 13 | memo?: string; 14 | hash?: Buffer | string; 15 | value?: number; 16 | valueMsat?: number; 17 | descriptionHash?: Buffer | string; 18 | expiry?: number; 19 | fallbackAddr?: string; 20 | cltvExpiry?: number; 21 | routeHints?: RouteHint[]; 22 | private?: boolean; 23 | } 24 | 25 | export interface AddHoldInvoiceResp { 26 | paymentRequest: string; 27 | } 28 | 29 | export interface SettleInvoiceMsg { 30 | preimage: Buffer | string; 31 | } 32 | 33 | /** 34 | * LND Invoices gRPC API Client 35 | */ 36 | export interface InvoicesRpc { 37 | /** 38 | * subscribeSingleInvoice returns a uni-directional stream (server -> client) 39 | * to notify the client of state transitions of the specified invoice. 40 | * Initially the current invoice state is always sent out. 41 | */ 42 | subscribeSingleInvoice(args: SubscribeSingleInvoiceRequest): Readable; 43 | 44 | /** 45 | * cancelInvoice cancels a currently open invoice. If the invoice is already 46 | * canceled, this call will succeed. If the invoice is already settled, it will 47 | * fail. 48 | */ 49 | cancelInvoice(args: CancelInvoiceMsg): Promise<{}>; 50 | 51 | /** 52 | * addHoldInvoice creates a hold invoice. It ties the invoice to the hash 53 | * supplied in the request. 54 | */ 55 | addHoldInvoice(args: AddHoldInvoiceRequest): Promise; 56 | 57 | /** 58 | * settleInvoice settles an accepted invoice. If the invoice is already 59 | * settled, this call will succeed. 60 | */ 61 | settleInvoice(args: SettleInvoiceMsg): Promise<{}>; 62 | } 63 | -------------------------------------------------------------------------------- /src/types/streams/duplex.ts: -------------------------------------------------------------------------------- 1 | import { Writable, WritableOptions } from 'stream'; 2 | import { Readable, ReadableOptions } from './readable'; 3 | 4 | export declare class Duplex extends Readable implements Writable { 5 | public writable: boolean; 6 | public readonly writableHighWaterMark: number; 7 | public readonly writableLength: number; 8 | constructor(opts?: DuplexOptions); 9 | public _write(chunk: REQ, encoding: string, callback: (error?: Error | null) => void): void; 10 | public _writev?(chunks: Array<{ chunk: REQ, encoding: string }>, callback: (error?: Error | null) => void): void; 11 | public _destroy(error: Error | null, callback: (error: Error | null) => void): void; 12 | public _final(callback: (error?: Error | null) => void): void; 13 | public write(chunk: REQ, cb?: (error: Error | null | undefined) => void): boolean; 14 | public write(chunk: REQ, encoding?: string, cb?: (error: Error | null | undefined) => void): boolean; 15 | public setDefaultEncoding(encoding: string): this; 16 | public end(cb?: () => void): void; 17 | public end(chunk: REQ, cb?: () => void): void; 18 | public end(chunk: REQ, encoding?: string, cb?: () => void): void; 19 | public cork(): void; 20 | public uncork(): void; 21 | } 22 | 23 | export interface DuplexOptions extends ReadableOptions, WritableOptions { 24 | allowHalfOpen?: boolean; 25 | readableObjectMode?: boolean; 26 | writableObjectMode?: boolean; 27 | read?(this: Duplex, size: number): void; 28 | write?(this: Duplex, chunk: REQ, encoding: string, callback: (error?: Error | null) => void): void; 29 | writev?( 30 | this: Duplex, 31 | chunks: Array<{ chunk: REQ, encoding: string }>, 32 | callback: (error?: Error | null) => void, 33 | ): void; 34 | final?(this: Duplex, callback: (error?: Error | null) => void): void; 35 | destroy?(this: Duplex, error: Error | null, callback: (error: Error | null) => void): void; 36 | } 37 | -------------------------------------------------------------------------------- /scripts/update_protos.sh: -------------------------------------------------------------------------------- 1 | # RPC Servers 2 | curl ${npm_package_config_lnd_url}/${npm_package_config_lnd_release_tag}/lnrpc/rpc.proto --create-dirs -o lnd/${npm_package_config_lnd_release_tag}/rpc.proto 3 | curl ${npm_package_config_lnd_url}/${npm_package_config_lnd_release_tag}/lnrpc/walletunlocker.proto --create-dirs -o lnd/${npm_package_config_lnd_release_tag}/walletunlocker.proto 4 | curl ${npm_package_config_lnd_url}/${npm_package_config_lnd_release_tag}/lnrpc/autopilotrpc/autopilot.proto --create-dirs -o lnd/${npm_package_config_lnd_release_tag}/autopilotrpc/autopilot.proto 5 | curl ${npm_package_config_lnd_url}/${npm_package_config_lnd_release_tag}/lnrpc/chainrpc/chainnotifier.proto --create-dirs -o lnd/${npm_package_config_lnd_release_tag}/chainrpc/chainnotifier.proto 6 | curl ${npm_package_config_lnd_url}/${npm_package_config_lnd_release_tag}/lnrpc/invoicesrpc/invoices.proto --create-dirs -o lnd/${npm_package_config_lnd_release_tag}/invoicesrpc/invoices.proto 7 | curl ${npm_package_config_lnd_url}/${npm_package_config_lnd_release_tag}/lnrpc/routerrpc/router.proto --create-dirs -o lnd/${npm_package_config_lnd_release_tag}/routerrpc/router.proto 8 | curl ${npm_package_config_lnd_url}/${npm_package_config_lnd_release_tag}/lnrpc/signrpc/signer.proto --create-dirs -o lnd/${npm_package_config_lnd_release_tag}/signrpc/signer.proto 9 | curl ${npm_package_config_lnd_url}/${npm_package_config_lnd_release_tag}/lnrpc/walletrpc/walletkit.proto --create-dirs -o lnd/${npm_package_config_lnd_release_tag}/walletrpc/walletkit.proto 10 | curl ${npm_package_config_lnd_url}/${npm_package_config_lnd_release_tag}/lnrpc/watchtowerrpc/watchtower.proto --create-dirs -o lnd/${npm_package_config_lnd_release_tag}/watchtowerrpc/watchtower.proto 11 | curl ${npm_package_config_lnd_url}/${npm_package_config_lnd_release_tag}/lnrpc/wtclientrpc/wtclient.proto --create-dirs -o lnd/${npm_package_config_lnd_release_tag}/wtclientrpc/wtclient.proto 12 | 13 | # Clean Invoices RPC Server 14 | sed -i '' 's/import "google\/api\/annotations\.proto";//g' lnd/${npm_package_config_lnd_release_tag}/invoicesrpc/invoices.proto 15 | -------------------------------------------------------------------------------- /scripts/generate_types.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | # Generate typescript definitions and service definitions from proto file 3 | 4 | set -e 5 | 6 | LND_RELEASE_TAG=$1 7 | PROTOC_VERSION=$2 8 | 9 | # Sanitize all proto files prior to type generation 10 | rm -f *.proto 11 | ts-node scripts/proto-sanitizer.ts "lnd/${LND_RELEASE_TAG}/**/*.proto" 12 | 13 | # Copy google definitions 14 | cp -r google/. lnd/${npm_package_config_lnd_release_tag}/google 15 | 16 | GENERATED_TYPES_DIR=src/types/generated 17 | if [ -d "$GENERATED_TYPES_DIR" ] 18 | then 19 | rm -rf "$GENERATED_TYPES_DIR" 20 | fi 21 | mkdir -p "$GENERATED_TYPES_DIR" 22 | 23 | # Download and install protoc 24 | unameOut="$(uname -s)" 25 | case "${unameOut}" in 26 | Linux*) platform=Linux;; 27 | Darwin*) platform=Mac;; 28 | *) platform="UNKNOWN:${unameOut}" 29 | esac 30 | 31 | mkdir -p protoc 32 | if [[ $platform == 'Linux' ]]; then 33 | PROTOC_URL="https://github.com/google/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip" 34 | elif [[ $platform == 'Mac' ]]; then 35 | PROTOC_URL="https://github.com/google/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-osx-x86_64.zip" 36 | else 37 | echo "Cannot download protoc. ${platform} is not currently supported by ts-protoc-gen" 38 | exit 1 39 | fi 40 | 41 | curl -L ${PROTOC_URL} -o "protoc-${PROTOC_VERSION}.zip" 42 | unzip "protoc-${PROTOC_VERSION}.zip" -d protoc 43 | rm "protoc-${PROTOC_VERSION}.zip" 44 | 45 | # Run protoc 46 | echo "running protoc..." 47 | protoc/bin/protoc \ 48 | --proto_path=lnd/${LND_RELEASE_TAG} \ 49 | --plugin=protoc-gen-ts=node_modules/.bin/protoc-gen-ts \ 50 | --ts_out=$GENERATED_TYPES_DIR \ 51 | google/api/annotations.proto \ 52 | google/api/http.proto \ 53 | rpc.proto \ 54 | walletunlocker.proto \ 55 | autopilotrpc/autopilot.proto \ 56 | chainrpc/chainnotifier.proto \ 57 | invoicesrpc/invoices.proto \ 58 | routerrpc/router.proto \ 59 | signrpc/signer.proto \ 60 | walletrpc/walletkit.proto \ 61 | watchtowerrpc/watchtower.proto \ 62 | wtclientrpc/wtclient.proto 63 | 64 | # Cleanup proto directory/files 65 | rm -rf *.proto protoc lnd/${npm_package_config_lnd_release_tag}/google 66 | 67 | # Remove 'List' from all generated Array type names 68 | ts-node scripts/clean-repeated.ts 69 | -------------------------------------------------------------------------------- /src/types/generated/watchtowerrpc/watchtower_pb.d.ts: -------------------------------------------------------------------------------- 1 | // package: watchtowerrpc 2 | // file: watchtowerrpc/watchtower.proto 3 | 4 | import * as jspb from "google-protobuf"; 5 | 6 | export class GetInfoRequest extends jspb.Message { 7 | serializeBinary(): Uint8Array; 8 | toObject(includeInstance?: boolean): GetInfoRequest.AsObject; 9 | static toObject(includeInstance: boolean, msg: GetInfoRequest): GetInfoRequest.AsObject; 10 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 11 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 12 | static serializeBinaryToWriter(message: GetInfoRequest, writer: jspb.BinaryWriter): void; 13 | static deserializeBinary(bytes: Uint8Array): GetInfoRequest; 14 | static deserializeBinaryFromReader(message: GetInfoRequest, reader: jspb.BinaryReader): GetInfoRequest; 15 | } 16 | 17 | export namespace GetInfoRequest { 18 | export type AsObject = { 19 | } 20 | } 21 | 22 | export class GetInfoResponse extends jspb.Message { 23 | getPubkey(): Uint8Array | string; 24 | getPubkey_asU8(): Uint8Array; 25 | getPubkey_asB64(): string; 26 | setPubkey(value: Uint8Array | string): void; 27 | 28 | clearListenersList(): void; 29 | getListenersList(): Array; 30 | setListenersList(value: Array): void; 31 | addListeners(value: string, index?: number): string; 32 | 33 | clearUrisList(): void; 34 | getUrisList(): Array; 35 | setUrisList(value: Array): void; 36 | addUris(value: string, index?: number): string; 37 | 38 | serializeBinary(): Uint8Array; 39 | toObject(includeInstance?: boolean): GetInfoResponse.AsObject; 40 | static toObject(includeInstance: boolean, msg: GetInfoResponse): GetInfoResponse.AsObject; 41 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 42 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 43 | static serializeBinaryToWriter(message: GetInfoResponse, writer: jspb.BinaryWriter): void; 44 | static deserializeBinary(bytes: Uint8Array): GetInfoResponse; 45 | static deserializeBinaryFromReader(message: GetInfoResponse, reader: jspb.BinaryReader): GetInfoResponse; 46 | } 47 | 48 | export namespace GetInfoResponse { 49 | export type AsObject = { 50 | pubkey: Uint8Array | string, 51 | listeners: Array, 52 | uris: Array, 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/types/general.ts: -------------------------------------------------------------------------------- 1 | import grpc, { ChannelCredentials, Client } from '@grpc/grpc-js'; 2 | import * as grpcLoader from '@grpc/proto-loader'; 3 | 4 | export type GrpcLoader = typeof grpcLoader; 5 | 6 | export type Grpc = typeof grpc; 7 | 8 | export interface NestedGrpcObject { 9 | [index: string]: { 10 | [index: string]: typeof Client; 11 | }; 12 | } 13 | 14 | export interface ConnectionConfig { 15 | grpcPkgObj: NestedGrpcObject; 16 | server: string; 17 | credentials: ChannelCredentials; 18 | } 19 | 20 | export interface GrpcServiceConfig extends ConnectionConfig { 21 | service: any; 22 | subscriptionMethods?: string[]; 23 | } 24 | 25 | export interface GrpcObjectConfig { 26 | protoFilePath: string; 27 | includeDirs?: string[]; 28 | grpcLoader: GrpcLoader; 29 | grpc: Grpc; 30 | includeDefaults?: boolean; 31 | } 32 | 33 | export interface RpcClientConfig { 34 | server?: string; // URL for the lightning node to connect to ie. localhost:10009 35 | tls?: string | false; // /path/to/tls.cert or false to disable certificate pinning 36 | cert?: Buffer | string; // string or buffer representation of tls.cert 37 | macaroonPath?: string; 38 | macaroon?: Buffer | string; // hex-encoded string of macaroon file 39 | certEncoding?: string; // Default to utf-8 40 | grpcLoader?: GrpcLoader; 41 | grpc?: Grpc; 42 | includeDefaults?: boolean; // Whether default values should be included in gRPC responses. Defaults to false. 43 | } 44 | 45 | export interface AutopilotRpcClientConfig extends RpcClientConfig { 46 | autopilot?: any; 47 | } 48 | 49 | export interface ChainRpcClientConfig extends RpcClientConfig { 50 | chainNotifier?: any; 51 | } 52 | 53 | export interface InvoicesRpcClientConfig extends RpcClientConfig { 54 | invoices?: any; 55 | } 56 | 57 | export interface RouterRpcClientConfig extends RpcClientConfig { 58 | router?: any; 59 | } 60 | 61 | export interface SignRpcClientConfig extends RpcClientConfig { 62 | signer?: any; 63 | } 64 | 65 | export interface WalletRpcClientConfig extends RpcClientConfig { 66 | walletKit?: any; 67 | } 68 | 69 | export interface WatchtowerRpcClientConfig extends RpcClientConfig { 70 | watchtower?: any; 71 | } 72 | 73 | export interface WtClientRpcClientConfig extends RpcClientConfig { 74 | watchtowerClient?: any; 75 | } 76 | 77 | export interface LnRpcClientConfig extends RpcClientConfig { 78 | lightning?: any; 79 | walletUnlocker?: any; 80 | } 81 | -------------------------------------------------------------------------------- /lnd/v0.11.1-beta/autopilotrpc/autopilot.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package autopilotrpc; 4 | 5 | option go_package = "github.com/lightningnetwork/lnd/lnrpc/autopilotrpc"; 6 | 7 | // Autopilot is a service that can be used to get information about the current 8 | // state of the daemon's autopilot agent, and also supply it with information 9 | // that can be used when deciding where to open channels. 10 | service Autopilot { 11 | /* 12 | Status returns whether the daemon's autopilot agent is active. 13 | */ 14 | rpc Status (StatusRequest) returns (StatusResponse); 15 | 16 | /* 17 | ModifyStatus is used to modify the status of the autopilot agent, like 18 | enabling or disabling it. 19 | */ 20 | rpc ModifyStatus (ModifyStatusRequest) returns (ModifyStatusResponse); 21 | 22 | /* 23 | QueryScores queries all available autopilot heuristics, in addition to any 24 | active combination of these heruristics, for the scores they would give to 25 | the given nodes. 26 | */ 27 | rpc QueryScores (QueryScoresRequest) returns (QueryScoresResponse); 28 | 29 | /* 30 | SetScores attempts to set the scores used by the running autopilot agent, 31 | if the external scoring heuristic is enabled. 32 | */ 33 | rpc SetScores (SetScoresRequest) returns (SetScoresResponse); 34 | } 35 | 36 | message StatusRequest { 37 | } 38 | 39 | message StatusResponse { 40 | // Indicates whether the autopilot is active or not. 41 | bool active = 1; 42 | } 43 | 44 | message ModifyStatusRequest { 45 | // Whether the autopilot agent should be enabled or not. 46 | bool enable = 1; 47 | } 48 | 49 | message ModifyStatusResponse { 50 | } 51 | 52 | message QueryScoresRequest { 53 | repeated string pubkeys = 1; 54 | 55 | // If set, we will ignore the local channel state when calculating scores. 56 | bool ignore_local_state = 2; 57 | } 58 | 59 | message QueryScoresResponse { 60 | message HeuristicResult { 61 | string heuristic = 1; 62 | map scores = 2; 63 | } 64 | 65 | repeated HeuristicResult results = 1; 66 | } 67 | 68 | message SetScoresRequest { 69 | // The name of the heuristic to provide scores to. 70 | string heuristic = 1; 71 | 72 | /* 73 | A map from hex-encoded public keys to scores. Scores must be in the range 74 | [0.0, 1.0]. 75 | */ 76 | map scores = 2; 77 | } 78 | 79 | message SetScoresResponse { 80 | } 81 | -------------------------------------------------------------------------------- /src/rpc/sign-rpc.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import pkgDir from 'pkg-dir'; 3 | import packageJson from '../../package.json'; 4 | import { createSigner } from '../services'; 5 | import { SignRpc, SignRpcClientConfig } from '../types'; 6 | import { createCredentials } from './create-credentials'; 7 | import { createGrpcObject } from './create-grpc-object'; 8 | import { defaults } from './defaults'; 9 | 10 | /** 11 | * Factory for a signrpc instance & proxy responsible for: 12 | * - Generating a GRPC Descriptor from user's config 13 | * - Instantiating/exposing all GRPC Services 14 | * - Resolving a proxy that: 15 | * 1. Invokes all top-level method calls to the lightning 16 | * proxy for user convience 17 | * 2. Allow basic user property requests to all GRPC Services 18 | * 19 | * @param userConfig The user provided configuration details 20 | * @return Returns proxy to signrpc instance 21 | */ 22 | export async function createSignRpc(userConfig: SignRpcClientConfig): Promise { 23 | const rootPath = await pkgDir(__dirname); 24 | const protoFilePath = join( 25 | rootPath, 26 | `lnd/${packageJson.config['lnd-release-tag']}/signrpc/signer.proto`, 27 | ); 28 | 29 | // Configuration options 30 | const config = { 31 | ...defaults, 32 | ...userConfig, 33 | }; 34 | const { signer, server, grpcLoader, grpc, includeDefaults } = config; 35 | 36 | // Generate grpc SSL credentials 37 | const credentials = await createCredentials(config); 38 | 39 | // Create RPC from proto and return GRPC 40 | const grpcPkgObj = createGrpcObject({ 41 | includeDefaults, 42 | protoFilePath, 43 | grpcLoader, 44 | grpc, 45 | }); 46 | 47 | /** 48 | * Signrpc instance 49 | */ 50 | const signrpc = Object.create(null, { 51 | description: {value: grpcPkgObj}, 52 | signer: { 53 | value: 54 | signer || createSigner({ 55 | grpcPkgObj, 56 | server, 57 | credentials, 58 | }), 59 | }, 60 | }); 61 | 62 | return new Proxy(signrpc, { 63 | /** 64 | * Provide lop-level access to any signer 65 | * methods, otherwise provide user with fallback value 66 | * @param target 67 | * @param key 68 | */ 69 | get(target: any, key: string): any { 70 | if (typeof target.signer[key] === 'function') { 71 | return target.signer[key].bind(target.signer); 72 | } else { 73 | return target[key]; // forward 74 | } 75 | }, 76 | }); 77 | } 78 | -------------------------------------------------------------------------------- /src/rpc/autopilot-rpc.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import pkgDir from 'pkg-dir'; 3 | import packageJson from '../../package.json'; 4 | import { createAutopilot } from '../services'; 5 | import { AutopilotRpc, AutopilotRpcClientConfig } from '../types'; 6 | import { createCredentials } from './create-credentials'; 7 | import { createGrpcObject } from './create-grpc-object'; 8 | import { defaults } from './defaults'; 9 | 10 | /** 11 | * Factory for a autopilotrpc instance & proxy responsible for: 12 | * - Generating a GRPC Descriptor from user's config 13 | * - Instantiating/exposing all GRPC Services 14 | * - Resolving a proxy that: 15 | * 1. Invokes all top-level method calls to the lightning 16 | * proxy for user convience 17 | * 2. Allow basic user property requests to all GRPC Services 18 | * 19 | * @param userConfig The user provided configuration details 20 | * @return Returns proxy to autopilotrpc instance 21 | */ 22 | export async function createAutopilotRpc(userConfig: AutopilotRpcClientConfig): Promise { 23 | const rootPath = await pkgDir(__dirname); 24 | const protoFilePath = join( 25 | rootPath, 26 | `lnd/${packageJson.config['lnd-release-tag']}/autopilotrpc/autopilot.proto`, 27 | ); 28 | 29 | // Configuration options 30 | const config = { 31 | ...defaults, 32 | ...userConfig, 33 | }; 34 | const { autopilot, server, grpcLoader, grpc, includeDefaults } = config; 35 | 36 | // Generate grpc SSL credentials 37 | const credentials = await createCredentials(config); 38 | 39 | // Create RPC from proto and return GRPC 40 | const grpcPkgObj = createGrpcObject({ 41 | includeDefaults, 42 | protoFilePath, 43 | grpcLoader, 44 | grpc, 45 | }); 46 | 47 | /** 48 | * Autopilotrpc instance 49 | */ 50 | const autopilotrpc = Object.create(null, { 51 | description: {value: grpcPkgObj}, 52 | autopilot: { 53 | value: 54 | autopilot || createAutopilot({ 55 | grpcPkgObj, 56 | server, 57 | credentials, 58 | }), 59 | }, 60 | }); 61 | 62 | return new Proxy(autopilotrpc, { 63 | /** 64 | * Provide lop-level access to any autopilot 65 | * methods, otherwise provide user with fallback value 66 | * @param target 67 | * @param key 68 | */ 69 | get(target: any, key: string): any { 70 | if (typeof target.autopilot[key] === 'function') { 71 | return target.autopilot[key].bind(target.autopilot); 72 | } else { 73 | return target[key]; // forward 74 | } 75 | }, 76 | }); 77 | } 78 | -------------------------------------------------------------------------------- /src/rpc/chain-rpc.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import pkgDir from 'pkg-dir'; 3 | import packageJson from '../../package.json'; 4 | import { createChainNotifier } from '../services'; 5 | import { ChainRpc, ChainRpcClientConfig } from '../types'; 6 | import { createCredentials } from './create-credentials'; 7 | import { createGrpcObject } from './create-grpc-object'; 8 | import { defaults } from './defaults'; 9 | 10 | /** 11 | * Factory for a chainrpc instance & proxy responsible for: 12 | * - Generating a GRPC Descriptor from user's config 13 | * - Instantiating/exposing all GRPC Services 14 | * - Resolving a proxy that: 15 | * 1. Invokes all top-level method calls to the lightning 16 | * proxy for user convience 17 | * 2. Allow basic user property requests to all GRPC Services 18 | * 19 | * @param userConfig The user provided configuration details 20 | * @return Returns proxy to chainrpc instance 21 | */ 22 | export async function createChainRpc(userConfig: ChainRpcClientConfig): Promise { 23 | const rootPath = await pkgDir(__dirname); 24 | const protoFilePath = join( 25 | rootPath, 26 | `lnd/${packageJson.config['lnd-release-tag']}/chainrpc/chainnotifier.proto`, 27 | ); 28 | 29 | // Configuration options 30 | const config = { 31 | ...defaults, 32 | ...userConfig, 33 | }; 34 | const { chainNotifier, server, grpcLoader, grpc, includeDefaults } = config; 35 | 36 | // Generate grpc SSL credentials 37 | const credentials = await createCredentials(config); 38 | 39 | // Create RPC from proto and return GRPC 40 | const grpcPkgObj = createGrpcObject({ 41 | includeDefaults, 42 | protoFilePath, 43 | grpcLoader, 44 | grpc, 45 | }); 46 | 47 | /** 48 | * Chainrpc instance 49 | * @type {chainrpc} 50 | */ 51 | const chainrpc = Object.create(null, { 52 | description: {value: grpcPkgObj}, 53 | chainNotifier: { 54 | value: 55 | chainNotifier || createChainNotifier({ 56 | grpcPkgObj, 57 | server, 58 | credentials, 59 | }), 60 | }, 61 | }); 62 | 63 | return new Proxy(chainrpc, { 64 | /** 65 | * Provide lop-level access to any chainnotifier 66 | * methods, otherwise provide user with fallback value 67 | * @param target 68 | * @param key 69 | */ 70 | get(target: any, key: string): any { 71 | if (typeof target.chainNotifier[key] === 'function') { 72 | return target.chainNotifier[key].bind(target.chainNotifier); 73 | } else { 74 | return target[key]; // forward 75 | } 76 | }, 77 | }); 78 | } 79 | -------------------------------------------------------------------------------- /src/rpc/watchtower-rpc.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import pkgDir from 'pkg-dir'; 3 | import packageJson from '../../package.json'; 4 | import { createWatchtower } from '../services'; 5 | import { WatchtowerRpc, WatchtowerRpcClientConfig } from '../types'; 6 | import { createCredentials } from './create-credentials'; 7 | import { createGrpcObject } from './create-grpc-object'; 8 | import { defaults } from './defaults'; 9 | 10 | /** 11 | * Factory for a watchtowerrpc instance & proxy responsible for: 12 | * - Generating a GRPC Descriptor from user's config 13 | * - Instantiating/exposing all GRPC Services 14 | * - Resolving a proxy that: 15 | * 1. Invokes all top-level method calls to the lightning 16 | * proxy for user convience 17 | * 2. Allow basic user property requests to all GRPC Services 18 | * 19 | * @param userConfig The user provided configuration details 20 | * @return Returns proxy to watchtowerrpc instance 21 | */ 22 | export async function createWatchtowerRpc( 23 | userConfig: WatchtowerRpcClientConfig, 24 | ): Promise { 25 | const rootPath = await pkgDir(__dirname); 26 | const protoFilePath = join( 27 | rootPath, 28 | `lnd/${packageJson.config['lnd-release-tag']}/watchtowerrpc/watchtower.proto`, 29 | ); 30 | 31 | // Configuration options 32 | const config = { 33 | ...defaults, 34 | ...userConfig, 35 | }; 36 | const { watchtower, server, grpcLoader, grpc, includeDefaults } = config; 37 | 38 | // Generate grpc SSL credentials 39 | const credentials = await createCredentials(config); 40 | 41 | // Create RPC from proto and return GRPC 42 | const grpcPkgObj = createGrpcObject({ 43 | includeDefaults, 44 | protoFilePath, 45 | grpcLoader, 46 | grpc, 47 | }); 48 | 49 | /** 50 | * Watchtowerrpc instance 51 | */ 52 | const watchtowerrpc = Object.create(null, { 53 | description: {value: grpcPkgObj}, 54 | watchtower: { 55 | value: 56 | watchtower || createWatchtower({ 57 | grpcPkgObj, 58 | server, 59 | credentials, 60 | }), 61 | }, 62 | }); 63 | 64 | return new Proxy(watchtowerrpc, { 65 | /** 66 | * Provide lop-level access to any watchtower 67 | * methods, otherwise provide user with fallback value 68 | * @param target 69 | * @param key 70 | */ 71 | get(target: any, key: string): any { 72 | if (typeof target.watchtower[key] === 'function') { 73 | return target.watchtower[key].bind(target.watchtower); 74 | } else { 75 | return target[key]; // forward 76 | } 77 | }, 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /src/rpc/wt-client-rpc.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import pkgDir from 'pkg-dir'; 3 | import packageJson from '../../package.json'; 4 | import { createWatchtowerClient } from '../services'; 5 | import { WtClientRpc, WtClientRpcClientConfig } from '../types'; 6 | import { createCredentials } from './create-credentials'; 7 | import { createGrpcObject } from './create-grpc-object'; 8 | import { defaults } from './defaults'; 9 | 10 | /** 11 | * Factory for a wtclientrpc instance & proxy responsible for: 12 | * - Generating a GRPC Descriptor from user's config 13 | * - Instantiating/exposing all GRPC Services 14 | * - Resolving a proxy that: 15 | * 1. Invokes all top-level method calls to the lightning 16 | * proxy for user convience 17 | * 2. Allow basic user property requests to all GRPC Services 18 | * 19 | * @param userConfig The user provided configuration details 20 | * @return Returns proxy to wtclientrpc instance 21 | */ 22 | export async function createWtClientRpc(userConfig: WtClientRpcClientConfig): Promise { 23 | const rootPath = await pkgDir(__dirname); 24 | const protoFilePath = join( 25 | rootPath, 26 | `lnd/${packageJson.config['lnd-release-tag']}/wtclientrpc/wtclient.proto`, 27 | ); 28 | 29 | // Configuration options 30 | const config = { 31 | ...defaults, 32 | ...userConfig, 33 | }; 34 | const { watchtowerClient, server, grpcLoader, grpc, includeDefaults } = config; 35 | 36 | // Generate grpc SSL credentials 37 | const credentials = await createCredentials(config); 38 | 39 | // Create RPC from proto and return GRPC 40 | const grpcPkgObj = createGrpcObject({ 41 | includeDefaults, 42 | protoFilePath, 43 | grpcLoader, 44 | grpc, 45 | }); 46 | 47 | /** 48 | * Wtclientrpc instance 49 | */ 50 | const wtclientrpc = Object.create(null, { 51 | description: {value: grpcPkgObj}, 52 | watchtowerClient: { 53 | value: 54 | watchtowerClient || createWatchtowerClient({ 55 | grpcPkgObj, 56 | server, 57 | credentials, 58 | }), 59 | }, 60 | }); 61 | 62 | return new Proxy(wtclientrpc, { 63 | /** 64 | * Provide lop-level access to any wtClient 65 | * methods, otherwise provide user with fallback value 66 | * @param target 67 | * @param key 68 | */ 69 | get(target: any, key: string): any { 70 | if (typeof target.watchtowerClient[key] === 'function') { 71 | return target.watchtowerClient[key].bind(target.watchtowerClient); 72 | } else { 73 | return target[key]; // forward 74 | } 75 | }, 76 | }); 77 | } 78 | -------------------------------------------------------------------------------- /src/rpc/router-rpc.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import pkgDir from 'pkg-dir'; 3 | import packageJson from '../../package.json'; 4 | import { createRouter } from '../services'; 5 | import { RouterRpc, RouterRpcClientConfig } from '../types'; 6 | import { createCredentials } from './create-credentials'; 7 | import { createGrpcObject } from './create-grpc-object'; 8 | import { defaults } from './defaults'; 9 | 10 | /** 11 | * Factory for a routerrpc instance & proxy responsible for: 12 | * - Generating a GRPC Descriptor from user's config 13 | * - Instantiating/exposing all GRPC Services 14 | * - Resolving a proxy that: 15 | * 1. Invokes all top-level method calls to the lightning 16 | * proxy for user convience 17 | * 2. Allow basic user property requests to all GRPC Services 18 | * 19 | * @param userConfig The user provided configuration details 20 | * @return Returns proxy to routerrpc instance 21 | */ 22 | export async function createRouterRpc(userConfig: RouterRpcClientConfig): Promise { 23 | const rootPath = await pkgDir(__dirname); 24 | const lndProtosRootPath = join( 25 | rootPath, 26 | `lnd/${packageJson.config['lnd-release-tag']}`, 27 | ); 28 | const protoFilePath = join( 29 | lndProtosRootPath, 30 | '/routerrpc/router.proto', 31 | ); 32 | 33 | // Configuration options 34 | const config = { 35 | ...defaults, 36 | ...userConfig, 37 | }; 38 | const { router, server, grpcLoader, grpc, includeDefaults } = config; 39 | 40 | // Generate grpc SSL credentials 41 | const credentials = await createCredentials(config); 42 | 43 | // Create RPC from proto and return GRPC 44 | const grpcPkgObj = createGrpcObject({ 45 | includeDefaults, 46 | protoFilePath, 47 | grpcLoader, 48 | grpc, 49 | includeDirs: [lndProtosRootPath], 50 | }); 51 | 52 | /** 53 | * Routerrpc instance 54 | */ 55 | const routerrpc = Object.create(null, { 56 | description: {value: grpcPkgObj}, 57 | router: { 58 | value: 59 | router || createRouter({ 60 | grpcPkgObj, 61 | server, 62 | credentials, 63 | }), 64 | }, 65 | }); 66 | 67 | return new Proxy(routerrpc, { 68 | /** 69 | * Provide lop-level access to any router 70 | * methods, otherwise provide user with fallback value 71 | * @param target 72 | * @param key 73 | */ 74 | get(target: any, key: string): any { 75 | if (typeof target.router[key] === 'function') { 76 | return target.router[key].bind(target.router); 77 | } else { 78 | return target[key]; // forward 79 | } 80 | }, 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /src/types/rpc/wt-client-rpc.ts: -------------------------------------------------------------------------------- 1 | export interface AddTowerRequest { 2 | pubkey: Buffer | string; 3 | address?: string; 4 | } 5 | 6 | export interface RemoveTowerRequest { 7 | pubkey: Buffer | string; 8 | address?: string; 9 | } 10 | 11 | export interface ListTowersRequest { 12 | includeSessions?: boolean; 13 | } 14 | 15 | export interface TowerSession { 16 | numBackups: number; 17 | numPendingBackups: number; 18 | maxBackups: number; 19 | sweepSatPerByte: number; 20 | } 21 | 22 | export interface Tower { 23 | pubkey: Buffer | string; 24 | addresses: string; 25 | activeSessionCandidate: boolean; 26 | numSessions: number; 27 | sessions: TowerSession[]; 28 | } 29 | 30 | export interface ListTowersResponse { 31 | towers: Tower[]; 32 | } 33 | 34 | export interface GetTowerInfoRequest { 35 | pubkey: Buffer | string; 36 | includeSessions?: boolean; 37 | } 38 | 39 | export interface StatsResponse { 40 | numBackups: number; 41 | numPendingBackups: number; 42 | numFailedBackups: number; 43 | numSessionsAcquired: number; 44 | numSessionsExhausted: number; 45 | } 46 | 47 | export interface PolicyResponse { 48 | maxUpdates: number; 49 | sweepSatPerByte: number; 50 | } 51 | 52 | /** 53 | * LND WtClient gRPC API Client 54 | */ 55 | export interface WtClientRpc { 56 | /* 57 | * addTower adds a new watchtower reachable at the given address and 58 | * considers it for new sessions. If the watchtower already exists, then 59 | * any new addresses included will be considered when dialing it for 60 | * session negotiations and backups. 61 | */ 62 | addTower(args: AddTowerRequest): Promise<{}>; 63 | 64 | /* 65 | * removeTower removes a watchtower from being considered for future session 66 | * negotiations and from being used for any subsequent backups until it's added 67 | * again. If an address is provided, then this RPC only serves as a way of 68 | * removing the address from the watchtower instead. 69 | */ 70 | removeTower(args: RemoveTowerRequest): Promise<{}>; 71 | 72 | /** 73 | * listTowers returns the list of watchtowers registered with the client. 74 | */ 75 | listTowers(args?: ListTowersRequest): Promise; 76 | 77 | /** 78 | * getTowerInfo retrieves information for a registered watchtower. 79 | */ 80 | getTowerInfo(args: GetTowerInfoRequest): Promise; 81 | 82 | /** 83 | * stats returns the in-memory statistics of the client since startup. 84 | */ 85 | stats(args?: {}): Promise; 86 | 87 | /** 88 | * policy returns the active watchtower client policy configuration. 89 | */ 90 | policy(args?: {}): Promise; 91 | } 92 | -------------------------------------------------------------------------------- /src/rpc/wallet-rpc.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import pkgDir from 'pkg-dir'; 3 | import packageJson from '../../package.json'; 4 | import { createWalletKit } from '../services'; 5 | import { WalletRpc, WalletRpcClientConfig } from '../types'; 6 | import { createCredentials } from './create-credentials'; 7 | import { createGrpcObject } from './create-grpc-object'; 8 | import { defaults } from './defaults'; 9 | 10 | /** 11 | * Factory for a walletrpc instance & proxy responsible for: 12 | * - Generating a GRPC Descriptor from user's config 13 | * - Instantiating/exposing all GRPC Services 14 | * - Resolving a proxy that: 15 | * 1. Invokes all top-level method calls to the lightning 16 | * proxy for user convience 17 | * 2. Allow basic user property requests to all GRPC Services 18 | * 19 | * @param userConfig The user provided configuration details 20 | * @return Returns proxy to walletrpc instance 21 | */ 22 | export async function createWalletRpc(userConfig: WalletRpcClientConfig): Promise { 23 | const rootPath = await pkgDir(__dirname); 24 | const lndProtosRootPath = join( 25 | rootPath, 26 | `lnd/${packageJson.config['lnd-release-tag']}`, 27 | ); 28 | const protoFilePath = join( 29 | lndProtosRootPath, 30 | '/walletrpc/walletkit.proto', 31 | ); 32 | 33 | // Configuration options 34 | const config = { 35 | ...defaults, 36 | ...userConfig, 37 | }; 38 | const { walletKit, server, grpcLoader, grpc, includeDefaults } = config; 39 | 40 | // Generate grpc SSL credentials 41 | const credentials = await createCredentials(config); 42 | 43 | // Create RPC from proto and return GRPC 44 | const grpcPkgObj = createGrpcObject({ 45 | includeDefaults, 46 | protoFilePath, 47 | grpcLoader, 48 | grpc, 49 | includeDirs: [lndProtosRootPath], 50 | }); 51 | 52 | /** 53 | * Walletrpc instance 54 | */ 55 | const walletrpc = Object.create(null, { 56 | description: {value: grpcPkgObj}, 57 | walletKit: { 58 | value: 59 | walletKit || createWalletKit({ 60 | grpcPkgObj, 61 | server, 62 | credentials, 63 | }), 64 | }, 65 | }); 66 | 67 | return new Proxy(walletrpc, { 68 | /** 69 | * Provide lop-level access to any walletKit 70 | * methods, otherwise provide user with fallback value 71 | * @param target 72 | * @param key 73 | */ 74 | get(target: any, key: string): any { 75 | if (typeof target.walletKit[key] === 'function') { 76 | return target.walletKit[key].bind(target.walletKit); 77 | } else { 78 | return target[key]; // forward 79 | } 80 | }, 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /src/rpc/create-credentials.ts: -------------------------------------------------------------------------------- 1 | import { ChannelCredentials } from '@grpc/grpc-js'; 2 | import fs from 'fs'; 3 | import { promisify } from 'util'; 4 | import { RpcClientConfig } from '../types'; 5 | 6 | const readFile = promisify(fs.readFile); 7 | 8 | /** 9 | * Generate grpc SSL credentials and combine with the macaroon 10 | * credentials if necessary. 11 | * @param config The rpc client configuration 12 | */ 13 | export async function createCredentials(config: RpcClientConfig): Promise { 14 | let credentials: ChannelCredentials; 15 | const { grpc } = config; 16 | 17 | try { 18 | // Use any SSL cert 19 | let { cert } = config; 20 | const { certEncoding, tls } = config; 21 | 22 | // Fallback optional .tls file path 23 | if (!cert && tls) { 24 | cert = await readFile(tls); 25 | } 26 | 27 | // Convert `cert` string to Buffer 28 | if (cert && !Buffer.isBuffer(cert)) { 29 | cert = Buffer.from(cert, certEncoding); 30 | } 31 | 32 | // Required for lnd SSL handshake when a cert is provided: 33 | // (SSL_ERROR_SSL: error:14094410) 34 | // More about GRPC environment variables here: 35 | // https://grpc.io/grpc/core/md_doc_environment_variables.html 36 | if (cert && !process.env.GRPC_SSL_CIPHER_SUITES) { 37 | process.env.GRPC_SSL_CIPHER_SUITES = 'HIGH+ECDSA'; 38 | } 39 | 40 | // NOTE: cert may be undefined at this point 41 | // which is desirable for when certificate pinning 42 | // is not necessary (i.e. BTCPayServer connection) 43 | credentials = grpc.credentials.createSsl(cert as Buffer); 44 | } catch (e) { 45 | if (!e.code) e.code = 'INVALID_SSL_CERT'; 46 | throw e; 47 | } 48 | 49 | // Combine SSL and Macaroon credentials 50 | if (config.macaroon || config.macaroonPath) { 51 | const metadata = new grpc.Metadata(); 52 | const macaroon = config.macaroon || (await readFile(config.macaroonPath)); 53 | 54 | // Add hex encoded macaroon 55 | // to gRPC metadata 56 | metadata.add( 57 | 'macaroon', 58 | Buffer.isBuffer(macaroon) ? macaroon.toString('hex') : macaroon, 59 | ); 60 | 61 | // Create macaroon credentials 62 | const macaroonCredentials = grpc.credentials.createFromMetadataGenerator( 63 | (_, callback) => { 64 | callback(null, metadata); 65 | }, 66 | ); 67 | 68 | // Update existing cert credentials by combining macaroon auth 69 | // credentials such that every call is properly encrypted and 70 | // authenticated 71 | credentials = grpc.credentials.combineChannelCredentials( 72 | credentials, 73 | macaroonCredentials, 74 | ); 75 | } 76 | return credentials; 77 | } 78 | -------------------------------------------------------------------------------- /src/rpc/invoices-rpc.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import pkgDir from 'pkg-dir'; 3 | import packageJson from '../../package.json'; 4 | import { createInvoices } from '../services'; 5 | import { InvoicesRpc, InvoicesRpcClientConfig } from '../types'; 6 | import { createCredentials } from './create-credentials'; 7 | import { createGrpcObject } from './create-grpc-object'; 8 | import { defaults } from './defaults'; 9 | 10 | /** 11 | * Factory for a invoicesrpc instance & proxy responsible for: 12 | * - Generating a GRPC Descriptor from user's config 13 | * - Instantiating/exposing all GRPC Services 14 | * - Resolving a proxy that: 15 | * 1. Invokes all top-level method calls to the lightning 16 | * proxy for user convience 17 | * 2. Allow basic user property requests to all GRPC Services 18 | * 19 | * @param userConfig The user provided configuration details 20 | * @return Returns proxy to invoicesrpc instance 21 | */ 22 | export async function createInvoicesRpc(userConfig: InvoicesRpcClientConfig): Promise { 23 | const rootPath = await pkgDir(__dirname); 24 | const lndProtosRootPath = join( 25 | rootPath, 26 | `lnd/${packageJson.config['lnd-release-tag']}`, 27 | ); 28 | const protoFilePath = join( 29 | lndProtosRootPath, 30 | '/invoicesrpc/invoices.proto', 31 | ); 32 | 33 | // Configuration options 34 | const config = { 35 | ...defaults, 36 | ...userConfig, 37 | }; 38 | const { invoices, server, grpcLoader, grpc, includeDefaults } = config; 39 | 40 | // Generate grpc SSL credentials 41 | const credentials = await createCredentials(config); 42 | 43 | // Create RPC from proto and return GRPC 44 | const grpcPkgObj = createGrpcObject({ 45 | includeDefaults, 46 | protoFilePath, 47 | grpcLoader, 48 | grpc, 49 | includeDirs: [lndProtosRootPath], 50 | }); 51 | 52 | /** 53 | * Invoicesrpc instance 54 | */ 55 | const invoicesrpc = Object.create(null, { 56 | description: {value: grpcPkgObj}, 57 | invoices: { 58 | value: 59 | invoices || createInvoices({ 60 | grpcPkgObj, 61 | server, 62 | credentials, 63 | }), 64 | }, 65 | }); 66 | 67 | return new Proxy(invoicesrpc, { 68 | /** 69 | * Provide lop-level access to any invoices 70 | * methods, otherwise provide user with fallback value 71 | * @param target 72 | * @param key 73 | */ 74 | get(target: any, key: string): any { 75 | if (typeof target.invoices[key] === 'function') { 76 | return target.invoices[key].bind(target.invoices); 77 | } else { 78 | return target[key]; // forward 79 | } 80 | }, 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /src/types/rpc/wallet-unlocker-rpc.ts: -------------------------------------------------------------------------------- 1 | import { ChanBackupSnapshot } from './ln-rpc'; 2 | 3 | export interface GenSeedRequest { 4 | aezeedPassphrase?: Buffer | string; 5 | seedEntropy?: Buffer | string; 6 | } 7 | 8 | export interface GenSeedResponse { 9 | cipherSeedMnemonic: string[]; 10 | encipheredSeed: Buffer | string; 11 | } 12 | 13 | export interface InitWalletRequest { 14 | walletPassword: Buffer | string; 15 | cipherSeedMnemonic: string[]; 16 | aezeedPassphrase?: Buffer | string; 17 | recoveryWindow?: number; 18 | channelBackups?: ChanBackupSnapshot; 19 | } 20 | 21 | export interface UnlockWalletRequest { 22 | walletPassword: Buffer | string; 23 | recoveryWindow?: number; 24 | channelBackups?: ChanBackupSnapshot; 25 | } 26 | 27 | export interface ChangePasswordRequest { 28 | currentPassword: Buffer | string; 29 | newPassword: Buffer | string; 30 | } 31 | 32 | /** 33 | * LND WalletUnlocker gRPC API Client 34 | */ 35 | export interface WalletUnlockerRpc { 36 | /** 37 | * genSeed is the first method that should be used to instantiate a new lnd instance. This method allows a caller 38 | * to generate a new aezeed cipher seed given an optional passphrase. If provided, the passphrase will be 39 | * necessary to decrypt the cipherseed to expose the internal wallet seed. Once the cipherseed is obtained and 40 | * verified by the user, the initWallet method should be used to commit the newly generated seed, and create the 41 | * wallet. 42 | */ 43 | genSeed(args: GenSeedRequest): Promise; 44 | 45 | /** 46 | * 47 | * initWallet is used when lnd is starting up for the first time to fully initialize the daemon and its internal 48 | * wallet. At the very least a wallet password must be provided. This will be used to encrypt sensitive material 49 | * on disk. In the case of a recovery scenario, the user can also specify their aezeed mnemonic and passphrase. 50 | * If set, then the daemon will use this prior state to initialize its internal wallet. Alternatively, this can 51 | * be used along with the genSeed RPC to obtain a seed, then present it to the user. Once it has been verified by 52 | * the user, the seed can be fed into this RPC in order to commit the new wallet. 53 | */ 54 | initWallet(args: InitWalletRequest): Promise<{}>; 55 | 56 | /** 57 | * unlockWallet is used at startup of lnd to provide a password to unlock the wallet database. 58 | */ 59 | unlockWallet(args: UnlockWalletRequest): Promise<{}>; 60 | 61 | /** 62 | * changePassword changes the password of the encrypted wallet. This will automatically unlock the wallet 63 | * database if successful. 64 | */ 65 | changePassword(args: ChangePasswordRequest): Promise<{}>; 66 | } 67 | -------------------------------------------------------------------------------- /src/types/rpc/chain-rpc.ts: -------------------------------------------------------------------------------- 1 | import { Readable } from '../streams'; 2 | 3 | export interface ConfRequest { 4 | txid?: Buffer | string; 5 | script?: Buffer | string; 6 | numConfs?: number; 7 | heightHint?: number; 8 | } 9 | 10 | export interface ConfDetails { 11 | rawTx: Buffer | string; 12 | blockHash: Buffer | string; 13 | blockHeight: number; 14 | txIndex: number; 15 | } 16 | 17 | export interface ConfEvent { 18 | conf?: ConfDetails; 19 | reorg?: {}; // Not implemented 20 | } 21 | 22 | export interface Outpoint { 23 | hash: Buffer | string; 24 | index: number; 25 | } 26 | 27 | export interface SpendRequest { 28 | outpoint?: Outpoint; 29 | script?: Buffer | string; 30 | heightHint?: number; 31 | } 32 | 33 | export interface SpendDetails { 34 | spendingOutpoint?: Outpoint; 35 | rawSpendingTx: Buffer | string; 36 | spendingTxHash: Buffer | string; 37 | spendingInputIndex: number; 38 | spendingHeight: number; 39 | } 40 | 41 | export interface SpendEvent { 42 | spend?: SpendDetails; 43 | reorg?: {}; // Not implemented 44 | } 45 | 46 | export interface BlockEpoch { 47 | hash?: Buffer | string; 48 | height?: number; 49 | } 50 | 51 | /** 52 | * LND Chain gRPC API Client 53 | */ 54 | export interface ChainRpc { 55 | /** 56 | * registerConfirmationsNtfn is a synchronous response-streaming RPC that 57 | * registers an intent for a client to be notified once a confirmation request 58 | * has reached its required number of confirmations on-chain. 59 | * A client can specify whether the confirmation request should be for a 60 | * particular transaction by its hash or for an output script by specifying a 61 | * zero hash. 62 | */ 63 | registerConfirmationsNtfn(args: ConfRequest): Readable; 64 | 65 | /** 66 | * registerSpendNtfn is a synchronous response-streaming RPC that registers an 67 | * intent for a client to be notification once a spend request has been spent 68 | * by a transaction that has confirmed on-chain. 69 | * A client can specify whether the spend request should be for a particular 70 | * outpoint or for an output script by specifying a zero outpoint. 71 | */ 72 | registerSpendNtfn(args: SpendRequest): Readable; 73 | 74 | /** 75 | * registerBlockEpochNtfn is a synchronous response-streaming RPC that 76 | * registers an intent for a client to be notified of blocks in the chain. The 77 | * stream will return a hash and height tuple of a block for each new/stale 78 | * block in the chain. It is the client's responsibility to determine whether 79 | * the tuple returned is for a new or stale block in the chain. 80 | * A client can also request a historical backlog of blocks from a particular 81 | * point. This allows clients to be idempotent by ensuring that they do not 82 | * missing processing a single block within the chain. 83 | */ 84 | registerBlockEpochNtfn(args: BlockEpoch): Readable; 85 | } 86 | -------------------------------------------------------------------------------- /src/rpc/ln-rpc.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import pkgDir from 'pkg-dir'; 3 | import packageJson from '../../package.json'; 4 | import { createLightning, createWalletUnlocker } from '../services'; 5 | import { LnRpc, LnRpcClientConfig, WalletUnlockerRpc } from '../types'; 6 | import { createCredentials } from './create-credentials'; 7 | import { createGrpcObject } from './create-grpc-object'; 8 | import { defaults } from './defaults'; 9 | 10 | /** 11 | * Factory for a lnrpc instance & proxy responsible for: 12 | * - Generating a GRPC Descriptor from user's config 13 | * - Instantiating/exposing all GRPC Services 14 | * - Resolving a proxy that: 15 | * 1. Invokes all top-level method calls to the lightning 16 | * proxy for user convience 17 | * 2. Allow basic user property requests to all GRPC Services 18 | * 19 | * @param userConfig The user provided configuration details 20 | * @return Returns proxy to lnrpc instance 21 | */ 22 | export async function createLnRpc( 23 | userConfig: LnRpcClientConfig, 24 | ): Promise { 25 | const rootPath = await pkgDir(__dirname); 26 | const lightningProtoFilePath = join( 27 | rootPath, 28 | `lnd/${packageJson.config['lnd-release-tag']}/rpc.proto`, 29 | ); 30 | const walletUnlockerProtoFilePath = join( 31 | rootPath, 32 | `lnd/${packageJson.config['lnd-release-tag']}/walletunlocker.proto`, 33 | ); 34 | 35 | // Configuration options 36 | const config = { 37 | ...defaults, 38 | ...userConfig, 39 | }; 40 | const { lightning, walletUnlocker, server, grpcLoader, grpc, includeDefaults } = config; 41 | 42 | // Generate grpc SSL credentials 43 | const credentials = await createCredentials(config); 44 | 45 | // Create RPC from proto and return GRPC 46 | const lightningGrpcPkgObj = createGrpcObject({ 47 | includeDefaults, 48 | grpcLoader, 49 | grpc, 50 | protoFilePath: lightningProtoFilePath, 51 | }); 52 | const walletUnlockerGrpcPkgObj = createGrpcObject({ 53 | includeDefaults, 54 | grpcLoader, 55 | grpc, 56 | protoFilePath: walletUnlockerProtoFilePath, 57 | }); 58 | 59 | /** 60 | * Lnrpc instance 61 | * @type {lnrpc} 62 | */ 63 | const lnrpc = Object.create(null, { 64 | description: {value: walletUnlockerGrpcPkgObj}, // walletunlocker.proto imports rpc.proto 65 | lightning: { 66 | value: 67 | lightning || createLightning({ 68 | server, 69 | credentials, 70 | grpcPkgObj: lightningGrpcPkgObj, 71 | }), 72 | }, 73 | walletUnlocker: { 74 | value: 75 | walletUnlocker || createWalletUnlocker({ 76 | server, 77 | credentials, 78 | grpcPkgObj: walletUnlockerGrpcPkgObj, 79 | }), 80 | }, 81 | }); 82 | 83 | return new Proxy(lnrpc, { 84 | /** 85 | * Provide lop-level access to any lightning/walletUnlocker 86 | * methods, otherwise provide user with fallback value 87 | * @param {lnrpc.Lightning} target 88 | * @param {String} key 89 | * @return {Any} 90 | */ 91 | get(target, key) { 92 | if (typeof target.lightning[key] === 'function') { 93 | return target.lightning[key].bind(target.lightning); 94 | } else if (typeof target.walletUnlocker[key] === 'function') { 95 | return target.walletUnlocker[key].bind(target.walletUnlocker); 96 | } else { 97 | return target[key]; // forward 98 | } 99 | }, 100 | }); 101 | } 102 | -------------------------------------------------------------------------------- /src/types/streams/readable.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from 'stream'; 2 | 3 | export declare class Readable extends Stream implements NodeJS.ReadableStream { 4 | public readable: boolean; 5 | public readonly readableHighWaterMark: number; 6 | public readonly readableLength: number; 7 | constructor(opts?: ReadableOptions); 8 | public _read(size: number): void; 9 | public read(size?: number): string | Buffer; 10 | public setEncoding(encoding: string): this; 11 | public pause(): this; 12 | public resume(): this; 13 | public isPaused(): boolean; 14 | public unpipe(destination?: T): this; 15 | public unshift(chunk: string | Buffer): void; 16 | public wrap(oldStream: NodeJS.ReadableStream): this; 17 | public push(chunk: RES, encoding?: string): boolean; 18 | public _destroy(error: Error | null, callback: (error: Error | null) => void): void; 19 | public destroy(error?: Error): void; 20 | 21 | /** 22 | * Event emitter 23 | * The defined events on documents including: 24 | * 1. close 25 | * 2. data 26 | * 3. end 27 | * 4. readable 28 | * 5. error 29 | */ 30 | public addListener(event: 'close' | 'end' | 'readable', listener: () => void): this; 31 | public addListener(event: 'data', listener: (chunk: RES) => void): this; 32 | public addListener(event: 'error', listener: (err: Error) => void): this; 33 | public addListener(event: string | symbol, listener: (...args: any[]) => void): this; 34 | 35 | public emit(event: 'close' | 'end' | 'readable'): boolean; 36 | public emit(event: 'data', chunk: RES): boolean; 37 | public emit(event: 'error', err: Error): boolean; 38 | public emit(event: string | symbol, ...args: any[]): boolean; 39 | 40 | public on(event: 'close' | 'end' | 'readable', listener: () => void): this; 41 | public on(event: 'data', listener: (chunk: RES) => void): this; 42 | public on(event: 'error', listener: (err: Error) => void): this; 43 | public on(event: string | symbol, listener: (...args: any[]) => void): this; 44 | 45 | public once(event: 'close' | 'end' | 'readable', listener: () => void): this; 46 | public once(event: 'data', listener: (chunk: RES) => void): this; 47 | public once(event: 'error', listener: (err: Error) => void): this; 48 | public once(event: string | symbol, listener: (...args: any[]) => void): this; 49 | 50 | public prependListener(event: 'close' | 'end' | 'readable', listener: () => void): this; 51 | public prependListener(event: 'data', listener: (chunk: RES) => void): this; 52 | public prependListener(event: 'error', listener: (err: Error) => void): this; 53 | public prependListener(event: string | symbol, listener: (...args: any[]) => void): this; 54 | 55 | public prependOnceListener(event: 'close' | 'end' | 'readable', listener: () => void): this; 56 | public prependOnceListener(event: 'data', listener: (chunk: RES) => void): this; 57 | public prependOnceListener(event: 'error', listener: (err: Error) => void): this; 58 | public prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; 59 | 60 | public removeListener(event: 'close' | 'end' | 'readable', listener: () => void): this; 61 | public removeListener(event: 'data', listener: (chunk: RES) => void): this; 62 | public removeListener(event: 'error', listener: (err: Error) => void): this; 63 | public removeListener(event: string | symbol, listener: (...args: any[]) => void): this; 64 | 65 | public [Symbol.asyncIterator](): AsyncIterableIterator; 66 | } 67 | 68 | export interface ReadableOptions { 69 | highWaterMark?: number; 70 | encoding?: string; 71 | objectMode?: boolean; 72 | read?(this: Readable, size: number): void; 73 | destroy?(this: Readable, error: Error | null, callback: (error: Error | null) => void): void; 74 | } 75 | -------------------------------------------------------------------------------- /test/helpers/grpc-stub.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Lightning RPC Stub 3 | * @constructor 4 | */ 5 | export const LightningStub = () => { /* noop */ }; 6 | 7 | /** 8 | * Wallet Unlocker RPC Stub 9 | * @constructor 10 | */ 11 | export const WalletUnlockerStub = () => { /* noop */ }; 12 | 13 | /** 14 | * Autopilot RPC Stub 15 | * @constructor 16 | */ 17 | export const AutopilotStub = () => { /* noop */ }; 18 | 19 | /** 20 | * Chain Notifier RPC Stub 21 | * @constructor 22 | */ 23 | export const ChainNotifierStub = () => { /* noop */ }; 24 | 25 | /** 26 | * Invoices RPC Stub 27 | * @constructor 28 | */ 29 | export const InvoicesStub = () => { /* noop */ }; 30 | 31 | /** 32 | * Router RPC Stub 33 | * @constructor 34 | */ 35 | export const RouterStub = () => { /* noop */ }; 36 | 37 | /** 38 | * Signer RPC Stub 39 | * @constructor 40 | */ 41 | export const SignerStub = () => { /* noop */ }; 42 | 43 | /** 44 | * Wallet Kit RPC Stub 45 | * @constructor 46 | */ 47 | export const WalletKitStub = () => { /* noop */ }; 48 | 49 | /** 50 | * Watchtower RPC Stub 51 | * @constructor 52 | */ 53 | export const WatchtowerStub = () => { /* noop */ }; 54 | 55 | /** 56 | * Watchtower Client RPC Stub 57 | * @constructor 58 | */ 59 | export const WatchtowerClientStub = () => { /* noop */ }; 60 | 61 | /** 62 | * Metadata 63 | * @constructor 64 | */ 65 | function Metadata() { /* noop */ } 66 | Metadata.prototype.add = () => { /* noop */ }; 67 | 68 | /** 69 | * Create a grpc stub 70 | * @param {Object?} options 71 | * @param {LightningStub?} lightning 72 | * @param {WalletUnlockerStub?} walletUnlocker 73 | * @param {AutopilotStub?} autopilot 74 | * @param {ChainNotifierStub?} chainNotifier 75 | * @param {InvoicesStub?} invoices 76 | * @param {RouterStub?} router 77 | * @param {SignerStub?} signer 78 | * @param {WalletKitStub?} walletKit 79 | * @param {WatchtowerStub?} watchtower 80 | * @param {WatchtowerClientStub?} watchtowerClient 81 | * @return {Object} 82 | */ 83 | export function grpcStub( 84 | options = {}, 85 | lightning: (value?: unknown) => void = LightningStub, 86 | walletUnlocker: (value?: unknown) => void = WalletUnlockerStub, 87 | autopilot: (value?: unknown) => void = AutopilotStub, 88 | chainNotifier: (value?: unknown) => void = ChainNotifierStub, 89 | invoices: (value?: unknown) => void = InvoicesStub, 90 | router: (value?: unknown) => void = RouterStub, 91 | signer: (value?: unknown) => void = SignerStub, 92 | walletKit: (value?: unknown) => void = WalletKitStub, 93 | watchtower: (value?: unknown) => void = WatchtowerStub, 94 | watchtowerClient: (value?: unknown) => void = WatchtowerClientStub, 95 | ) { 96 | return Object.assign( 97 | { 98 | Metadata, 99 | credentials: { 100 | createSsl: () => ({}), 101 | createFromMetadataGenerator: (cb) => { 102 | cb({}, () => { /* noop */ }); 103 | return {}; 104 | }, 105 | combineChannelCredentials: () => ({}), 106 | }, 107 | loadPackageDefinition: () => ({ 108 | lnrpc: { 109 | Lightning: lightning, 110 | WalletUnlocker: walletUnlocker, 111 | }, 112 | autopilotrpc: { 113 | Autopilot: autopilot, 114 | }, 115 | chainrpc: { 116 | ChainNotifier: chainNotifier, 117 | }, 118 | invoicesrpc: { 119 | Invoices: invoices, 120 | }, 121 | routerrpc: { 122 | Router: router, 123 | }, 124 | signrpc: { 125 | Signer: signer, 126 | }, 127 | walletrpc: { 128 | WalletKit: walletKit, 129 | }, 130 | watchtowerrpc: { 131 | Watchtower: watchtower, 132 | }, 133 | wtclientrpc: { 134 | WatchtowerClient: watchtowerClient, 135 | }, 136 | }), 137 | }, 138 | options, 139 | ) as any; 140 | } 141 | -------------------------------------------------------------------------------- /lnd/v0.11.1-beta/invoicesrpc/invoices.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "rpc.proto"; 4 | 5 | package invoicesrpc; 6 | 7 | option go_package = "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"; 8 | 9 | // Invoices is a service that can be used to create, accept, settle and cancel 10 | // invoices. 11 | service Invoices { 12 | /* 13 | SubscribeSingleInvoice returns a uni-directional stream (server -> client) 14 | to notify the client of state transitions of the specified invoice. 15 | Initially the current invoice state is always sent out. 16 | */ 17 | rpc SubscribeSingleInvoice (SubscribeSingleInvoiceRequest) 18 | returns (stream lnrpc.Invoice); 19 | 20 | /* 21 | CancelInvoice cancels a currently open invoice. If the invoice is already 22 | canceled, this call will succeed. If the invoice is already settled, it will 23 | fail. 24 | */ 25 | rpc CancelInvoice (CancelInvoiceMsg) returns (CancelInvoiceResp); 26 | 27 | /* 28 | AddHoldInvoice creates a hold invoice. It ties the invoice to the hash 29 | supplied in the request. 30 | */ 31 | rpc AddHoldInvoice (AddHoldInvoiceRequest) returns (AddHoldInvoiceResp); 32 | 33 | /* 34 | SettleInvoice settles an accepted invoice. If the invoice is already 35 | settled, this call will succeed. 36 | */ 37 | rpc SettleInvoice (SettleInvoiceMsg) returns (SettleInvoiceResp); 38 | } 39 | 40 | message CancelInvoiceMsg { 41 | // Hash corresponding to the (hold) invoice to cancel. 42 | bytes payment_hash = 1; 43 | } 44 | message CancelInvoiceResp { 45 | } 46 | 47 | message AddHoldInvoiceRequest { 48 | /* 49 | An optional memo to attach along with the invoice. Used for record keeping 50 | purposes for the invoice's creator, and will also be set in the description 51 | field of the encoded payment request if the description_hash field is not 52 | being used. 53 | */ 54 | string memo = 1; 55 | 56 | // The hash of the preimage 57 | bytes hash = 2; 58 | 59 | /* 60 | The value of this invoice in satoshis 61 | 62 | The fields value and value_msat are mutually exclusive. 63 | */ 64 | int64 value = 3; 65 | 66 | /* 67 | The value of this invoice in millisatoshis 68 | 69 | The fields value and value_msat are mutually exclusive. 70 | */ 71 | int64 value_msat = 10; 72 | 73 | /* 74 | Hash (SHA-256) of a description of the payment. Used if the description of 75 | payment (memo) is too long to naturally fit within the description field 76 | of an encoded payment request. 77 | */ 78 | bytes description_hash = 4; 79 | 80 | // Payment request expiry time in seconds. Default is 3600 (1 hour). 81 | int64 expiry = 5; 82 | 83 | // Fallback on-chain address. 84 | string fallback_addr = 6; 85 | 86 | // Delta to use for the time-lock of the CLTV extended to the final hop. 87 | uint64 cltv_expiry = 7; 88 | 89 | /* 90 | Route hints that can each be individually used to assist in reaching the 91 | invoice's destination. 92 | */ 93 | repeated lnrpc.RouteHint route_hints = 8; 94 | 95 | // Whether this invoice should include routing hints for private channels. 96 | bool private = 9; 97 | } 98 | 99 | message AddHoldInvoiceResp { 100 | /* 101 | A bare-bones invoice for a payment within the Lightning Network. With the 102 | details of the invoice, the sender has all the data necessary to send a 103 | payment to the recipient. 104 | */ 105 | string payment_request = 1; 106 | } 107 | 108 | message SettleInvoiceMsg { 109 | // Externally discovered pre-image that should be used to settle the hold 110 | // invoice. 111 | bytes preimage = 1; 112 | } 113 | 114 | message SettleInvoiceResp { 115 | } 116 | 117 | message SubscribeSingleInvoiceRequest { 118 | reserved 1; 119 | 120 | // Hash corresponding to the (hold) invoice to subscribe to. 121 | bytes r_hash = 2; 122 | } 123 | -------------------------------------------------------------------------------- /test/services/wallet-unlocker.test.ts: -------------------------------------------------------------------------------- 1 | import { ChannelCredentials } from '@grpc/grpc-js'; 2 | import assert, { equal } from 'assert'; 3 | import { createWalletUnlocker } from '../../src/services'; 4 | import { grpcStub, LightningStub } from '../helpers/grpc-stub'; 5 | 6 | const { stringify } = JSON; 7 | 8 | describe('Wallet Unlocker Service', () => { 9 | const credentials = {} as ChannelCredentials; 10 | 11 | it('should not modify arguments', () => { 12 | const descriptor = grpcStub().loadPackageDefinition(); 13 | const expDescriptor = stringify(descriptor); 14 | const server = 'localhost:10003'; 15 | const expServer = `${server}`; 16 | const expCredentials = stringify(credentials); 17 | 18 | createWalletUnlocker({ 19 | grpcPkgObj: descriptor, 20 | server, 21 | credentials, 22 | }); 23 | 24 | equal(stringify(descriptor), expDescriptor, 'has expected descriptor'); 25 | equal(server, expServer, 'has expected server'); 26 | equal(stringify(credentials), expCredentials, 'has expected credentials'); 27 | }); 28 | 29 | it('should throw when unable to create wallet unlocker service', () => { 30 | let expectedErr; 31 | 32 | try { 33 | /** 34 | * Custom WalletUnlockerStub 35 | * @constructor 36 | */ 37 | const WalletUnlockerCustomStub = () => { 38 | throw new Error(); 39 | }; 40 | 41 | const descriptor = grpcStub( 42 | {}, 43 | LightningStub, 44 | WalletUnlockerCustomStub, 45 | ).loadPackageDefinition(); 46 | 47 | assert.throws( 48 | () => createWalletUnlocker({ 49 | grpcPkgObj: descriptor, 50 | server: 'localhost:1', 51 | credentials, 52 | }), 53 | (e) => { 54 | expectedErr = e; 55 | return true; 56 | }, 57 | ); 58 | } catch (_) { 59 | // noop 60 | } 61 | 62 | return new Promise((resolve) => { 63 | equal( 64 | expectedErr?.code, 65 | 'GRPC_WALLET_UNLOCKER_SERVICE_ERR', 66 | 'has expected error', 67 | ); 68 | resolve(true); 69 | }); 70 | }); 71 | 72 | it('should allow getting on proxy target', () => { 73 | const expected = 'test'; 74 | 75 | /** 76 | * Custom WalletUnlockerStub 77 | * @constructor 78 | */ 79 | function WalletUnlockerCustomStub() { 80 | this.name = expected; 81 | } 82 | 83 | const descriptor = grpcStub( 84 | {}, 85 | LightningStub, 86 | WalletUnlockerCustomStub, 87 | ).loadPackageDefinition(); 88 | const instance = createWalletUnlocker({ 89 | grpcPkgObj: descriptor, 90 | server: 'localhost:1', 91 | credentials, 92 | }); 93 | equal(instance.name, expected, 'proxy forwards to target props'); 94 | }); 95 | 96 | it('should allow setting on proxy target', () => { 97 | const expected = 'test'; 98 | const descriptor = grpcStub().loadPackageDefinition(); 99 | const instance = createWalletUnlocker({ 100 | grpcPkgObj: descriptor, 101 | server: 'localhost:1', 102 | credentials, 103 | }); 104 | 105 | instance.name = expected; 106 | equal(instance.name, expected, 'proxy sets target properties'); 107 | }); 108 | 109 | it('should provide promisified target methods', async () => { 110 | const expected = 'test'; 111 | 112 | /** 113 | * Custom WalletUnlockerStub 114 | * @constructor 115 | */ 116 | function WalletUnlockerCustomStub() { 117 | // noop 118 | } 119 | WalletUnlockerCustomStub.prototype.initWallet = (_, cb) => { 120 | cb(null, expected); 121 | }; 122 | const descriptor = grpcStub( 123 | {}, 124 | LightningStub, 125 | WalletUnlockerCustomStub, 126 | ).loadPackageDefinition(); 127 | const instance = createWalletUnlocker({ 128 | grpcPkgObj: descriptor, 129 | server: 'localhost:1', 130 | credentials, 131 | }); 132 | 133 | const actual = await instance.initWallet({}); 134 | equal(actual, expected, 'promisified `initWallet` target method'); 135 | }); 136 | }); 137 | -------------------------------------------------------------------------------- /src/types/rpc/sign-rpc.ts: -------------------------------------------------------------------------------- 1 | export interface KeyLocator { 2 | keyFamily: number; 3 | keyIndex: number; 4 | } 5 | 6 | export interface KeyDescriptor { 7 | rawKeyBytes: Buffer | string; 8 | keyLoc?: KeyLocator; 9 | } 10 | 11 | export interface TxOut { 12 | value: number; 13 | pkScript: Buffer | string; 14 | } 15 | 16 | export interface SignDescriptor { 17 | keyDesc?: KeyDescriptor; 18 | singleTweak?: Buffer | string; 19 | doubleTweak?: Buffer | string; 20 | witnessScript?: Buffer | string; 21 | output?: TxOut; 22 | sighash?: number; 23 | inputIndex?: number; 24 | } 25 | 26 | export interface SignReq { 27 | rawTxBytes?: Buffer | string; 28 | signDescs?: SignDescriptor[]; 29 | } 30 | 31 | export interface SignResp { 32 | rawSigs: Array; 33 | } 34 | 35 | export interface InputScript { 36 | witness: Array; 37 | sigScript: Buffer | string; 38 | } 39 | 40 | export interface InputScriptResp { 41 | inputScripts: InputScript[]; 42 | } 43 | 44 | export interface SignMessageReq { 45 | msg: Buffer | string; 46 | keyLoc?: KeyLocator; 47 | } 48 | 49 | export interface SignMessageResp { 50 | signature: Buffer | string; 51 | } 52 | 53 | export interface VerifyMessageReq { 54 | msg: Buffer | string; 55 | signature: Buffer | string; 56 | pubkey: Buffer | string; 57 | } 58 | 59 | export interface VerifyMessageResp { 60 | valid: boolean; 61 | } 62 | 63 | export interface SharedKeyRequest { 64 | ephemeralPubkey: Buffer | string; 65 | keyLoc?: KeyLocator; 66 | } 67 | 68 | export interface SharedKeyResponse { 69 | sharedKey: Buffer | string; 70 | } 71 | 72 | /** 73 | * LND Sign gRPC API Client 74 | */ 75 | export interface SignRpc { 76 | /** 77 | * signOutputRaw is a method that can be used to generated a signature for a 78 | * set of inputs/outputs to a transaction. Each request specifies details 79 | * concerning how the outputs should be signed, which keys they should be 80 | * signed with, and also any optional tweaks. The return value is a fixed 81 | * 64-byte signature (the same format as we use on the wire in Lightning). 82 | * If we are unable to sign using the specified keys, then an error will be 83 | * returned. 84 | */ 85 | signOutputRaw(args: SignReq): Promise; 86 | 87 | /** 88 | * computeInputScript generates a complete InputIndex for the passed 89 | * transaction with the signature as defined within the passed SignDescriptor. 90 | * This method should be capable of generating the proper input script for 91 | * both regular p2wkh output and p2wkh outputs nested within a regular p2sh 92 | * output. 93 | * Note that when using this method to sign inputs belonging to the wallet, 94 | * the only items of the SignDescriptor that need to be populated are pkScript 95 | * in the TxOut field, the value in that same field, and finally the input 96 | * index. 97 | */ 98 | computeInputScript(args: SignReq): Promise; 99 | 100 | /** 101 | * SignMessage signs a message with the key specified in the key locator. The 102 | * returned signature is fixed-size LN wire format encoded. 103 | * 104 | * The main difference to SignMessage in the main RPC is that a specific key is 105 | * used to sign the message instead of the node identity private key. 106 | */ 107 | signMessage(args: SignMessageReq): Promise; 108 | 109 | /** 110 | * VerifyMessage verifies a signature over a message using the public key 111 | * provided. The signature must be fixed-size LN wire format encoded. 112 | * 113 | * The main difference to VerifyMessage in the main RPC is that the public key 114 | * used to sign the message does not have to be a node known to the network. 115 | */ 116 | verifyMessage(args: VerifyMessageReq): Promise; 117 | 118 | /** 119 | * DeriveSharedKey returns a shared secret key by performing Diffie-Hellman key 120 | * derivation between the ephemeral public key in the request and the node's 121 | * key specified in the key_loc parameter (or the node's identity private key 122 | * if no key locator is specified): 123 | * P_shared = privKeyNode * ephemeralPubkey 124 | * The resulting shared public key is serialized in the compressed format and 125 | * hashed with sha256, resulting in the final key length of 256bit. 126 | */ 127 | deriveSharedKey(args: SharedKeyRequest): Promise; 128 | } 129 | -------------------------------------------------------------------------------- /src/types/generated/google/api/http_pb.d.ts: -------------------------------------------------------------------------------- 1 | // package: google.api 2 | // file: google/api/http.proto 3 | 4 | import * as jspb from "google-protobuf"; 5 | 6 | export class Http extends jspb.Message { 7 | clearRulesList(): void; 8 | getRulesList(): Array; 9 | setRulesList(value: Array): void; 10 | addRules(value?: HttpRule, index?: number): HttpRule; 11 | 12 | getFullyDecodeReservedExpansion(): boolean; 13 | setFullyDecodeReservedExpansion(value: boolean): void; 14 | 15 | serializeBinary(): Uint8Array; 16 | toObject(includeInstance?: boolean): Http.AsObject; 17 | static toObject(includeInstance: boolean, msg: Http): Http.AsObject; 18 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 19 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 20 | static serializeBinaryToWriter(message: Http, writer: jspb.BinaryWriter): void; 21 | static deserializeBinary(bytes: Uint8Array): Http; 22 | static deserializeBinaryFromReader(message: Http, reader: jspb.BinaryReader): Http; 23 | } 24 | 25 | export namespace Http { 26 | export type AsObject = { 27 | rules: Array, 28 | fullyDecodeReservedExpansion: boolean, 29 | } 30 | } 31 | 32 | export class HttpRule extends jspb.Message { 33 | getSelector(): string; 34 | setSelector(value: string): void; 35 | 36 | hasGet(): boolean; 37 | clearGet(): void; 38 | getGet(): string; 39 | setGet(value: string): void; 40 | 41 | hasPut(): boolean; 42 | clearPut(): void; 43 | getPut(): string; 44 | setPut(value: string): void; 45 | 46 | hasPost(): boolean; 47 | clearPost(): void; 48 | getPost(): string; 49 | setPost(value: string): void; 50 | 51 | hasDelete(): boolean; 52 | clearDelete(): void; 53 | getDelete(): string; 54 | setDelete(value: string): void; 55 | 56 | hasPatch(): boolean; 57 | clearPatch(): void; 58 | getPatch(): string; 59 | setPatch(value: string): void; 60 | 61 | hasCustom(): boolean; 62 | clearCustom(): void; 63 | getCustom(): CustomHttpPattern | undefined; 64 | setCustom(value?: CustomHttpPattern): void; 65 | 66 | getBody(): string; 67 | setBody(value: string): void; 68 | 69 | getResponseBody(): string; 70 | setResponseBody(value: string): void; 71 | 72 | clearAdditionalBindingsList(): void; 73 | getAdditionalBindingsList(): Array; 74 | setAdditionalBindingsList(value: Array): void; 75 | addAdditionalBindings(value?: HttpRule, index?: number): HttpRule; 76 | 77 | getPatternCase(): HttpRule.PatternCase; 78 | serializeBinary(): Uint8Array; 79 | toObject(includeInstance?: boolean): HttpRule.AsObject; 80 | static toObject(includeInstance: boolean, msg: HttpRule): HttpRule.AsObject; 81 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 82 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 83 | static serializeBinaryToWriter(message: HttpRule, writer: jspb.BinaryWriter): void; 84 | static deserializeBinary(bytes: Uint8Array): HttpRule; 85 | static deserializeBinaryFromReader(message: HttpRule, reader: jspb.BinaryReader): HttpRule; 86 | } 87 | 88 | export namespace HttpRule { 89 | export type AsObject = { 90 | selector: string, 91 | get: string, 92 | put: string, 93 | post: string, 94 | pb_delete: string, 95 | patch: string, 96 | custom?: CustomHttpPattern.AsObject, 97 | body: string, 98 | responseBody: string, 99 | additionalBindings: Array, 100 | } 101 | 102 | export enum PatternCase { 103 | PATTERN_NOT_SET = 0, 104 | GET = 2, 105 | PUT = 3, 106 | POST = 4, 107 | DELETE = 5, 108 | PATCH = 6, 109 | CUSTOM = 8, 110 | } 111 | } 112 | 113 | export class CustomHttpPattern extends jspb.Message { 114 | getKind(): string; 115 | setKind(value: string): void; 116 | 117 | getPath(): string; 118 | setPath(value: string): void; 119 | 120 | serializeBinary(): Uint8Array; 121 | toObject(includeInstance?: boolean): CustomHttpPattern.AsObject; 122 | static toObject(includeInstance: boolean, msg: CustomHttpPattern): CustomHttpPattern.AsObject; 123 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 124 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 125 | static serializeBinaryToWriter(message: CustomHttpPattern, writer: jspb.BinaryWriter): void; 126 | static deserializeBinary(bytes: Uint8Array): CustomHttpPattern; 127 | static deserializeBinaryFromReader(message: CustomHttpPattern, reader: jspb.BinaryReader): CustomHttpPattern; 128 | } 129 | 130 | export namespace CustomHttpPattern { 131 | export type AsObject = { 132 | kind: string, 133 | path: string, 134 | } 135 | } 136 | 137 | -------------------------------------------------------------------------------- /test/services/lightning.test.ts: -------------------------------------------------------------------------------- 1 | import { ChannelCredentials } from '@grpc/grpc-js'; 2 | import assert from 'assert'; 3 | import { createLightning } from '../../src/services'; 4 | import { grpcStub } from '../helpers/grpc-stub'; 5 | const {equal} = assert; 6 | 7 | const {stringify} = JSON; 8 | 9 | describe('Lightning Service', () => { 10 | const credentials = {} as ChannelCredentials; 11 | 12 | it('should not modify arguments', () => { 13 | const descriptor = grpcStub().loadPackageDefinition(); 14 | const expDescriptor = stringify(descriptor); 15 | const server = 'localhost:10003'; 16 | const expServer = `${server}`; 17 | const expCredentials = stringify(credentials); 18 | const config = {subscriptionMethods: ['subscribeInvoices']}; 19 | const expConfig = stringify(config); 20 | 21 | createLightning({ 22 | grpcPkgObj: descriptor, 23 | server, 24 | credentials, 25 | }); 26 | 27 | equal(stringify(descriptor), expDescriptor, 'has expected descriptor'); 28 | equal(server, expServer, 'has expected server'); 29 | equal(stringify(credentials), expCredentials, 'has expected credentials'); 30 | equal(stringify(config), expConfig, 'has expected config'); 31 | }); 32 | 33 | it('should throw when unable to create lightning service', () => { 34 | let expectedErr; 35 | 36 | try { 37 | /** 38 | * Custom LightningStub 39 | * @constructor 40 | */ 41 | const LightningCustomStub = () => { 42 | throw new Error(); 43 | }; 44 | 45 | const descriptor = grpcStub( 46 | {}, 47 | LightningCustomStub, 48 | ).loadPackageDefinition(); 49 | assert.throws( 50 | () => createLightning({ 51 | grpcPkgObj: descriptor, 52 | server: 'localhost:1', 53 | credentials, 54 | }), 55 | (e) => { 56 | expectedErr = e; 57 | return true; 58 | }, 59 | ); 60 | } catch (_) { /* noop */ } 61 | 62 | return new Promise((resolve) => { 63 | equal( 64 | expectedErr?.code, 65 | 'GRPC_LIGHTNING_SERVICE_ERR', 66 | 'has expected error', 67 | ); 68 | resolve(true); 69 | }); 70 | }); 71 | 72 | it('should allow getting on proxy target', () => { 73 | const expected = 'test'; 74 | 75 | /** 76 | * Custom LightningStub 77 | * @constructor 78 | */ 79 | function LightningCustomStub() { 80 | this.name = expected; 81 | } 82 | 83 | const descriptor = grpcStub( 84 | {}, 85 | LightningCustomStub, 86 | ).loadPackageDefinition(); 87 | const instance = createLightning({ 88 | grpcPkgObj: descriptor, 89 | server: 'localhost:1', 90 | credentials, 91 | }); 92 | equal(instance.name, expected, 'proxy forwards to target props'); 93 | }); 94 | 95 | it('should allow setting on proxy target', () => { 96 | const expected = 'test'; 97 | const descriptor = grpcStub().loadPackageDefinition(); 98 | const instance = createLightning({ 99 | grpcPkgObj: descriptor, 100 | server: 'localhost:1', 101 | credentials, 102 | }); 103 | 104 | instance.name = expected; 105 | equal(instance.name, expected, 'proxy sets target properties'); 106 | }); 107 | 108 | it('should provide promisified target methods', async () => { 109 | const expected = 'test'; 110 | 111 | /** 112 | * Custom LightningStub 113 | * @constructor 114 | */ 115 | function LightningCustomStub() { /* noop */ } 116 | LightningCustomStub.prototype.getInfo = (_, cb) => { 117 | cb(null, expected); 118 | }; 119 | const descriptor = grpcStub( 120 | {}, 121 | LightningCustomStub, 122 | ).loadPackageDefinition(); 123 | const instance = createLightning({ 124 | grpcPkgObj: descriptor, 125 | server: 'localhost:1', 126 | credentials, 127 | }); 128 | 129 | const actual = await instance.getInfo({}); 130 | equal(actual, expected, 'promisified `getInfo` target method'); 131 | }); 132 | 133 | it('should forward streaming methods unmodified', () => { 134 | // RPC streaming response interface 135 | const expected = { 136 | on() { /* noop */ }, 137 | write() { /* noop */ }, 138 | }; 139 | 140 | // Subscription methods 141 | const expSubscriptionMethods = ['openChannel', 'closeChannel']; 142 | 143 | /** 144 | * Custom LightningStub 145 | * @constructor 146 | */ 147 | function LightningCustomStub() { /* noop */ } 148 | LightningCustomStub.prototype.openChannel = () => expected; 149 | LightningCustomStub.prototype.closeChannel = () => expected; 150 | 151 | const descriptor = grpcStub( 152 | {}, 153 | LightningCustomStub, 154 | ).loadPackageDefinition(); 155 | const instance = createLightning({ 156 | grpcPkgObj: descriptor, 157 | server: 'localhost:1', 158 | credentials, 159 | }); 160 | 161 | expSubscriptionMethods.forEach((method) => { 162 | equal(instance[method](), expected, 'forwards original method'); 163 | }); 164 | }); 165 | }); 166 | -------------------------------------------------------------------------------- /lnd/v0.11.1-beta/wtclientrpc/wtclient.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package wtclientrpc; 4 | 5 | option go_package = "github.com/lightningnetwork/lnd/lnrpc/wtclientrpc"; 6 | 7 | // WatchtowerClient is a service that grants access to the watchtower client 8 | // functionality of the daemon. 9 | service WatchtowerClient { 10 | /* 11 | AddTower adds a new watchtower reachable at the given address and 12 | considers it for new sessions. If the watchtower already exists, then 13 | any new addresses included will be considered when dialing it for 14 | session negotiations and backups. 15 | */ 16 | rpc AddTower (AddTowerRequest) returns (AddTowerResponse); 17 | 18 | /* 19 | RemoveTower removes a watchtower from being considered for future session 20 | negotiations and from being used for any subsequent backups until it's added 21 | again. If an address is provided, then this RPC only serves as a way of 22 | removing the address from the watchtower instead. 23 | */ 24 | rpc RemoveTower (RemoveTowerRequest) returns (RemoveTowerResponse); 25 | 26 | // ListTowers returns the list of watchtowers registered with the client. 27 | rpc ListTowers (ListTowersRequest) returns (ListTowersResponse); 28 | 29 | // GetTowerInfo retrieves information for a registered watchtower. 30 | rpc GetTowerInfo (GetTowerInfoRequest) returns (Tower); 31 | 32 | // Stats returns the in-memory statistics of the client since startup. 33 | rpc Stats (StatsRequest) returns (StatsResponse); 34 | 35 | // Policy returns the active watchtower client policy configuration. 36 | rpc Policy (PolicyRequest) returns (PolicyResponse); 37 | } 38 | 39 | message AddTowerRequest { 40 | // The identifying public key of the watchtower to add. 41 | bytes pubkey = 1; 42 | 43 | // A network address the watchtower is reachable over. 44 | string address = 2; 45 | } 46 | 47 | message AddTowerResponse { 48 | } 49 | 50 | message RemoveTowerRequest { 51 | // The identifying public key of the watchtower to remove. 52 | bytes pubkey = 1; 53 | 54 | /* 55 | If set, then the record for this address will be removed, indicating that is 56 | is stale. Otherwise, the watchtower will no longer be used for future 57 | session negotiations and backups. 58 | */ 59 | string address = 2; 60 | } 61 | 62 | message RemoveTowerResponse { 63 | } 64 | 65 | message GetTowerInfoRequest { 66 | // The identifying public key of the watchtower to retrieve information for. 67 | bytes pubkey = 1; 68 | 69 | // Whether we should include sessions with the watchtower in the response. 70 | bool include_sessions = 2; 71 | } 72 | 73 | message TowerSession { 74 | /* 75 | The total number of successful backups that have been made to the 76 | watchtower session. 77 | */ 78 | uint32 num_backups = 1; 79 | 80 | /* 81 | The total number of backups in the session that are currently pending to be 82 | acknowledged by the watchtower. 83 | */ 84 | uint32 num_pending_backups = 2; 85 | 86 | // The maximum number of backups allowed by the watchtower session. 87 | uint32 max_backups = 3; 88 | 89 | /* 90 | The fee rate, in satoshis per vbyte, that will be used by the watchtower for 91 | the justice transaction in the event of a channel breach. 92 | */ 93 | uint32 sweep_sat_per_byte = 4; 94 | } 95 | 96 | message Tower { 97 | // The identifying public key of the watchtower. 98 | bytes pubkey = 1; 99 | 100 | // The list of addresses the watchtower is reachable over. 101 | repeated string addresses = 2; 102 | 103 | // Whether the watchtower is currently a candidate for new sessions. 104 | bool active_session_candidate = 3; 105 | 106 | // The number of sessions that have been negotiated with the watchtower. 107 | uint32 num_sessions = 4; 108 | 109 | // The list of sessions that have been negotiated with the watchtower. 110 | repeated TowerSession sessions = 5; 111 | } 112 | 113 | message ListTowersRequest { 114 | // Whether we should include sessions with the watchtower in the response. 115 | bool include_sessions = 1; 116 | } 117 | 118 | message ListTowersResponse { 119 | // The list of watchtowers available for new backups. 120 | repeated Tower towers = 1; 121 | } 122 | 123 | message StatsRequest { 124 | } 125 | 126 | message StatsResponse { 127 | /* 128 | The total number of backups made to all active and exhausted watchtower 129 | sessions. 130 | */ 131 | uint32 num_backups = 1; 132 | 133 | /* 134 | The total number of backups that are pending to be acknowledged by all 135 | active and exhausted watchtower sessions. 136 | */ 137 | uint32 num_pending_backups = 2; 138 | 139 | /* 140 | The total number of backups that all active and exhausted watchtower 141 | sessions have failed to acknowledge. 142 | */ 143 | uint32 num_failed_backups = 3; 144 | 145 | // The total number of new sessions made to watchtowers. 146 | uint32 num_sessions_acquired = 4; 147 | 148 | // The total number of watchtower sessions that have been exhausted. 149 | uint32 num_sessions_exhausted = 5; 150 | } 151 | 152 | message PolicyRequest { 153 | } 154 | 155 | message PolicyResponse { 156 | /* 157 | The maximum number of updates each session we negotiate with watchtowers 158 | should allow. 159 | */ 160 | uint32 max_updates = 1; 161 | 162 | /* 163 | The fee rate, in satoshis per vbyte, that will be used by watchtowers for 164 | justice transactions in response to channel breaches. 165 | */ 166 | uint32 sweep_sat_per_byte = 2; 167 | } 168 | -------------------------------------------------------------------------------- /lnd/v0.11.1-beta/chainrpc/chainnotifier.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package chainrpc; 4 | 5 | // ChainNotifier is a service that can be used to get information about the 6 | // chain backend by registering notifiers for chain events. 7 | service ChainNotifier { 8 | /* 9 | RegisterConfirmationsNtfn is a synchronous response-streaming RPC that 10 | registers an intent for a client to be notified once a confirmation request 11 | has reached its required number of confirmations on-chain. 12 | 13 | A client can specify whether the confirmation request should be for a 14 | particular transaction by its hash or for an output script by specifying a 15 | zero hash. 16 | */ 17 | rpc RegisterConfirmationsNtfn (ConfRequest) returns (stream ConfEvent); 18 | 19 | /* 20 | RegisterSpendNtfn is a synchronous response-streaming RPC that registers an 21 | intent for a client to be notification once a spend request has been spent 22 | by a transaction that has confirmed on-chain. 23 | 24 | A client can specify whether the spend request should be for a particular 25 | outpoint or for an output script by specifying a zero outpoint. 26 | */ 27 | rpc RegisterSpendNtfn (SpendRequest) returns (stream SpendEvent); 28 | 29 | /* 30 | RegisterBlockEpochNtfn is a synchronous response-streaming RPC that 31 | registers an intent for a client to be notified of blocks in the chain. The 32 | stream will return a hash and height tuple of a block for each new/stale 33 | block in the chain. It is the client's responsibility to determine whether 34 | the tuple returned is for a new or stale block in the chain. 35 | 36 | A client can also request a historical backlog of blocks from a particular 37 | point. This allows clients to be idempotent by ensuring that they do not 38 | missing processing a single block within the chain. 39 | */ 40 | rpc RegisterBlockEpochNtfn (BlockEpoch) returns (stream BlockEpoch); 41 | } 42 | 43 | message ConfRequest { 44 | /* 45 | The transaction hash for which we should request a confirmation notification 46 | for. If set to a hash of all zeros, then the confirmation notification will 47 | be requested for the script instead. 48 | */ 49 | bytes txid = 1; 50 | 51 | /* 52 | An output script within a transaction with the hash above which will be used 53 | by light clients to match block filters. If the transaction hash is set to a 54 | hash of all zeros, then a confirmation notification will be requested for 55 | this script instead. 56 | */ 57 | bytes script = 2; 58 | 59 | /* 60 | The number of desired confirmations the transaction/output script should 61 | reach before dispatching a confirmation notification. 62 | */ 63 | uint32 num_confs = 3; 64 | 65 | /* 66 | The earliest height in the chain for which the transaction/output script 67 | could have been included in a block. This should in most cases be set to the 68 | broadcast height of the transaction/output script. 69 | */ 70 | uint32 height_hint = 4; 71 | } 72 | 73 | message ConfDetails { 74 | // The raw bytes of the confirmed transaction. 75 | bytes raw_tx = 1; 76 | 77 | // The hash of the block in which the confirmed transaction was included in. 78 | bytes block_hash = 2; 79 | 80 | // The height of the block in which the confirmed transaction was included 81 | // in. 82 | uint32 block_height = 3; 83 | 84 | // The index of the confirmed transaction within the transaction. 85 | uint32 tx_index = 4; 86 | } 87 | 88 | message Reorg { 89 | // TODO(wilmer): need to know how the client will use this first. 90 | } 91 | 92 | message ConfEvent { 93 | oneof event { 94 | /* 95 | An event that includes the confirmation details of the request 96 | (txid/ouput script). 97 | */ 98 | ConfDetails conf = 1; 99 | 100 | /* 101 | An event send when the transaction of the request is reorged out of the 102 | chain. 103 | */ 104 | Reorg reorg = 2; 105 | } 106 | } 107 | 108 | message Outpoint { 109 | // The hash of the transaction. 110 | bytes hash = 1; 111 | 112 | // The index of the output within the transaction. 113 | uint32 index = 2; 114 | } 115 | 116 | message SpendRequest { 117 | /* 118 | The outpoint for which we should request a spend notification for. If set to 119 | a zero outpoint, then the spend notification will be requested for the 120 | script instead. 121 | */ 122 | Outpoint outpoint = 1; 123 | 124 | /* 125 | The output script for the outpoint above. This will be used by light clients 126 | to match block filters. If the outpoint is set to a zero outpoint, then a 127 | spend notification will be requested for this script instead. 128 | */ 129 | bytes script = 2; 130 | 131 | /* 132 | The earliest height in the chain for which the outpoint/output script could 133 | have been spent. This should in most cases be set to the broadcast height of 134 | the outpoint/output script. 135 | */ 136 | uint32 height_hint = 3; 137 | 138 | // TODO(wilmer): extend to support num confs on spending tx. 139 | } 140 | 141 | message SpendDetails { 142 | // The outpoint was that spent. 143 | Outpoint spending_outpoint = 1; 144 | 145 | // The raw bytes of the spending transaction. 146 | bytes raw_spending_tx = 2; 147 | 148 | // The hash of the spending transaction. 149 | bytes spending_tx_hash = 3; 150 | 151 | // The input of the spending transaction that fulfilled the spend request. 152 | uint32 spending_input_index = 4; 153 | 154 | // The height at which the spending transaction was included in a block. 155 | uint32 spending_height = 5; 156 | } 157 | 158 | message SpendEvent { 159 | oneof event { 160 | /* 161 | An event that includes the details of the spending transaction of the 162 | request (outpoint/output script). 163 | */ 164 | SpendDetails spend = 1; 165 | 166 | /* 167 | An event sent when the spending transaction of the request was 168 | reorged out of the chain. 169 | */ 170 | Reorg reorg = 2; 171 | } 172 | } 173 | 174 | message BlockEpoch { 175 | // The hash of the block. 176 | bytes hash = 1; 177 | 178 | // The height of the block. 179 | uint32 height = 2; 180 | } 181 | -------------------------------------------------------------------------------- /lnd/v0.11.1-beta/walletunlocker.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "rpc.proto"; 4 | 5 | package lnrpc; 6 | 7 | option go_package = "github.com/lightningnetwork/lnd/lnrpc"; 8 | 9 | /* 10 | * Comments in this file will be directly parsed into the API 11 | * Documentation as descriptions of the associated method, message, or field. 12 | * These descriptions should go right above the definition of the object, and 13 | * can be in either block or // comment format. 14 | * 15 | * An RPC method can be matched to an lncli command by placing a line in the 16 | * beginning of the description in exactly the following format: 17 | * lncli: `methodname` 18 | * 19 | * Failure to specify the exact name of the command will cause documentation 20 | * generation to fail. 21 | * 22 | * More information on how exactly the gRPC documentation is generated from 23 | * this proto file can be found here: 24 | * https://github.com/lightninglabs/lightning-api 25 | */ 26 | 27 | // WalletUnlocker is a service that is used to set up a wallet password for 28 | // lnd at first startup, and unlock a previously set up wallet. 29 | service WalletUnlocker { 30 | /* 31 | GenSeed is the first method that should be used to instantiate a new lnd 32 | instance. This method allows a caller to generate a new aezeed cipher seed 33 | given an optional passphrase. If provided, the passphrase will be necessary 34 | to decrypt the cipherseed to expose the internal wallet seed. 35 | 36 | Once the cipherseed is obtained and verified by the user, the InitWallet 37 | method should be used to commit the newly generated seed, and create the 38 | wallet. 39 | */ 40 | rpc GenSeed (GenSeedRequest) returns (GenSeedResponse); 41 | 42 | /* 43 | InitWallet is used when lnd is starting up for the first time to fully 44 | initialize the daemon and its internal wallet. At the very least a wallet 45 | password must be provided. This will be used to encrypt sensitive material 46 | on disk. 47 | 48 | In the case of a recovery scenario, the user can also specify their aezeed 49 | mnemonic and passphrase. If set, then the daemon will use this prior state 50 | to initialize its internal wallet. 51 | 52 | Alternatively, this can be used along with the GenSeed RPC to obtain a 53 | seed, then present it to the user. Once it has been verified by the user, 54 | the seed can be fed into this RPC in order to commit the new wallet. 55 | */ 56 | rpc InitWallet (InitWalletRequest) returns (InitWalletResponse); 57 | 58 | /* lncli: `unlock` 59 | UnlockWallet is used at startup of lnd to provide a password to unlock 60 | the wallet database. 61 | */ 62 | rpc UnlockWallet (UnlockWalletRequest) returns (UnlockWalletResponse); 63 | 64 | /* lncli: `changepassword` 65 | ChangePassword changes the password of the encrypted wallet. This will 66 | automatically unlock the wallet database if successful. 67 | */ 68 | rpc ChangePassword (ChangePasswordRequest) returns (ChangePasswordResponse); 69 | } 70 | 71 | message GenSeedRequest { 72 | /* 73 | aezeed_passphrase is an optional user provided passphrase that will be used 74 | to encrypt the generated aezeed cipher seed. When using REST, this field 75 | must be encoded as base64. 76 | */ 77 | bytes aezeed_passphrase = 1; 78 | 79 | /* 80 | seed_entropy is an optional 16-bytes generated via CSPRNG. If not 81 | specified, then a fresh set of randomness will be used to create the seed. 82 | When using REST, this field must be encoded as base64. 83 | */ 84 | bytes seed_entropy = 2; 85 | } 86 | message GenSeedResponse { 87 | /* 88 | cipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed 89 | cipher seed obtained by the user. This field is optional, as if not 90 | provided, then the daemon will generate a new cipher seed for the user. 91 | Otherwise, then the daemon will attempt to recover the wallet state linked 92 | to this cipher seed. 93 | */ 94 | repeated string cipher_seed_mnemonic = 1; 95 | 96 | /* 97 | enciphered_seed are the raw aezeed cipher seed bytes. This is the raw 98 | cipher text before run through our mnemonic encoding scheme. 99 | */ 100 | bytes enciphered_seed = 2; 101 | } 102 | 103 | message InitWalletRequest { 104 | /* 105 | wallet_password is the passphrase that should be used to encrypt the 106 | wallet. This MUST be at least 8 chars in length. After creation, this 107 | password is required to unlock the daemon. When using REST, this field 108 | must be encoded as base64. 109 | */ 110 | bytes wallet_password = 1; 111 | 112 | /* 113 | cipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed 114 | cipher seed obtained by the user. This may have been generated by the 115 | GenSeed method, or be an existing seed. 116 | */ 117 | repeated string cipher_seed_mnemonic = 2; 118 | 119 | /* 120 | aezeed_passphrase is an optional user provided passphrase that will be used 121 | to encrypt the generated aezeed cipher seed. When using REST, this field 122 | must be encoded as base64. 123 | */ 124 | bytes aezeed_passphrase = 3; 125 | 126 | /* 127 | recovery_window is an optional argument specifying the address lookahead 128 | when restoring a wallet seed. The recovery window applies to each 129 | individual branch of the BIP44 derivation paths. Supplying a recovery 130 | window of zero indicates that no addresses should be recovered, such after 131 | the first initialization of the wallet. 132 | */ 133 | int32 recovery_window = 4; 134 | 135 | /* 136 | channel_backups is an optional argument that allows clients to recover the 137 | settled funds within a set of channels. This should be populated if the 138 | user was unable to close out all channels and sweep funds before partial or 139 | total data loss occurred. If specified, then after on-chain recovery of 140 | funds, lnd begin to carry out the data loss recovery protocol in order to 141 | recover the funds in each channel from a remote force closed transaction. 142 | */ 143 | ChanBackupSnapshot channel_backups = 5; 144 | } 145 | message InitWalletResponse { 146 | } 147 | 148 | message UnlockWalletRequest { 149 | /* 150 | wallet_password should be the current valid passphrase for the daemon. This 151 | will be required to decrypt on-disk material that the daemon requires to 152 | function properly. When using REST, this field must be encoded as base64. 153 | */ 154 | bytes wallet_password = 1; 155 | 156 | /* 157 | recovery_window is an optional argument specifying the address lookahead 158 | when restoring a wallet seed. The recovery window applies to each 159 | individual branch of the BIP44 derivation paths. Supplying a recovery 160 | window of zero indicates that no addresses should be recovered, such after 161 | the first initialization of the wallet. 162 | */ 163 | int32 recovery_window = 2; 164 | 165 | /* 166 | channel_backups is an optional argument that allows clients to recover the 167 | settled funds within a set of channels. This should be populated if the 168 | user was unable to close out all channels and sweep funds before partial or 169 | total data loss occurred. If specified, then after on-chain recovery of 170 | funds, lnd begin to carry out the data loss recovery protocol in order to 171 | recover the funds in each channel from a remote force closed transaction. 172 | */ 173 | ChanBackupSnapshot channel_backups = 3; 174 | } 175 | message UnlockWalletResponse { 176 | } 177 | 178 | message ChangePasswordRequest { 179 | /* 180 | current_password should be the current valid passphrase used to unlock the 181 | daemon. When using REST, this field must be encoded as base64. 182 | */ 183 | bytes current_password = 1; 184 | 185 | /* 186 | new_password should be the new passphrase that will be needed to unlock the 187 | daemon. When using REST, this field must be encoded as base64. 188 | */ 189 | bytes new_password = 2; 190 | } 191 | message ChangePasswordResponse { 192 | } 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## @radar/lnrpc 2 | 3 | [![CircleCI](https://img.shields.io/circleci/project/github/RadarTech/lnrpc/master.svg?style=flat)](https://circleci.com/gh/RadarTech/lnrpc) 4 | [![Known Vulnerabilities](https://snyk.io/test/github/RadarTech/lnrpc/badge.svg?targetFile=package.json)](https://snyk.io/test/github/RadarTech/lnrpc?targetFile=package.json) 5 | [![NPM Version](https://img.shields.io/npm/v/@radar/lnrpc.svg?style=flat)](https://www.npmjs.com/package/@radar/lnrpc) 6 | [![License](https://img.shields.io/github/license/radartech/lnrpc.svg?style=flat)](https://img.shields.io/github/license/radartech/lnrpc.svg?style=flat) 7 | 8 | A Typescript gRPC client for LND with support for all LND sub-servers. Originally forked from [Matt-Jensen/lnrpc](https://github.com/Matt-Jensen/lnrpc). 9 | 10 | ### Features 11 | - Auto-generates [lnd/lnrpc](https://github.com/lightningnetwork/lnd/tree/master/lnrpc) clients and Typescript type definitions using a target release tag 12 | - Supports all LND sub-servers 13 | - Wraps requests in promises 14 | - Easily setup SSL and Macaroons 15 | - Instantiates all gRPC services 16 | - uint64/int64 types cast to string to prevent precision loss 17 | 18 | ### Installation 19 | ```sh 20 | npm install @radar/lnrpc 21 | # OR 22 | yarn add @radar/lnrpc 23 | ``` 24 | 25 | **Notes:** 26 | - Ensure you have an lnd instance running with `--no-macaroons`, unless you provide macaroon authentication to your lnrpc instance when created. 27 | - If you want to interact with the LND sub-servers, ensure that LND was compiled with the necessary sub-server build tags. 28 | - If the following error is thrown in the consuming application, run `npm rebuild`: 29 | ``` 30 | Error: Failed to load gRPC binary module because it was not installed for the current system 31 | ``` 32 | 33 | ### Usage 34 | 35 | This package exports a create function for the main gRPC server as well as each sub-server: 36 | 37 | ```typescript 38 | import { 39 | createAutopilotRpc, 40 | createChainRpc, 41 | createInvoicesRpc, 42 | createLnRpc, 43 | createRouterRpc, 44 | createSignRpc, 45 | createWalletRpc, 46 | createWatchtowerRpc, 47 | createWtClientRpc, 48 | } from '@radar/lnrpc'; 49 | ``` 50 | 51 | You can also import the create function for the main gRPC server using the default import: 52 | 53 | ```typescript 54 | import createLnRpc from '@radar/lnrpc'; 55 | ``` 56 | 57 | If you want to interact with all servers, wrap the functions in a class or object for easy initialization: 58 | 59 | ```typescript 60 | import createLnRpc, { 61 | AutopilotRpc, 62 | ChainRpc, 63 | createAutopilotRpc, 64 | createChainRpc, 65 | createInvoicesRpc, 66 | createRouterRpc, 67 | createSignRpc, 68 | createWalletRpc, 69 | createWatchtowerRpc, 70 | createWtClientRpc, 71 | InvoicesRpc, 72 | LnRpc, 73 | RouterRpc, 74 | RpcClientConfig, 75 | SignRpc, 76 | WalletRpc, 77 | WatchtowerRpc, 78 | WtClientRpc, 79 | } from '@radar/lnrpc'; 80 | 81 | export class Lightning { 82 | public static lnrpc: LnRpc; 83 | public static autopilotrpc: AutopilotRpc; 84 | public static chainrpc: ChainRpc; 85 | public static invoicesrpc: InvoicesRpc; 86 | public static routerrpc: RouterRpc; 87 | public static signrpc: SignRpc; 88 | public static walletrpc: WalletRpc; 89 | public static watchtowerrpc: WatchtowerRpc; 90 | public static wtclientrpc: WtClientRpc; 91 | 92 | /** 93 | * Initialize gRPC clients for the main server and all sub-servers 94 | * @param config The RPC client connection configuration 95 | */ 96 | public static async init(config: RpcClientConfig): Promise { 97 | this.lnrpc = await createLnRpc(config); 98 | this.autopilotrpc = await createAutopilotRpc(config); 99 | this.chainrpc = await createChainRpc(config); 100 | this.invoicesrpc = await createInvoicesRpc(config); 101 | this.routerrpc = await createRouterRpc(config); 102 | this.signrpc = await createSignRpc(config); 103 | this.walletrpc = await createWalletRpc(config); 104 | this.watchtowerrpc = await createWatchtowerRpc(config); 105 | this.wtclientrpc = await createWtClientRpc(config); 106 | } 107 | } 108 | ``` 109 | 110 | ### Usage Example - Main Server 111 | 112 | Connecting to an lnd instance at `localhost:10001`. 113 | 114 | ```typescript 115 | import createLnRpc from '@radar/lnrpc'; 116 | 117 | (async () => { 118 | const lnRpcClient = await createLnRpc(config); 119 | 120 | // All requests are promisified and typed 121 | const { confirmedBalance } = await lnRpcClient.walletBalance(); 122 | 123 | // ...and you're off! 124 | console.log(confirmedBalance); 125 | 126 | // subscribe to LND server events 127 | const subscriber = await lnRpcClient.subscribeInvoices(); 128 | subscriber.on('data', invoice => { 129 | console.log(invoice); // do something with invoice event 130 | }); 131 | })(); 132 | ``` 133 | 134 | ### Options Example - Main Server 135 | 136 | ```typescript 137 | import createLnRpc from '@radar/lnrpc'; 138 | 139 | (async () => { 140 | const lnRpcClient = await createLnRpc({ 141 | /* 142 | * By default lnrpc connects to `localhost:10001`, 143 | * however we can point to any host. 144 | */ 145 | server: '173.239.209.2:3001', 146 | 147 | /* 148 | * By default lnrpc looks for your tls certificate at: 149 | * `~/.lnd/tls.cert`, unless it detects you're using macOS and 150 | * defaults to `~/Library/Application\ Support/Lnd/tls.cert` 151 | * however you can configure your own SSL certificate path like: 152 | */ 153 | tls: './path/to/tls.cert', 154 | 155 | /* 156 | * You can also provide a TLS certificate directly as a string 157 | * (Just make sure you don't commit this to git). 158 | * Overwrites: `tls` 159 | */ 160 | cert: process.env.MY_SSL_CERT, 161 | 162 | /* 163 | * Optional path to configure macaroon authentication 164 | * from LND generated macaroon file. 165 | */ 166 | macaroonPath: './path/to/data/admin.macaroon', 167 | 168 | /* 169 | * Optional way to configure macaroon authentication by 170 | * passing a hex encoded string of your macaroon file. 171 | * Encoding: `xxd -ps -u -c 1000 ./path/to/data/admin.macaroon` 172 | * Details: https://github.com/lightningnetwork/lnd/blob/dc3db4b/docs/macaroons.md#using-macaroons-with-grpc-clients 173 | */ 174 | macaroon: process.env.MY_MACAROON_HEX, 175 | }); 176 | 177 | try { 178 | const getInfoResponse = await lnRpcClient.getInfo(); 179 | console.log(getInfoResponse); 180 | } catch (error) { 181 | console.error(error); 182 | } 183 | })(); 184 | ``` 185 | 186 | ### API Reference 187 | 188 | [All main server (lnrpc) methods documentation can be found here](http://api.lightning.community). 189 | 190 | ### Usage With BTCPayServer 191 | 192 | By default lnrpc assumes SSl certificate pinning. 193 | In order to use lnrpc with a service (like BTCPayServer) which manages your certification, 194 | you'll have to opt to disable certificate pinning by passing `{ tls: false }` within your lnrpc configuration. 195 | 196 | ### Contributing 197 | 198 | #### Clone Repository & Install Dependencies 199 | ```sh 200 | git clone git@github.com:RadarTech/lnrpc.git && cd $_ 201 | 202 | npm install 203 | # OR 204 | yarn 205 | ``` 206 | 207 | #### Change LND gRPC release version 208 | To change the gRPC definitions used for all auto-generated types and RPC methods edit the `config.lnd_release_tag` value in `package.json` to the desired [LND release tag](https://github.com/lightningnetwork/lnd/releases) and run the following: 209 | 210 | ```sh 211 | npm run update-protos 212 | # OR 213 | yarn update-protos 214 | 215 | # AND 216 | 217 | npm run generate 218 | # OR 219 | yarn generate 220 | ``` 221 | Newly generated type definitions will be available in `./generated`. 222 | You can now delete the old proto file inside the lnd directory. 223 | Use the generated type definitions to update the types in `src/types/rpc`. 224 | Any added streaming methods must be included in the `subscriptionMethods` array that's into the `createServiceClient` function. 225 | This prevents streaming methods from being promisified. 226 | 227 | ### License 228 | 229 | This project is licensed under the MIT License. 230 | -------------------------------------------------------------------------------- /src/types/generated/invoicesrpc/invoices_pb.d.ts: -------------------------------------------------------------------------------- 1 | // package: invoicesrpc 2 | // file: invoicesrpc/invoices.proto 3 | 4 | import * as jspb from "google-protobuf"; 5 | import * as rpc_pb from "../rpc_pb"; 6 | 7 | export class CancelInvoiceMsg extends jspb.Message { 8 | getPaymentHash(): Uint8Array | string; 9 | getPaymentHash_asU8(): Uint8Array; 10 | getPaymentHash_asB64(): string; 11 | setPaymentHash(value: Uint8Array | string): void; 12 | 13 | serializeBinary(): Uint8Array; 14 | toObject(includeInstance?: boolean): CancelInvoiceMsg.AsObject; 15 | static toObject(includeInstance: boolean, msg: CancelInvoiceMsg): CancelInvoiceMsg.AsObject; 16 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 17 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 18 | static serializeBinaryToWriter(message: CancelInvoiceMsg, writer: jspb.BinaryWriter): void; 19 | static deserializeBinary(bytes: Uint8Array): CancelInvoiceMsg; 20 | static deserializeBinaryFromReader(message: CancelInvoiceMsg, reader: jspb.BinaryReader): CancelInvoiceMsg; 21 | } 22 | 23 | export namespace CancelInvoiceMsg { 24 | export type AsObject = { 25 | paymentHash: Uint8Array | string, 26 | } 27 | } 28 | 29 | export class CancelInvoiceResp extends jspb.Message { 30 | serializeBinary(): Uint8Array; 31 | toObject(includeInstance?: boolean): CancelInvoiceResp.AsObject; 32 | static toObject(includeInstance: boolean, msg: CancelInvoiceResp): CancelInvoiceResp.AsObject; 33 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 34 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 35 | static serializeBinaryToWriter(message: CancelInvoiceResp, writer: jspb.BinaryWriter): void; 36 | static deserializeBinary(bytes: Uint8Array): CancelInvoiceResp; 37 | static deserializeBinaryFromReader(message: CancelInvoiceResp, reader: jspb.BinaryReader): CancelInvoiceResp; 38 | } 39 | 40 | export namespace CancelInvoiceResp { 41 | export type AsObject = { 42 | } 43 | } 44 | 45 | export class AddHoldInvoiceRequest extends jspb.Message { 46 | getMemo(): string; 47 | setMemo(value: string): void; 48 | 49 | getHash(): Uint8Array | string; 50 | getHash_asU8(): Uint8Array; 51 | getHash_asB64(): string; 52 | setHash(value: Uint8Array | string): void; 53 | 54 | getValue(): number; 55 | setValue(value: number): void; 56 | 57 | getValueMsat(): number; 58 | setValueMsat(value: number): void; 59 | 60 | getDescriptionHash(): Uint8Array | string; 61 | getDescriptionHash_asU8(): Uint8Array; 62 | getDescriptionHash_asB64(): string; 63 | setDescriptionHash(value: Uint8Array | string): void; 64 | 65 | getExpiry(): number; 66 | setExpiry(value: number): void; 67 | 68 | getFallbackAddr(): string; 69 | setFallbackAddr(value: string): void; 70 | 71 | getCltvExpiry(): number; 72 | setCltvExpiry(value: number): void; 73 | 74 | clearRouteHintsList(): void; 75 | getRouteHintsList(): Array; 76 | setRouteHintsList(value: Array): void; 77 | addRouteHints(value?: rpc_pb.RouteHint, index?: number): rpc_pb.RouteHint; 78 | 79 | getPrivate(): boolean; 80 | setPrivate(value: boolean): void; 81 | 82 | serializeBinary(): Uint8Array; 83 | toObject(includeInstance?: boolean): AddHoldInvoiceRequest.AsObject; 84 | static toObject(includeInstance: boolean, msg: AddHoldInvoiceRequest): AddHoldInvoiceRequest.AsObject; 85 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 86 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 87 | static serializeBinaryToWriter(message: AddHoldInvoiceRequest, writer: jspb.BinaryWriter): void; 88 | static deserializeBinary(bytes: Uint8Array): AddHoldInvoiceRequest; 89 | static deserializeBinaryFromReader(message: AddHoldInvoiceRequest, reader: jspb.BinaryReader): AddHoldInvoiceRequest; 90 | } 91 | 92 | export namespace AddHoldInvoiceRequest { 93 | export type AsObject = { 94 | memo: string, 95 | hash: Uint8Array | string, 96 | value: number, 97 | valueMsat: number, 98 | descriptionHash: Uint8Array | string, 99 | expiry: number, 100 | fallbackAddr: string, 101 | cltvExpiry: number, 102 | routeHints: Array, 103 | pb_private: boolean, 104 | } 105 | } 106 | 107 | export class AddHoldInvoiceResp extends jspb.Message { 108 | getPaymentRequest(): string; 109 | setPaymentRequest(value: string): void; 110 | 111 | serializeBinary(): Uint8Array; 112 | toObject(includeInstance?: boolean): AddHoldInvoiceResp.AsObject; 113 | static toObject(includeInstance: boolean, msg: AddHoldInvoiceResp): AddHoldInvoiceResp.AsObject; 114 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 115 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 116 | static serializeBinaryToWriter(message: AddHoldInvoiceResp, writer: jspb.BinaryWriter): void; 117 | static deserializeBinary(bytes: Uint8Array): AddHoldInvoiceResp; 118 | static deserializeBinaryFromReader(message: AddHoldInvoiceResp, reader: jspb.BinaryReader): AddHoldInvoiceResp; 119 | } 120 | 121 | export namespace AddHoldInvoiceResp { 122 | export type AsObject = { 123 | paymentRequest: string, 124 | } 125 | } 126 | 127 | export class SettleInvoiceMsg extends jspb.Message { 128 | getPreimage(): Uint8Array | string; 129 | getPreimage_asU8(): Uint8Array; 130 | getPreimage_asB64(): string; 131 | setPreimage(value: Uint8Array | string): void; 132 | 133 | serializeBinary(): Uint8Array; 134 | toObject(includeInstance?: boolean): SettleInvoiceMsg.AsObject; 135 | static toObject(includeInstance: boolean, msg: SettleInvoiceMsg): SettleInvoiceMsg.AsObject; 136 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 137 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 138 | static serializeBinaryToWriter(message: SettleInvoiceMsg, writer: jspb.BinaryWriter): void; 139 | static deserializeBinary(bytes: Uint8Array): SettleInvoiceMsg; 140 | static deserializeBinaryFromReader(message: SettleInvoiceMsg, reader: jspb.BinaryReader): SettleInvoiceMsg; 141 | } 142 | 143 | export namespace SettleInvoiceMsg { 144 | export type AsObject = { 145 | preimage: Uint8Array | string, 146 | } 147 | } 148 | 149 | export class SettleInvoiceResp extends jspb.Message { 150 | serializeBinary(): Uint8Array; 151 | toObject(includeInstance?: boolean): SettleInvoiceResp.AsObject; 152 | static toObject(includeInstance: boolean, msg: SettleInvoiceResp): SettleInvoiceResp.AsObject; 153 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 154 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 155 | static serializeBinaryToWriter(message: SettleInvoiceResp, writer: jspb.BinaryWriter): void; 156 | static deserializeBinary(bytes: Uint8Array): SettleInvoiceResp; 157 | static deserializeBinaryFromReader(message: SettleInvoiceResp, reader: jspb.BinaryReader): SettleInvoiceResp; 158 | } 159 | 160 | export namespace SettleInvoiceResp { 161 | export type AsObject = { 162 | } 163 | } 164 | 165 | export class SubscribeSingleInvoiceRequest extends jspb.Message { 166 | getRHash(): Uint8Array | string; 167 | getRHash_asU8(): Uint8Array; 168 | getRHash_asB64(): string; 169 | setRHash(value: Uint8Array | string): void; 170 | 171 | serializeBinary(): Uint8Array; 172 | toObject(includeInstance?: boolean): SubscribeSingleInvoiceRequest.AsObject; 173 | static toObject(includeInstance: boolean, msg: SubscribeSingleInvoiceRequest): SubscribeSingleInvoiceRequest.AsObject; 174 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 175 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 176 | static serializeBinaryToWriter(message: SubscribeSingleInvoiceRequest, writer: jspb.BinaryWriter): void; 177 | static deserializeBinary(bytes: Uint8Array): SubscribeSingleInvoiceRequest; 178 | static deserializeBinaryFromReader(message: SubscribeSingleInvoiceRequest, reader: jspb.BinaryReader): SubscribeSingleInvoiceRequest; 179 | } 180 | 181 | export namespace SubscribeSingleInvoiceRequest { 182 | export type AsObject = { 183 | rHash: Uint8Array | string, 184 | } 185 | } 186 | 187 | -------------------------------------------------------------------------------- /lnd/v0.11.1-beta/signrpc/signer.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package signrpc; 4 | 5 | option go_package = "github.com/lightningnetwork/lnd/lnrpc/signrpc"; 6 | 7 | // Signer is a service that gives access to the signing functionality of the 8 | // daemon's wallet. 9 | service Signer { 10 | /* 11 | SignOutputRaw is a method that can be used to generated a signature for a 12 | set of inputs/outputs to a transaction. Each request specifies details 13 | concerning how the outputs should be signed, which keys they should be 14 | signed with, and also any optional tweaks. The return value is a fixed 15 | 64-byte signature (the same format as we use on the wire in Lightning). 16 | 17 | If we are unable to sign using the specified keys, then an error will be 18 | returned. 19 | */ 20 | rpc SignOutputRaw (SignReq) returns (SignResp); 21 | 22 | /* 23 | ComputeInputScript generates a complete InputIndex for the passed 24 | transaction with the signature as defined within the passed SignDescriptor. 25 | This method should be capable of generating the proper input script for 26 | both regular p2wkh output and p2wkh outputs nested within a regular p2sh 27 | output. 28 | 29 | Note that when using this method to sign inputs belonging to the wallet, 30 | the only items of the SignDescriptor that need to be populated are pkScript 31 | in the TxOut field, the value in that same field, and finally the input 32 | index. 33 | */ 34 | rpc ComputeInputScript (SignReq) returns (InputScriptResp); 35 | 36 | /* 37 | SignMessage signs a message with the key specified in the key locator. The 38 | returned signature is fixed-size LN wire format encoded. 39 | 40 | The main difference to SignMessage in the main RPC is that a specific key is 41 | used to sign the message instead of the node identity private key. 42 | */ 43 | rpc SignMessage (SignMessageReq) returns (SignMessageResp); 44 | 45 | /* 46 | VerifyMessage verifies a signature over a message using the public key 47 | provided. The signature must be fixed-size LN wire format encoded. 48 | 49 | The main difference to VerifyMessage in the main RPC is that the public key 50 | used to sign the message does not have to be a node known to the network. 51 | */ 52 | rpc VerifyMessage (VerifyMessageReq) returns (VerifyMessageResp); 53 | 54 | /* 55 | DeriveSharedKey returns a shared secret key by performing Diffie-Hellman key 56 | derivation between the ephemeral public key in the request and the node's 57 | key specified in the key_loc parameter (or the node's identity private key 58 | if no key locator is specified): 59 | P_shared = privKeyNode * ephemeralPubkey 60 | The resulting shared public key is serialized in the compressed format and 61 | hashed with sha256, resulting in the final key length of 256bit. 62 | */ 63 | rpc DeriveSharedKey (SharedKeyRequest) returns (SharedKeyResponse); 64 | } 65 | 66 | message KeyLocator { 67 | // The family of key being identified. 68 | int32 key_family = 1; 69 | 70 | // The precise index of the key being identified. 71 | int32 key_index = 2; 72 | } 73 | 74 | message KeyDescriptor { 75 | /* 76 | The raw bytes of the key being identified. Either this or the KeyLocator 77 | must be specified. 78 | */ 79 | bytes raw_key_bytes = 1; 80 | 81 | /* 82 | The key locator that identifies which key to use for signing. Either this 83 | or the raw bytes of the target key must be specified. 84 | */ 85 | KeyLocator key_loc = 2; 86 | } 87 | 88 | message TxOut { 89 | // The value of the output being spent. 90 | int64 value = 1; 91 | 92 | // The script of the output being spent. 93 | bytes pk_script = 2; 94 | } 95 | 96 | message SignDescriptor { 97 | /* 98 | A descriptor that precisely describes *which* key to use for signing. This 99 | may provide the raw public key directly, or require the Signer to re-derive 100 | the key according to the populated derivation path. 101 | 102 | Note that if the key descriptor was obtained through walletrpc.DeriveKey, 103 | then the key locator MUST always be provided, since the derived keys are not 104 | persisted unlike with DeriveNextKey. 105 | */ 106 | KeyDescriptor key_desc = 1; 107 | 108 | /* 109 | A scalar value that will be added to the private key corresponding to the 110 | above public key to obtain the private key to be used to sign this input. 111 | This value is typically derived via the following computation: 112 | 113 | * derivedKey = privkey + sha256(perCommitmentPoint || pubKey) mod N 114 | */ 115 | bytes single_tweak = 2; 116 | 117 | /* 118 | A private key that will be used in combination with its corresponding 119 | private key to derive the private key that is to be used to sign the target 120 | input. Within the Lightning protocol, this value is typically the 121 | commitment secret from a previously revoked commitment transaction. This 122 | value is in combination with two hash values, and the original private key 123 | to derive the private key to be used when signing. 124 | 125 | * k = (privKey*sha256(pubKey || tweakPub) + 126 | tweakPriv*sha256(tweakPub || pubKey)) mod N 127 | */ 128 | bytes double_tweak = 3; 129 | 130 | /* 131 | The full script required to properly redeem the output. This field will 132 | only be populated if a p2wsh or a p2sh output is being signed. 133 | */ 134 | bytes witness_script = 4; 135 | 136 | /* 137 | A description of the output being spent. The value and script MUST be 138 | provided. 139 | */ 140 | TxOut output = 5; 141 | 142 | /* 143 | The target sighash type that should be used when generating the final 144 | sighash, and signature. 145 | */ 146 | uint32 sighash = 7; 147 | 148 | /* 149 | The target input within the transaction that should be signed. 150 | */ 151 | int32 input_index = 8; 152 | } 153 | 154 | message SignReq { 155 | // The raw bytes of the transaction to be signed. 156 | bytes raw_tx_bytes = 1; 157 | 158 | // A set of sign descriptors, for each input to be signed. 159 | repeated SignDescriptor sign_descs = 2; 160 | } 161 | 162 | message SignResp { 163 | /* 164 | A set of signatures realized in a fixed 64-byte format ordered in ascending 165 | input order. 166 | */ 167 | repeated bytes raw_sigs = 1; 168 | } 169 | 170 | message InputScript { 171 | // The serializes witness stack for the specified input. 172 | repeated bytes witness = 1; 173 | 174 | /* 175 | The optional sig script for the specified witness that will only be set if 176 | the input specified is a nested p2sh witness program. 177 | */ 178 | bytes sig_script = 2; 179 | } 180 | 181 | message InputScriptResp { 182 | // The set of fully valid input scripts requested. 183 | repeated InputScript input_scripts = 1; 184 | } 185 | 186 | message SignMessageReq { 187 | // The message to be signed. 188 | bytes msg = 1; 189 | 190 | // The key locator that identifies which key to use for signing. 191 | KeyLocator key_loc = 2; 192 | } 193 | message SignMessageResp { 194 | /* 195 | The signature for the given message in the fixed-size LN wire format. 196 | */ 197 | bytes signature = 1; 198 | } 199 | 200 | message VerifyMessageReq { 201 | // The message over which the signature is to be verified. 202 | bytes msg = 1; 203 | 204 | /* 205 | The fixed-size LN wire encoded signature to be verified over the given 206 | message. 207 | */ 208 | bytes signature = 2; 209 | 210 | // The public key the signature has to be valid for. 211 | bytes pubkey = 3; 212 | } 213 | message VerifyMessageResp { 214 | // Whether the signature was valid over the given message. 215 | bool valid = 1; 216 | } 217 | 218 | message SharedKeyRequest { 219 | // The ephemeral public key to use for the DH key derivation. 220 | bytes ephemeral_pubkey = 1; 221 | 222 | /* 223 | The optional key locator of the local key that should be used. If this 224 | parameter is not set then the node's identity private key will be used. 225 | */ 226 | KeyLocator key_loc = 2; 227 | } 228 | 229 | message SharedKeyResponse { 230 | // The shared public key, hashed with sha256. 231 | bytes shared_key = 1; 232 | } 233 | -------------------------------------------------------------------------------- /src/types/rpc/wallet-rpc.ts: -------------------------------------------------------------------------------- 1 | import { OutPoint, TransactionDetails, Utxo } from './ln-rpc'; 2 | import { KeyDescriptor, KeyLocator, TxOut } from './sign-rpc'; 3 | 4 | export enum WitnessType { 5 | UNKNOWN_WITNESS = 0, 6 | COMMITMENT_TIME_LOCK = 1, 7 | COMMITMENT_NO_DELAY = 2, 8 | COMMITMENT_REVOKE = 3, 9 | HTLC_OFFERED_REVOKE = 4, 10 | HTLC_ACCEPTED_REVOKE = 5, 11 | HTLC_OFFERED_TIMEOUT_SECOND_LEVEL = 6, 12 | HTLC_ACCEPTED_SUCCESS_SECOND_LEVEL = 7, 13 | HTLC_OFFERED_REMOTE_TIMEOUT = 8, 14 | HTLC_ACCEPTED_REMOTE_SUCCESS = 9, 15 | HTLC_SECOND_LEVEL_REVOKE = 10, 16 | WITNESS_KEY_HASH = 11, 17 | NESTED_WITNESS_KEY_HASH = 12, 18 | COMMITMENT_ANCHOR = 13, 19 | } 20 | 21 | export enum SweepsCase { 22 | SWEEPS_NOT_SET = 0, 23 | TRANSACTION_DETAILS = 1, 24 | TRANSACTION_IDS = 2, 25 | } 26 | 27 | export interface ListUnspentReq { 28 | minConfs?: number; 29 | maxConfs?: number; 30 | } 31 | 32 | export interface ListUnspentResp { 33 | utxos: Utxo[]; 34 | } 35 | 36 | export interface LeaseOutputRequest { 37 | id: Buffer | string; 38 | outpoint?: OutPoint; 39 | } 40 | 41 | export interface LeaseOutputResponse { 42 | expiration: number; 43 | } 44 | 45 | export interface ReleaseOutputRequest { 46 | id: Buffer | string; 47 | outpoint?: OutPoint; 48 | } 49 | 50 | export interface KeyReq { 51 | keyFingerPrint: number; 52 | keyFamily: number; 53 | } 54 | 55 | export interface AddrResponse { 56 | addr: string; 57 | } 58 | 59 | export interface Tx { 60 | txHex: Buffer | string; 61 | label?: string; 62 | } 63 | 64 | export interface PublishResponse { 65 | publishError?: string; 66 | } 67 | 68 | export interface SendOutputsRequest { 69 | satPerKw: number; 70 | outputs: TxOut[]; 71 | label?: string; 72 | } 73 | 74 | export interface SendOutputsResponse { 75 | rawTx: Buffer | string; 76 | } 77 | 78 | export interface EstimateFeeReq { 79 | confTarget: number; 80 | } 81 | 82 | export interface EstimateFeeResp { 83 | satPerKw: number; 84 | } 85 | 86 | export interface PendingSweep { 87 | outpoint?: OutPoint; 88 | witnessType: WitnessType; 89 | amountSat: number; 90 | satPerByte: number; 91 | broadcastAttempts: number; 92 | nextBroadcastHeight: number; 93 | requestedConfTarget: number; 94 | requestedSatPerByte: number; 95 | force: boolean; 96 | } 97 | 98 | export interface PendingSweepsResponse { 99 | pendingSweeps: PendingSweep[]; 100 | } 101 | 102 | export interface BumpFeeRequest { 103 | outpoint: OutPoint; 104 | targetConf?: number; 105 | satPerByte?: number; 106 | force?: boolean; 107 | } 108 | 109 | export interface ListSweepsRequest { 110 | verbose?: boolean; 111 | } 112 | 113 | export interface TransactionIDs { 114 | transactionIds: string[]; 115 | } 116 | 117 | export interface ListSweepsResponse { 118 | transactionDetails?: TransactionDetails; 119 | transactionIds?: TransactionIDs; 120 | } 121 | 122 | export interface LabelTransactionRequest { 123 | txid: Buffer | string; 124 | label: string; 125 | overwrite?: boolean; 126 | } 127 | 128 | /** 129 | * LND Wallet gRPC API Client 130 | */ 131 | export interface WalletRpc { 132 | /** 133 | * listUnspent returns a list of all utxos spendable by the wallet with a 134 | * number of confirmations between the specified minimum and maximum. 135 | */ 136 | listUnspent(args?: ListUnspentReq): Promise; 137 | 138 | /** 139 | * leaseOutput locks an output to the given ID, preventing it from being 140 | * available for any future coin selection attempts. The absolute time of the 141 | * lock's expiration is returned. The expiration of the lock can be extended by 142 | * successive invocations of this RPC. Outputs can be unlocked before their 143 | * expiration through `ReleaseOutput`. 144 | */ 145 | leaseOutput(args: LeaseOutputRequest): Promise; 146 | 147 | /** 148 | * releaseOutput unlocks an output, allowing it to be available for coin 149 | * selection if it remains unspent. The ID should match the one used to 150 | * originally lock the output. 151 | */ 152 | releaseOutput(args?: ReleaseOutputRequest): Promise<{}>; 153 | 154 | /** 155 | * deriveNextKey attempts to derive the *next* key within the key family 156 | * (account in BIP43) specified. This method should return the next external 157 | * child within this branch. 158 | */ 159 | deriveNextKey(args: KeyReq): Promise; 160 | 161 | /** 162 | * deriveKey attempts to derive an arbitrary key specified by the passed 163 | * KeyLocator. 164 | */ 165 | deriveKey(args: KeyLocator): Promise; 166 | 167 | /** 168 | * nextAddr returns the next unused address within the wallet. 169 | */ 170 | nextAddr(args?: {}): Promise; 171 | 172 | /** 173 | * publishTransaction attempts to publish the passed transaction to the 174 | * network. Once this returns without an error, the wallet will continually 175 | * attempt to re-broadcast the transaction on start up, until it enters the 176 | * chain. 177 | */ 178 | publishTransaction(args: Tx): Promise; 179 | 180 | /** 181 | * sendOutputs is similar to the existing sendmany call in Bitcoind, and 182 | * allows the caller to create a transaction that sends to several outputs at 183 | * once. This is ideal when wanting to batch create a set of transactions. 184 | */ 185 | sendOutputs(args: SendOutputsRequest): Promise; 186 | 187 | /** 188 | * estimateFee attempts to query the internal fee estimator of the wallet to 189 | * determine the fee (in sat/kw) to attach to a transaction in order to 190 | * achieve the confirmation target. 191 | */ 192 | estimateFee(args: EstimateFeeReq): Promise; 193 | 194 | /* 195 | * pendingSweeps returns lists of on-chain outputs that lnd is currently 196 | * attempting to sweep within its central batching engine. Outputs with similar 197 | * fee rates are batched together in order to sweep them within a single 198 | * transaction. 199 | * NOTE: Some of the fields within PendingSweepsRequest are not guaranteed to 200 | * remain supported. This is an advanced API that depends on the internals of 201 | * the UtxoSweeper, so things may change. 202 | */ 203 | pendingSweeps(args?: {}): Promise; 204 | 205 | /* 206 | * bumpFee bumps the fee of an arbitrary input within a transaction. This RPC 207 | * takes a different approach than bitcoind's bumpfee command. lnd has a 208 | * central batching engine in which inputs with similar fee rates are batched 209 | * together to save on transaction fees. Due to this, we cannot rely on 210 | * bumping the fee on a specific transaction, since transactions can change at 211 | * any point with the addition of new inputs. The list of inputs that 212 | * currently exist within lnd's central batching engine can be retrieved 213 | * through the PendingSweeps RPC. 214 | * When bumping the fee of an input that currently exists within lnd's central 215 | * batching engine, a higher fee transaction will be created that replaces the 216 | * lower fee transaction through the Replace-By-Fee (RBF) policy. If it 217 | * This RPC also serves useful when wanting to perform a Child-Pays-For-Parent 218 | * (CPFP), where the child transaction pays for its parent's fee. This can be 219 | * done by specifying an outpoint within the low fee transaction that is under 220 | * the control of the wallet. 221 | * The fee preference can be expressed either as a specific fee rate or a delta 222 | * of blocks in which the output should be swept on-chain within. If a fee 223 | * preference is not explicitly specified, then an error is returned. 224 | * Note that this RPC currently doesn't perform any validation checks on the 225 | * fee preference being provided. For now, the responsibility of ensuring that 226 | * the new fee preference is sufficient is delegated to the user. 227 | */ 228 | bumpFee(args: BumpFeeRequest): Promise<{}>; 229 | 230 | /** 231 | * listSweeps returns a list of the sweep transactions our node has produced. 232 | * Note that these sweeps may not be confirmed yet, as we record sweeps on 233 | * broadcast, not confirmation. 234 | */ 235 | listSweeps(args?: ListSweepsRequest): Promise; 236 | 237 | /** 238 | * labelTransaction adds a label to a transaction. If the transaction already 239 | * has a label the call will fail unless the overwrite bool is set. This will 240 | * overwrite the exiting transaction label. Labels must not be empty, and 241 | * cannot exceed 500 characters. 242 | */ 243 | labelTransaction(args: LabelTransactionRequest): Promise<{}>; 244 | } 245 | -------------------------------------------------------------------------------- /src/types/generated/autopilotrpc/autopilot_pb.d.ts: -------------------------------------------------------------------------------- 1 | // package: autopilotrpc 2 | // file: autopilotrpc/autopilot.proto 3 | 4 | import * as jspb from "google-protobuf"; 5 | 6 | export class StatusRequest extends jspb.Message { 7 | serializeBinary(): Uint8Array; 8 | toObject(includeInstance?: boolean): StatusRequest.AsObject; 9 | static toObject(includeInstance: boolean, msg: StatusRequest): StatusRequest.AsObject; 10 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 11 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 12 | static serializeBinaryToWriter(message: StatusRequest, writer: jspb.BinaryWriter): void; 13 | static deserializeBinary(bytes: Uint8Array): StatusRequest; 14 | static deserializeBinaryFromReader(message: StatusRequest, reader: jspb.BinaryReader): StatusRequest; 15 | } 16 | 17 | export namespace StatusRequest { 18 | export type AsObject = { 19 | } 20 | } 21 | 22 | export class StatusResponse extends jspb.Message { 23 | getActive(): boolean; 24 | setActive(value: boolean): void; 25 | 26 | serializeBinary(): Uint8Array; 27 | toObject(includeInstance?: boolean): StatusResponse.AsObject; 28 | static toObject(includeInstance: boolean, msg: StatusResponse): StatusResponse.AsObject; 29 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 30 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 31 | static serializeBinaryToWriter(message: StatusResponse, writer: jspb.BinaryWriter): void; 32 | static deserializeBinary(bytes: Uint8Array): StatusResponse; 33 | static deserializeBinaryFromReader(message: StatusResponse, reader: jspb.BinaryReader): StatusResponse; 34 | } 35 | 36 | export namespace StatusResponse { 37 | export type AsObject = { 38 | active: boolean, 39 | } 40 | } 41 | 42 | export class ModifyStatusRequest extends jspb.Message { 43 | getEnable(): boolean; 44 | setEnable(value: boolean): void; 45 | 46 | serializeBinary(): Uint8Array; 47 | toObject(includeInstance?: boolean): ModifyStatusRequest.AsObject; 48 | static toObject(includeInstance: boolean, msg: ModifyStatusRequest): ModifyStatusRequest.AsObject; 49 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 50 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 51 | static serializeBinaryToWriter(message: ModifyStatusRequest, writer: jspb.BinaryWriter): void; 52 | static deserializeBinary(bytes: Uint8Array): ModifyStatusRequest; 53 | static deserializeBinaryFromReader(message: ModifyStatusRequest, reader: jspb.BinaryReader): ModifyStatusRequest; 54 | } 55 | 56 | export namespace ModifyStatusRequest { 57 | export type AsObject = { 58 | enable: boolean, 59 | } 60 | } 61 | 62 | export class ModifyStatusResponse extends jspb.Message { 63 | serializeBinary(): Uint8Array; 64 | toObject(includeInstance?: boolean): ModifyStatusResponse.AsObject; 65 | static toObject(includeInstance: boolean, msg: ModifyStatusResponse): ModifyStatusResponse.AsObject; 66 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 67 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 68 | static serializeBinaryToWriter(message: ModifyStatusResponse, writer: jspb.BinaryWriter): void; 69 | static deserializeBinary(bytes: Uint8Array): ModifyStatusResponse; 70 | static deserializeBinaryFromReader(message: ModifyStatusResponse, reader: jspb.BinaryReader): ModifyStatusResponse; 71 | } 72 | 73 | export namespace ModifyStatusResponse { 74 | export type AsObject = { 75 | } 76 | } 77 | 78 | export class QueryScoresRequest extends jspb.Message { 79 | clearPubkeysList(): void; 80 | getPubkeysList(): Array; 81 | setPubkeysList(value: Array): void; 82 | addPubkeys(value: string, index?: number): string; 83 | 84 | getIgnoreLocalState(): boolean; 85 | setIgnoreLocalState(value: boolean): void; 86 | 87 | serializeBinary(): Uint8Array; 88 | toObject(includeInstance?: boolean): QueryScoresRequest.AsObject; 89 | static toObject(includeInstance: boolean, msg: QueryScoresRequest): QueryScoresRequest.AsObject; 90 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 91 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 92 | static serializeBinaryToWriter(message: QueryScoresRequest, writer: jspb.BinaryWriter): void; 93 | static deserializeBinary(bytes: Uint8Array): QueryScoresRequest; 94 | static deserializeBinaryFromReader(message: QueryScoresRequest, reader: jspb.BinaryReader): QueryScoresRequest; 95 | } 96 | 97 | export namespace QueryScoresRequest { 98 | export type AsObject = { 99 | pubkeys: Array, 100 | ignoreLocalState: boolean, 101 | } 102 | } 103 | 104 | export class QueryScoresResponse extends jspb.Message { 105 | clearResultsList(): void; 106 | getResultsList(): Array; 107 | setResultsList(value: Array): void; 108 | addResults(value?: QueryScoresResponse.HeuristicResult, index?: number): QueryScoresResponse.HeuristicResult; 109 | 110 | serializeBinary(): Uint8Array; 111 | toObject(includeInstance?: boolean): QueryScoresResponse.AsObject; 112 | static toObject(includeInstance: boolean, msg: QueryScoresResponse): QueryScoresResponse.AsObject; 113 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 114 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 115 | static serializeBinaryToWriter(message: QueryScoresResponse, writer: jspb.BinaryWriter): void; 116 | static deserializeBinary(bytes: Uint8Array): QueryScoresResponse; 117 | static deserializeBinaryFromReader(message: QueryScoresResponse, reader: jspb.BinaryReader): QueryScoresResponse; 118 | } 119 | 120 | export namespace QueryScoresResponse { 121 | export type AsObject = { 122 | results: Array, 123 | } 124 | 125 | export class HeuristicResult extends jspb.Message { 126 | getHeuristic(): string; 127 | setHeuristic(value: string): void; 128 | 129 | getScoresMap(): jspb.Map; 130 | clearScoresMap(): void; 131 | serializeBinary(): Uint8Array; 132 | toObject(includeInstance?: boolean): HeuristicResult.AsObject; 133 | static toObject(includeInstance: boolean, msg: HeuristicResult): HeuristicResult.AsObject; 134 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 135 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 136 | static serializeBinaryToWriter(message: HeuristicResult, writer: jspb.BinaryWriter): void; 137 | static deserializeBinary(bytes: Uint8Array): HeuristicResult; 138 | static deserializeBinaryFromReader(message: HeuristicResult, reader: jspb.BinaryReader): HeuristicResult; 139 | } 140 | 141 | export namespace HeuristicResult { 142 | export type AsObject = { 143 | heuristic: string, 144 | scores: Array<[string, number]>, 145 | } 146 | } 147 | } 148 | 149 | export class SetScoresRequest extends jspb.Message { 150 | getHeuristic(): string; 151 | setHeuristic(value: string): void; 152 | 153 | getScoresMap(): jspb.Map; 154 | clearScoresMap(): void; 155 | serializeBinary(): Uint8Array; 156 | toObject(includeInstance?: boolean): SetScoresRequest.AsObject; 157 | static toObject(includeInstance: boolean, msg: SetScoresRequest): SetScoresRequest.AsObject; 158 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 159 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 160 | static serializeBinaryToWriter(message: SetScoresRequest, writer: jspb.BinaryWriter): void; 161 | static deserializeBinary(bytes: Uint8Array): SetScoresRequest; 162 | static deserializeBinaryFromReader(message: SetScoresRequest, reader: jspb.BinaryReader): SetScoresRequest; 163 | } 164 | 165 | export namespace SetScoresRequest { 166 | export type AsObject = { 167 | heuristic: string, 168 | scores: Array<[string, number]>, 169 | } 170 | } 171 | 172 | export class SetScoresResponse extends jspb.Message { 173 | serializeBinary(): Uint8Array; 174 | toObject(includeInstance?: boolean): SetScoresResponse.AsObject; 175 | static toObject(includeInstance: boolean, msg: SetScoresResponse): SetScoresResponse.AsObject; 176 | static extensions: {[key: number]: jspb.ExtensionFieldInfo}; 177 | static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; 178 | static serializeBinaryToWriter(message: SetScoresResponse, writer: jspb.BinaryWriter): void; 179 | static deserializeBinary(bytes: Uint8Array): SetScoresResponse; 180 | static deserializeBinaryFromReader(message: SetScoresResponse, reader: jspb.BinaryReader): SetScoresResponse; 181 | } 182 | 183 | export namespace SetScoresResponse { 184 | export type AsObject = { 185 | } 186 | } 187 | 188 | -------------------------------------------------------------------------------- /src/types/rpc/router-rpc.ts: -------------------------------------------------------------------------------- 1 | import { Duplex, Readable } from '../streams'; 2 | import { Failure, FailureCode, FeatureBit, HTLCAttempt, Payment, Route, RouteHint } from './ln-rpc'; 3 | 4 | export enum HtlcEventType { 5 | UNKNOWN = 0, 6 | SEND = 1, 7 | RECEIVE = 2, 8 | FORWARD = 3, 9 | } 10 | 11 | export enum FailureDetail { 12 | UNKNOWN = 0, 13 | NO_DETAIL = 1, 14 | ONION_DECODE = 2, 15 | LINK_NOT_ELIGIBLE = 3, 16 | ON_CHAIN_TIMEOUT = 4, 17 | HTLC_EXCEEDS_MAX = 5, 18 | INSUFFICIENT_BALANCE = 6, 19 | INCOMPLETE_FORWARD = 7, 20 | HTLC_ADD_FAILED = 8, 21 | FORWARDS_DISABLED = 9, 22 | INVOICE_CANCELED = 10, 23 | INVOICE_UNDERPAID = 11, 24 | INVOICE_EXPIRY_TOO_SOON = 12, 25 | INVOICE_NOT_OPEN = 13, 26 | MPP_INVOICE_TIMEOUT = 14, 27 | ADDRESS_MISMATCH = 15, 28 | SET_TOTAL_MISMATCH = 16, 29 | SET_TOTAL_TOO_LOW = 17, 30 | SET_OVERPAID = 18, 31 | UNKNOWN_INVOICE = 19, 32 | INVALID_KEYSEND = 20, 33 | MPP_IN_PROGRESS = 21, 34 | CIRCULAR_ROUTE = 22, 35 | } 36 | 37 | export enum PaymentState { 38 | IN_FLIGHT = 0, 39 | SUCCEEDED = 1, 40 | FAILED_TIMEOUT = 2, 41 | FAILED_NO_ROUTE = 3, 42 | FAILED_ERROR = 4, 43 | FAILED_INCORRECT_PAYMENT_DETAILS = 5, 44 | FAILED_INSUFFICIENT_BALANCE = 6, 45 | } 46 | 47 | export enum ResolveHoldForwardAction { 48 | SETTLE = 0, 49 | FAIL = 1, 50 | RESUME = 2, 51 | } 52 | 53 | export interface SendPaymentRequest { 54 | dest?: Buffer | string; 55 | amt?: number; 56 | amtMsat?: number; 57 | paymentHash?: Buffer | string; 58 | finalCltvDelta?: number; 59 | paymentRequest?: string; 60 | timeoutSeconds?: number; 61 | feeLimitSat?: number; 62 | feeLimitMsat?: number; 63 | outgoingChanId?: string; 64 | outgoingChanIds?: number[]; 65 | lastHopPubkey?: Buffer | string; 66 | cltvLimit?: number; 67 | routeHints?: RouteHint[]; 68 | destCustomRecords?: Array<[number, Buffer]> | string[]; 69 | allowSelfPayment?: boolean; 70 | destFeatures?: FeatureBit[]; 71 | maxParts?: number; 72 | noInflightUpdates?: boolean; 73 | } 74 | 75 | export interface TrackPaymentRequest { 76 | paymentHash: Buffer | string; 77 | noInflightUpdates?: boolean; 78 | } 79 | 80 | export interface RouteFeeRequest { 81 | dest: Buffer | string; 82 | amtSat: number; 83 | } 84 | 85 | export interface RouteFeeResponse { 86 | routingFeeMsat: number; 87 | timeLockDelay: number; 88 | } 89 | 90 | export interface SendToRouteReq { 91 | paymentHash: Buffer | string; 92 | route?: Route; 93 | } 94 | 95 | export interface ChanUpdate { 96 | signature: Buffer | string; 97 | chainHash: Buffer | string; 98 | chanId: number; 99 | timestamp: number; 100 | messageFlags: number; 101 | channelFlags: number; 102 | timeLockDelta: number; 103 | htlcMinimumMsat: number; 104 | baseFee: number; 105 | feeRate: number; 106 | htlcMaximumMsat: number; 107 | extraOpaqueData: Buffer | string; 108 | } 109 | 110 | export interface SendToRouteResponse { 111 | preimage: Buffer | string; 112 | failure?: Failure; 113 | } 114 | 115 | export interface PairHistory { 116 | nodeFrom: Buffer | string; 117 | nodeTo: Buffer | string; 118 | history?: PairData; 119 | } 120 | 121 | export interface PairData { 122 | failTime?: number; 123 | failAmtSat?: number; 124 | failAmtMsat?: number; 125 | successTime?: number; 126 | successAmtSat?: number; 127 | successAmtMsat?: number; 128 | } 129 | 130 | export interface QueryProbabilityRequest { 131 | fromNode?: Buffer | string; 132 | toNode?: Buffer | string; 133 | amtMsat?: number; 134 | } 135 | 136 | export interface QueryProbabilityResponse { 137 | probability: number; 138 | history?: PairData; 139 | } 140 | 141 | export interface QueryMissionControlResponse { 142 | pairs: PairHistory[]; 143 | } 144 | 145 | export interface BuildRouteRequest { 146 | amtMsat: number; 147 | finalCltvDelta?: number; 148 | outgoingChanId?: string; 149 | hopPubkeys: Array; 150 | } 151 | 152 | export interface BuildRouteResponse { 153 | route?: Route; 154 | } 155 | 156 | export interface HtlcInfo { 157 | incomingTimelock: number; 158 | outgoingTimelock: number; 159 | incomingAmtMsat: number; 160 | outgoingAmtMsat: number; 161 | } 162 | 163 | export interface ForwardEvent { 164 | info?: HtlcInfo; 165 | } 166 | 167 | export interface LinkFailEvent { 168 | info?: HtlcInfo; 169 | wireFailure: FailureCode; 170 | failureDetail: FailureDetail; 171 | failureString: string; 172 | } 173 | 174 | export interface HtlcEvent { 175 | incomingChannelId: number; 176 | outgoingChannelId: number; 177 | incomingHtlcId: number; 178 | outgoingHtlcId: number; 179 | timestampNs: number; 180 | eventType: HtlcEventType; 181 | forwardEvent?: ForwardEvent; 182 | forwardFailEvent?: {}; 183 | settleEvent?: {}; 184 | linkFailEvent?: LinkFailEvent; 185 | } 186 | 187 | export interface PaymentStatusUpdate { 188 | state: PaymentState; 189 | preimage?: Buffer | string; 190 | htlcs: HTLCAttempt[]; 191 | } 192 | 193 | export interface CircuitKey { 194 | chanId?: number; 195 | htlcId?: number; 196 | } 197 | 198 | export interface ForwardHtlcInterceptRequest { 199 | incomingCircuitKey?: CircuitKey; 200 | incomingAmountMsat?: number; 201 | incomingExpiry?: number; 202 | paymentHash?: Buffer | string; 203 | outgoingRequestedChanId?: number; 204 | outgoingAmountMsat?: number; 205 | outgoingExpiry?: number; 206 | customRecords?: Array<[number, Buffer | string]>; 207 | } 208 | 209 | export interface ForwardHtlcInterceptResponse { 210 | incomingCircuitKey?: CircuitKey; 211 | action: ResolveHoldForwardAction; 212 | preimage: Buffer | string; 213 | } 214 | 215 | /** 216 | * LND Router gRPC API Client 217 | */ 218 | export interface RouterRpc { 219 | /** 220 | * sendPaymentV2 attempts to route a payment described by the passed 221 | * PaymentRequest to the final destination. The call returns a stream of 222 | * payment updates. 223 | */ 224 | sendPaymentV2(args: SendPaymentRequest): Readable; 225 | 226 | /** 227 | * trackPaymentV2 returns an update stream for the payment identified by the 228 | * payment hash. 229 | */ 230 | trackPaymentV2(args: TrackPaymentRequest): Readable; 231 | 232 | /** 233 | * estimateRouteFee allows callers to obtain a lower bound w.r.t how much it 234 | * may cost to send an HTLC to the target end destination. 235 | */ 236 | estimateRouteFee(args: RouteFeeRequest): Promise; 237 | 238 | /** 239 | * sendToRoute attempts to make a payment via the specified route. This method 240 | * differs from SendPayment in that it allows users to specify a full route 241 | * manually. This can be used for things like rebalancing, and atomic swaps. 242 | */ 243 | sendToRoute(args: SendToRouteReq): Promise; 244 | 245 | /** 246 | * sendToRouteV2 attempts to make a payment via the specified route. This 247 | * method differs from SendPayment in that it allows users to specify a full 248 | * route manually. This can be used for things like rebalancing, and atomic 249 | * swaps. 250 | */ 251 | sendToRouteV2(args: SendToRouteReq): Promise; 252 | 253 | /** 254 | * resetMissionControl clears all mission control state and starts with a clean 255 | * slate. 256 | */ 257 | resetMissionControl(args?: {}): Promise<{}>; 258 | 259 | /** 260 | * queryMissionControl exposes the internal mission control state to callers. 261 | * It is a development feature. 262 | */ 263 | queryMissionControl(args?: {}): Promise; 264 | 265 | /** 266 | * queryProbability returns the current success probability estimate for a 267 | * given node pair and amount. 268 | */ 269 | queryProbability(args: QueryProbabilityRequest): Promise; 270 | 271 | /** 272 | * buildRoute builds a fully specified route based on a list of hop public 273 | * keys. It retrieves the relevant channel policies from the graph in order to 274 | * calculate the correct fees and time locks. 275 | */ 276 | buildRoute(args: BuildRouteRequest): Promise; 277 | 278 | /** 279 | * subscribeHtlcEvents creates a uni-directional stream from the server to 280 | * the client which delivers a stream of htlc events. 281 | */ 282 | subscribeHtlcEvents(args?: {}): Readable; 283 | 284 | /** 285 | * Deprecated, use sendPaymentV2. sendPayment attempts to route a payment described 286 | * by the passed PaymentRequest to the final destination. The call returns a stream of 287 | * payment status updates. 288 | */ 289 | sendPayment(args: SendPaymentRequest): Readable; 290 | 291 | /** 292 | * Deprecated, use trackPaymentV2. trackPayment returns an update stream for the payment 293 | * identified by the payment hash. 294 | */ 295 | trackPayment(args: TrackPaymentRequest): Readable; 296 | 297 | /** 298 | * htlcInterceptor dispatches a bi-directional streaming RPC in which 299 | * Forwarded HTLC requests are sent to the client and the client responds with 300 | * a boolean that tells LND if this htlc should be intercepted. 301 | * In case of interception, the htlc can be either settled, cancelled or 302 | * resumed later by using the ResolveHoldForward endpoint. 303 | */ 304 | htlcInterceptor(args?: ForwardHtlcInterceptRequest): Duplex; 305 | } 306 | -------------------------------------------------------------------------------- /test/rpc/signer-rpc.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import fs from 'fs'; 3 | import {join} from 'path'; 4 | import pkgDir from 'pkg-dir'; 5 | import {promisify} from 'util'; 6 | import packageJson from '../../package.json'; 7 | import { createSignRpc } from '../../src'; 8 | import { GrpcLoader } from '../../src/types'; 9 | import {grpcStub, SignerStub} from '../helpers/grpc-stub'; 10 | 11 | const {equal, fail} = assert; 12 | const unlink = promisify(fs.unlink); 13 | const writeFile = promisify(fs.writeFile); 14 | 15 | describe('SignRpc Factory', () => { 16 | const certStub = 'cert'; 17 | 18 | describe('TLS settings', () => { 19 | it('should throw an error when tls file not found', () => 20 | createSignRpc({tls: './not-a-file.cert'}) 21 | .then(() => fail('failed to reject invalid SSL cert')) 22 | .catch((e) => assert(e, 'SSL cert not found'))); 23 | 24 | it('should use configured `cert` over `tls` when provided', () => { 25 | const expected = Buffer.from('cert'); 26 | 27 | return createSignRpc({ 28 | cert: expected.toString(), 29 | tls: 'not-expected', 30 | grpc: grpcStub({ 31 | credentials: { 32 | createSsl: (actual) => { 33 | assert(actual instanceof Buffer, 'cert is a Buffer instance'); 34 | 35 | equal( 36 | actual.toString(), 37 | expected.toString(), 38 | 'configures bufferized `cert` value', 39 | ); 40 | }, 41 | }, 42 | }), 43 | }); 44 | }); 45 | 46 | it('should use configured `tls` when provided', (done) => { 47 | const expected = 'test-tls.cert'; 48 | 49 | createSignRpc({ 50 | tls: expected, 51 | grpc: grpcStub({ 52 | credentials: { 53 | createSsl: (actual) => { 54 | equal(actual, expected, 'configures provided `tls` value'); 55 | }, 56 | }, 57 | loadPackageDefinition: () => { 58 | throw new Error('force error'); 59 | }, 60 | }), 61 | }) 62 | .then(fail) 63 | .catch(() => done()); 64 | }); 65 | 66 | it('should default to a system lnd SSL cert when unconfigured', (done) => { 67 | createSignRpc({ 68 | grpc: grpcStub({ 69 | credentials: { 70 | createSsl: (cert) => { 71 | assert(/lnd\.conf$/.test(cert), 'used system SSL cert file path'); 72 | }, 73 | }, 74 | loadPackageDefinition: () => { 75 | throw new Error('force error'); 76 | }, 77 | }), 78 | }) 79 | .then(fail) 80 | .catch(() => done()); 81 | }); 82 | 83 | it('should allow opting out of certificate pinning', (done) => { 84 | createSignRpc({ 85 | tls: false, // opt out 86 | grpc: grpcStub({ 87 | credentials: { 88 | createSsl: (cert) => { 89 | assert( 90 | typeof cert === 'undefined', 91 | 'opted out of SSL cert pinning', 92 | ); 93 | }, 94 | }, 95 | loadPackageDefinition: () => { 96 | throw new Error('force error'); 97 | }, 98 | }), 99 | }) 100 | .then(fail) 101 | .catch(() => done()); 102 | }); 103 | 104 | it('should combine credentials when macaroon present', async () => { 105 | let tests = 0; 106 | const expSslCreds = {}; 107 | const expMacaroonCreds = {}; 108 | 109 | try { 110 | await createSignRpc({ 111 | cert: '123', 112 | macaroon: '746573740a', 113 | grpc: grpcStub({ 114 | credentials: { 115 | createSsl: () => expSslCreds, 116 | createFromMetadataGenerator: (cb) => { 117 | cb({}, () => { /* noop */ }); 118 | return expMacaroonCreds; 119 | }, 120 | combineChannelCredentials: (sslCreds, macaroonCreds) => { 121 | equal(sslCreds, expSslCreds, 'has expected ssl credentials'); 122 | equal( 123 | macaroonCreds, 124 | expMacaroonCreds, 125 | 'has expected macaroon credentials', 126 | ); 127 | tests++; 128 | }, 129 | }, 130 | loadPackageDefinition: () => { 131 | throw new Error('force error'); 132 | }, 133 | }), 134 | }); 135 | } catch (e) { /* noop */ } 136 | 137 | equal(tests, 1, 'called `combineChannelCredentials`'); 138 | }); 139 | }); 140 | 141 | describe('macaroon settings', () => { 142 | let macaroonDest; 143 | const macaroonTxt = 'test'; 144 | const macaroonHex = '74657374'; 145 | 146 | before(async () => { 147 | const root = await pkgDir(__dirname); 148 | macaroonDest = join(root, 'test.macaroon'); 149 | await writeFile(macaroonDest, macaroonTxt, 'utf8'); 150 | }); 151 | 152 | after(async () => { 153 | await unlink(macaroonDest); 154 | }); 155 | 156 | it('should set hex encoded macaroon from `macaroonPath`', async () => { 157 | let tests = 0; 158 | 159 | const CustomMetadataStub = () => { /* noop */ }; 160 | CustomMetadataStub.prototype.add = (_, macaroon) => { 161 | tests++; 162 | equal(macaroon, macaroonHex); 163 | }; 164 | 165 | try { 166 | await createSignRpc({ 167 | cert: '123', 168 | macaroonPath: macaroonDest, 169 | grpc: grpcStub({ 170 | Metadata: CustomMetadataStub, 171 | loadPackageDefinition: () => { 172 | throw new Error('force error'); 173 | }, 174 | }), 175 | }); 176 | } catch (e) { /* noop */ } 177 | 178 | equal(tests, 1, 'called Metadata.add with macaroon'); 179 | }); 180 | 181 | it('should set macaroon from `macaroon` hex string', async () => { 182 | let tests = 0; 183 | 184 | const CustomMetadataStub = () => { /* noop */ }; 185 | CustomMetadataStub.prototype.add = (_, macaroon) => { 186 | tests++; 187 | equal(macaroon, macaroonHex); 188 | }; 189 | 190 | try { 191 | await createSignRpc({ 192 | cert: '123', 193 | macaroon: macaroonHex, 194 | grpc: grpcStub({ 195 | Metadata: CustomMetadataStub, 196 | loadPackageDefinition: () => { 197 | throw new Error('force error'); 198 | }, 199 | }), 200 | }); 201 | } catch (e) { /* noop */ } 202 | 203 | equal(tests, 1, 'called Metadata.add with macaroon'); 204 | }); 205 | }); 206 | 207 | describe('grpc lnd/proto instantiation', () => { 208 | // eslint-disable-next-line max-len 209 | it('should load `./lnd/lnd_version/signrpc/signer.proto` filename via `grpc.loadSync()`', async () => { 210 | const root = await pkgDir(__dirname); 211 | const expected = join( 212 | root, 213 | `lnd/${packageJson.config['lnd-release-tag']}/signrpc/signer.proto`, 214 | ); 215 | 216 | return createSignRpc({ 217 | grpcLoader: { 218 | loadSync(actual) { 219 | equal(actual, expected, 'loaded generated `signer.proto` via load'); 220 | return {}; 221 | }, 222 | } as unknown as GrpcLoader, 223 | grpc: grpcStub(), 224 | cert: certStub, 225 | }); 226 | }); 227 | 228 | it('should default to server `localhost:10001`', () => { 229 | const expected = 'localhost:10001'; 230 | 231 | return createSignRpc({ 232 | grpc: grpcStub({}, (actual) => { 233 | equal(actual, expected, 'defaults to expected server'); 234 | return new SignerStub(); 235 | }), 236 | cert: certStub, 237 | }); 238 | }); 239 | 240 | it('should connect to a configured server address', () => { 241 | const expected = 'localhost:30001'; 242 | 243 | return createSignRpc({ 244 | server: expected, 245 | grpc: grpcStub({}, (actual) => { 246 | equal(actual, expected, 'recieved configured server'); 247 | return new SignerStub(); 248 | }), 249 | cert: certStub, 250 | }); 251 | }); 252 | }); 253 | 254 | describe('proxy instance', () => { 255 | it('should provide access to GRPC Package Definition', () => { 256 | return createSignRpc<{ description: object }>({ 257 | grpc: grpcStub(), 258 | cert: certStub, 259 | }).then((signrpc) => { 260 | equal(typeof signrpc.description, 'object'); 261 | }); 262 | }); 263 | 264 | it('should provide access to the signer instance', () => { 265 | const expected = {}; 266 | return createSignRpc<{ signer: object }>({ 267 | grpc: grpcStub(), 268 | signer: expected, 269 | cert: certStub}, 270 | ).then( 271 | (signrpc) => { 272 | equal(signrpc.signer, expected); 273 | }, 274 | ); 275 | }); 276 | 277 | it('should provide all signer methods top-level', () => { 278 | return createSignRpc<{ test: () => void }>({ 279 | grpc: grpcStub(), 280 | signer: {test: () => { /* noop */ }}, 281 | cert: certStub, 282 | }).then((signrpc) => { 283 | equal(typeof signrpc.test, 'function'); 284 | }); 285 | }); 286 | }); 287 | }); 288 | 289 | process.on('unhandledRejection', () => { /* noop */ }); 290 | -------------------------------------------------------------------------------- /test/rpc/router-rpc.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import fs from 'fs'; 3 | import {join} from 'path'; 4 | import pkgDir from 'pkg-dir'; 5 | import {promisify} from 'util'; 6 | import packageJson from '../../package.json'; 7 | import { createRouterRpc } from '../../src'; 8 | import { GrpcLoader } from '../../src/types'; 9 | import {grpcStub, RouterStub} from '../helpers/grpc-stub'; 10 | 11 | const {equal, fail} = assert; 12 | const unlink = promisify(fs.unlink); 13 | const writeFile = promisify(fs.writeFile); 14 | 15 | describe('RouterRpc Factory', () => { 16 | const certStub = 'cert'; 17 | 18 | describe('TLS settings', () => { 19 | it('should throw an error when tls file not found', () => 20 | createRouterRpc({tls: './not-a-file.cert'}) 21 | .then(() => fail('failed to reject invalid SSL cert')) 22 | .catch((e) => assert(e, 'SSL cert not found'))); 23 | 24 | it('should use configured `cert` over `tls` when provided', () => { 25 | const expected = Buffer.from('cert'); 26 | 27 | return createRouterRpc({ 28 | cert: expected.toString(), 29 | tls: 'not-expected', 30 | grpc: grpcStub({ 31 | credentials: { 32 | createSsl: (actual) => { 33 | assert(actual instanceof Buffer, 'cert is a Buffer instance'); 34 | 35 | equal( 36 | actual.toString(), 37 | expected.toString(), 38 | 'configures bufferized `cert` value', 39 | ); 40 | }, 41 | }, 42 | }), 43 | }); 44 | }); 45 | 46 | it('should use configured `tls` when provided', (done) => { 47 | const expected = 'test-tls.cert'; 48 | 49 | createRouterRpc({ 50 | tls: expected, 51 | grpc: grpcStub({ 52 | credentials: { 53 | createSsl: (actual) => { 54 | equal(actual, expected, 'configures provided `tls` value'); 55 | }, 56 | }, 57 | loadPackageDefinition: () => { 58 | throw new Error('force error'); 59 | }, 60 | }), 61 | }) 62 | .then(fail) 63 | .catch(() => done()); 64 | }); 65 | 66 | it('should default to a system lnd SSL cert when unconfigured', (done) => { 67 | createRouterRpc({ 68 | grpc: grpcStub({ 69 | credentials: { 70 | createSsl: (cert) => { 71 | assert(/lnd\.conf$/.test(cert), 'used system SSL cert file path'); 72 | }, 73 | }, 74 | loadPackageDefinition: () => { 75 | throw new Error('force error'); 76 | }, 77 | }), 78 | }) 79 | .then(fail) 80 | .catch(() => done()); 81 | }); 82 | 83 | it('should allow opting out of certificate pinning', (done) => { 84 | createRouterRpc({ 85 | tls: false, // opt out 86 | grpc: grpcStub({ 87 | credentials: { 88 | createSsl: (cert) => { 89 | assert( 90 | typeof cert === 'undefined', 91 | 'opted out of SSL cert pinning', 92 | ); 93 | }, 94 | }, 95 | loadPackageDefinition: () => { 96 | throw new Error('force error'); 97 | }, 98 | }), 99 | }) 100 | .then(fail) 101 | .catch(() => done()); 102 | }); 103 | 104 | it('should combine credentials when macaroon present', async () => { 105 | let tests = 0; 106 | const expSslCreds = {}; 107 | const expMacaroonCreds = {}; 108 | 109 | try { 110 | await createRouterRpc({ 111 | cert: '123', 112 | macaroon: '746573740a', 113 | grpc: grpcStub({ 114 | credentials: { 115 | createSsl: () => expSslCreds, 116 | createFromMetadataGenerator: (cb) => { 117 | cb({}, () => { /* noop */ }); 118 | return expMacaroonCreds; 119 | }, 120 | combineChannelCredentials: (sslCreds, macaroonCreds) => { 121 | equal(sslCreds, expSslCreds, 'has expected ssl credentials'); 122 | equal( 123 | macaroonCreds, 124 | expMacaroonCreds, 125 | 'has expected macaroon credentials', 126 | ); 127 | tests++; 128 | }, 129 | }, 130 | loadPackageDefinition: () => { 131 | throw new Error('force error'); 132 | }, 133 | }), 134 | }); 135 | } catch (e) { /* noop */ } 136 | 137 | equal(tests, 1, 'called `combineChannelCredentials`'); 138 | }); 139 | }); 140 | 141 | describe('macaroon settings', () => { 142 | let macaroonDest; 143 | const macaroonTxt = 'test'; 144 | const macaroonHex = '74657374'; 145 | 146 | before(async () => { 147 | const root = await pkgDir(__dirname); 148 | macaroonDest = join(root, 'test.macaroon'); 149 | await writeFile(macaroonDest, macaroonTxt, 'utf8'); 150 | }); 151 | 152 | after(async () => { 153 | await unlink(macaroonDest); 154 | }); 155 | 156 | it('should set hex encoded macaroon from `macaroonPath`', async () => { 157 | let tests = 0; 158 | 159 | const CustomMetadataStub = () => { /* noop */ }; 160 | CustomMetadataStub.prototype.add = (_, macaroon) => { 161 | tests++; 162 | equal(macaroon, macaroonHex); 163 | }; 164 | 165 | try { 166 | await createRouterRpc({ 167 | cert: '123', 168 | macaroonPath: macaroonDest, 169 | grpc: grpcStub({ 170 | Metadata: CustomMetadataStub, 171 | loadPackageDefinition: () => { 172 | throw new Error('force error'); 173 | }, 174 | }), 175 | }); 176 | } catch (e) { /* noop */ } 177 | 178 | equal(tests, 1, 'called Metadata.add with macaroon'); 179 | }); 180 | 181 | it('should set macaroon from `macaroon` hex string', async () => { 182 | let tests = 0; 183 | 184 | const CustomMetadataStub = () => { /* noop */ }; 185 | CustomMetadataStub.prototype.add = (_, macaroon) => { 186 | tests++; 187 | equal(macaroon, macaroonHex); 188 | }; 189 | 190 | try { 191 | await createRouterRpc({ 192 | cert: '123', 193 | macaroon: macaroonHex, 194 | grpc: grpcStub({ 195 | Metadata: CustomMetadataStub, 196 | loadPackageDefinition: () => { 197 | throw new Error('force error'); 198 | }, 199 | }), 200 | }); 201 | } catch (e) { /* noop */ } 202 | 203 | equal(tests, 1, 'called Metadata.add with macaroon'); 204 | }); 205 | }); 206 | 207 | describe('grpc lnd/proto instantiation', () => { 208 | // eslint-disable-next-line max-len 209 | it('should load `./lnd/lnd_version/routerrpc/router.proto` filename via `grpc.loadSync()`', async () => { 210 | const root = await pkgDir(__dirname); 211 | const expected = join( 212 | root, 213 | `lnd/${packageJson.config['lnd-release-tag']}/routerrpc/router.proto`, 214 | ); 215 | 216 | return createRouterRpc({ 217 | grpcLoader: { 218 | loadSync(actual) { 219 | equal(actual, expected, 'loaded generated `router.proto` via load'); 220 | return {}; 221 | }, 222 | } as unknown as GrpcLoader, 223 | grpc: grpcStub(), 224 | cert: certStub, 225 | }); 226 | }); 227 | 228 | it('should default to server `localhost:10001`', () => { 229 | const expected = 'localhost:10001'; 230 | 231 | return createRouterRpc({ 232 | grpc: grpcStub({}, (actual) => { 233 | equal(actual, expected, 'defaults to expected server'); 234 | return new RouterStub(); 235 | }), 236 | cert: certStub, 237 | }); 238 | }); 239 | 240 | it('should connect to a configured server address', () => { 241 | const expected = 'localhost:30001'; 242 | 243 | return createRouterRpc({ 244 | server: expected, 245 | grpc: grpcStub({}, (actual) => { 246 | equal(actual, expected, 'recieved configured server'); 247 | return new RouterStub(); 248 | }), 249 | cert: certStub, 250 | }); 251 | }); 252 | }); 253 | 254 | describe('proxy instance', () => { 255 | it('should provide access to GRPC Package Definition', () => { 256 | return createRouterRpc<{ description: object }>({ 257 | grpc: grpcStub(), 258 | cert: certStub, 259 | }).then((routerrpc) => { 260 | equal(typeof routerrpc.description, 'object'); 261 | }); 262 | }); 263 | 264 | it('should provide access to the router instance', () => { 265 | const expected = {}; 266 | return createRouterRpc<{ router: object }>({ 267 | grpc: grpcStub(), 268 | router: expected, 269 | cert: certStub}, 270 | ).then( 271 | (routerrpc) => { 272 | equal(routerrpc.router, expected); 273 | }, 274 | ); 275 | }); 276 | 277 | it('should provide all router methods top-level', () => { 278 | return createRouterRpc<{ test: () => void }>({ 279 | grpc: grpcStub(), 280 | router: {test: () => { /* noop */ }}, 281 | cert: certStub, 282 | }).then((routerrpc) => { 283 | equal(typeof routerrpc.test, 'function'); 284 | }); 285 | }); 286 | }); 287 | }); 288 | 289 | process.on('unhandledRejection', () => { /* noop */ }); 290 | -------------------------------------------------------------------------------- /test/rpc/wallet-rpc.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import fs from 'fs'; 3 | import {join} from 'path'; 4 | import pkgDir from 'pkg-dir'; 5 | import {promisify} from 'util'; 6 | import packageJson from '../../package.json'; 7 | import { createWalletRpc } from '../../src'; 8 | import { GrpcLoader } from '../../src/types'; 9 | import {grpcStub, WalletKitStub} from '../helpers/grpc-stub'; 10 | 11 | const {equal, fail} = assert; 12 | const unlink = promisify(fs.unlink); 13 | const writeFile = promisify(fs.writeFile); 14 | 15 | describe('WalletRpc Factory', () => { 16 | const certStub = 'cert'; 17 | 18 | describe('TLS settings', () => { 19 | it('should throw an error when tls file not found', () => 20 | createWalletRpc({tls: './not-a-file.cert'}) 21 | .then(() => fail('failed to reject invalid SSL cert')) 22 | .catch((e) => assert(e, 'SSL cert not found'))); 23 | 24 | it('should use configured `cert` over `tls` when provided', () => { 25 | const expected = Buffer.from('cert'); 26 | 27 | return createWalletRpc({ 28 | cert: expected.toString(), 29 | tls: 'not-expected', 30 | grpc: grpcStub({ 31 | credentials: { 32 | createSsl: (actual) => { 33 | assert(actual instanceof Buffer, 'cert is a Buffer instance'); 34 | 35 | equal( 36 | actual.toString(), 37 | expected.toString(), 38 | 'configures bufferized `cert` value', 39 | ); 40 | }, 41 | }, 42 | }), 43 | }); 44 | }); 45 | 46 | it('should use configured `tls` when provided', (done) => { 47 | const expected = 'test-tls.cert'; 48 | 49 | createWalletRpc({ 50 | tls: expected, 51 | grpc: grpcStub({ 52 | credentials: { 53 | createSsl: (actual) => { 54 | equal(actual, expected, 'configures provided `tls` value'); 55 | }, 56 | }, 57 | loadPackageDefinition: () => { 58 | throw new Error('force error'); 59 | }, 60 | }), 61 | }) 62 | .then(fail) 63 | .catch(() => done()); 64 | }); 65 | 66 | it('should default to a system lnd SSL cert when unconfigured', (done) => { 67 | createWalletRpc({ 68 | grpc: grpcStub({ 69 | credentials: { 70 | createSsl: (cert) => { 71 | assert(/lnd\.conf$/.test(cert), 'used system SSL cert file path'); 72 | }, 73 | }, 74 | loadPackageDefinition: () => { 75 | throw new Error('force error'); 76 | }, 77 | }), 78 | }) 79 | .then(fail) 80 | .catch(() => done()); 81 | }); 82 | 83 | it('should allow opting out of certificate pinning', (done) => { 84 | createWalletRpc({ 85 | tls: false, // opt out 86 | grpc: grpcStub({ 87 | credentials: { 88 | createSsl: (cert) => { 89 | assert( 90 | typeof cert === 'undefined', 91 | 'opted out of SSL cert pinning', 92 | ); 93 | }, 94 | }, 95 | loadPackageDefinition: () => { 96 | throw new Error('force error'); 97 | }, 98 | }), 99 | }) 100 | .then(fail) 101 | .catch(() => done()); 102 | }); 103 | 104 | it('should combine credentials when macaroon present', async () => { 105 | let tests = 0; 106 | const expSslCreds = {}; 107 | const expMacaroonCreds = {}; 108 | 109 | try { 110 | await createWalletRpc({ 111 | cert: '123', 112 | macaroon: '746573740a', 113 | grpc: grpcStub({ 114 | credentials: { 115 | createSsl: () => expSslCreds, 116 | createFromMetadataGenerator: (cb) => { 117 | cb({}, () => { /* noop */ }); 118 | return expMacaroonCreds; 119 | }, 120 | combineChannelCredentials: (sslCreds, macaroonCreds) => { 121 | equal(sslCreds, expSslCreds, 'has expected ssl credentials'); 122 | equal( 123 | macaroonCreds, 124 | expMacaroonCreds, 125 | 'has expected macaroon credentials', 126 | ); 127 | tests++; 128 | }, 129 | }, 130 | loadPackageDefinition: () => { 131 | throw new Error('force error'); 132 | }, 133 | }), 134 | }); 135 | } catch (e) { /* noop */ } 136 | 137 | equal(tests, 1, 'called `combineChannelCredentials`'); 138 | }); 139 | }); 140 | 141 | describe('macaroon settings', () => { 142 | let macaroonDest; 143 | const macaroonTxt = 'test'; 144 | const macaroonHex = '74657374'; 145 | 146 | before(async () => { 147 | const root = await pkgDir(__dirname); 148 | macaroonDest = join(root, 'test.macaroon'); 149 | await writeFile(macaroonDest, macaroonTxt, 'utf8'); 150 | }); 151 | 152 | after(async () => { 153 | await unlink(macaroonDest); 154 | }); 155 | 156 | it('should set hex encoded macaroon from `macaroonPath`', async () => { 157 | let tests = 0; 158 | 159 | const CustomMetadataStub = () => { /* noop */ }; 160 | CustomMetadataStub.prototype.add = (_, macaroon) => { 161 | tests++; 162 | equal(macaroon, macaroonHex); 163 | }; 164 | 165 | try { 166 | await createWalletRpc({ 167 | cert: '123', 168 | macaroonPath: macaroonDest, 169 | grpc: grpcStub({ 170 | Metadata: CustomMetadataStub, 171 | loadPackageDefinition: () => { 172 | throw new Error('force error'); 173 | }, 174 | }), 175 | }); 176 | } catch (e) { /* noop */ } 177 | 178 | equal(tests, 1, 'called Metadata.add with macaroon'); 179 | }); 180 | 181 | it('should set macaroon from `macaroon` hex string', async () => { 182 | let tests = 0; 183 | 184 | const CustomMetadataStub = () => { /* noop */ }; 185 | CustomMetadataStub.prototype.add = (_, macaroon) => { 186 | tests++; 187 | equal(macaroon, macaroonHex); 188 | }; 189 | 190 | try { 191 | await createWalletRpc({ 192 | cert: '123', 193 | macaroon: macaroonHex, 194 | grpc: grpcStub({ 195 | Metadata: CustomMetadataStub, 196 | loadPackageDefinition: () => { 197 | throw new Error('force error'); 198 | }, 199 | }), 200 | }); 201 | } catch (e) { /* noop */ } 202 | 203 | equal(tests, 1, 'called Metadata.add with macaroon'); 204 | }); 205 | }); 206 | 207 | describe('grpc lnd/proto instantiation', () => { 208 | // eslint-disable-next-line max-len 209 | it('should load `./lnd/lnd_version/walletrpc/walletkit.proto` filename via `grpc.loadSync()`', async () => { 210 | const root = await pkgDir(__dirname); 211 | const expected = join( 212 | root, 213 | `lnd/${packageJson.config['lnd-release-tag']}/walletrpc/walletkit.proto`, 214 | ); 215 | 216 | return createWalletRpc({ 217 | grpcLoader: { 218 | loadSync(actual) { 219 | equal(actual, expected, 'loaded generated `walletkit.proto` via load'); 220 | return {}; 221 | }, 222 | } as unknown as GrpcLoader, 223 | grpc: grpcStub(), 224 | cert: certStub, 225 | }); 226 | }); 227 | 228 | it('should default to server `localhost:10001`', () => { 229 | const expected = 'localhost:10001'; 230 | 231 | return createWalletRpc({ 232 | grpc: grpcStub({}, (actual) => { 233 | equal(actual, expected, 'defaults to expected server'); 234 | return new WalletKitStub(); 235 | }), 236 | cert: certStub, 237 | }); 238 | }); 239 | 240 | it('should connect to a configured server address', () => { 241 | const expected = 'localhost:30001'; 242 | 243 | return createWalletRpc({ 244 | server: expected, 245 | grpc: grpcStub({}, (actual) => { 246 | equal(actual, expected, 'recieved configured server'); 247 | return new WalletKitStub(); 248 | }), 249 | cert: certStub, 250 | }); 251 | }); 252 | }); 253 | 254 | describe('proxy instance', () => { 255 | it('should provide access to GRPC Package Definition', () => { 256 | return createWalletRpc<{ description: object }>({ 257 | grpc: grpcStub(), 258 | cert: certStub, 259 | }).then((walletrpc) => { 260 | equal(typeof walletrpc.description, 'object'); 261 | }); 262 | }); 263 | 264 | it('should provide access to the walletKit instance', () => { 265 | const expected = {}; 266 | return createWalletRpc<{ walletKit: object }>({ 267 | grpc: grpcStub(), 268 | walletKit: expected, 269 | cert: certStub}, 270 | ).then( 271 | (walletrpc) => { 272 | equal(walletrpc.walletKit, expected); 273 | }, 274 | ); 275 | }); 276 | 277 | it('should provide all walletKit methods top-level', () => { 278 | return createWalletRpc<{ test: () => void }>({ 279 | grpc: grpcStub(), 280 | walletKit: {test: () => { /* noop */ }}, 281 | cert: certStub, 282 | }).then((walletrpc) => { 283 | equal(typeof walletrpc.test, 'function'); 284 | }); 285 | }); 286 | }); 287 | }); 288 | 289 | process.on('unhandledRejection', () => { /* noop */ }); 290 | -------------------------------------------------------------------------------- /test/rpc/chain-rpc.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import fs from 'fs'; 3 | import {join} from 'path'; 4 | import pkgDir from 'pkg-dir'; 5 | import {promisify} from 'util'; 6 | import packageJson from '../../package.json'; 7 | import { createChainRpc } from '../../src'; 8 | import { GrpcLoader } from '../../src/types'; 9 | import {ChainNotifierStub, grpcStub} from '../helpers/grpc-stub'; 10 | 11 | const {equal, fail} = assert; 12 | const unlink = promisify(fs.unlink); 13 | const writeFile = promisify(fs.writeFile); 14 | 15 | describe('ChainRpc Factory', () => { 16 | const certStub = 'cert'; 17 | 18 | describe('TLS settings', () => { 19 | it('should throw an error when tls file not found', () => 20 | createChainRpc({tls: './not-a-file.cert'}) 21 | .then(() => fail('failed to reject invalid SSL cert')) 22 | .catch((e) => assert(e, 'SSL cert not found'))); 23 | 24 | it('should use configured `cert` over `tls` when provided', () => { 25 | const expected = Buffer.from('cert'); 26 | 27 | return createChainRpc({ 28 | cert: expected.toString(), 29 | tls: 'not-expected', 30 | grpc: grpcStub({ 31 | credentials: { 32 | createSsl: (actual) => { 33 | assert(actual instanceof Buffer, 'cert is a Buffer instance'); 34 | 35 | equal( 36 | actual.toString(), 37 | expected.toString(), 38 | 'configures bufferized `cert` value', 39 | ); 40 | }, 41 | }, 42 | }), 43 | }); 44 | }); 45 | 46 | it('should use configured `tls` when provided', (done) => { 47 | const expected = 'test-tls.cert'; 48 | 49 | createChainRpc({ 50 | tls: expected, 51 | grpc: grpcStub({ 52 | credentials: { 53 | createSsl: (actual) => { 54 | equal(actual, expected, 'configures provided `tls` value'); 55 | }, 56 | }, 57 | loadPackageDefinition: () => { 58 | throw new Error('force error'); 59 | }, 60 | }), 61 | }) 62 | .then(fail) 63 | .catch(() => done()); 64 | }); 65 | 66 | it('should default to a system lnd SSL cert when unconfigured', (done) => { 67 | createChainRpc({ 68 | grpc: grpcStub({ 69 | credentials: { 70 | createSsl: (cert) => { 71 | assert(/lnd\.conf$/.test(cert), 'used system SSL cert file path'); 72 | }, 73 | }, 74 | loadPackageDefinition: () => { 75 | throw new Error('force error'); 76 | }, 77 | }), 78 | }) 79 | .then(fail) 80 | .catch(() => done()); 81 | }); 82 | 83 | it('should allow opting out of certificate pinning', (done) => { 84 | createChainRpc({ 85 | tls: false, // opt out 86 | grpc: grpcStub({ 87 | credentials: { 88 | createSsl: (cert) => { 89 | assert( 90 | typeof cert === 'undefined', 91 | 'opted out of SSL cert pinning', 92 | ); 93 | }, 94 | }, 95 | loadPackageDefinition: () => { 96 | throw new Error('force error'); 97 | }, 98 | }), 99 | }) 100 | .then(fail) 101 | .catch(() => done()); 102 | }); 103 | 104 | it('should combine credentials when macaroon present', async () => { 105 | let tests = 0; 106 | const expSslCreds = {}; 107 | const expMacaroonCreds = {}; 108 | 109 | try { 110 | await createChainRpc({ 111 | cert: '123', 112 | macaroon: '746573740a', 113 | grpc: grpcStub({ 114 | credentials: { 115 | createSsl: () => expSslCreds, 116 | createFromMetadataGenerator: (cb) => { 117 | cb({}, () => { /* noop */ }); 118 | return expMacaroonCreds; 119 | }, 120 | combineChannelCredentials: (sslCreds, macaroonCreds) => { 121 | equal(sslCreds, expSslCreds, 'has expected ssl credentials'); 122 | equal( 123 | macaroonCreds, 124 | expMacaroonCreds, 125 | 'has expected macaroon credentials', 126 | ); 127 | tests++; 128 | }, 129 | }, 130 | loadPackageDefinition: () => { 131 | throw new Error('force error'); 132 | }, 133 | }), 134 | }); 135 | } catch (e) { /* noop */ } 136 | 137 | equal(tests, 1, 'called `combineChannelCredentials`'); 138 | }); 139 | }); 140 | 141 | describe('macaroon settings', () => { 142 | let macaroonDest; 143 | const macaroonTxt = 'test'; 144 | const macaroonHex = '74657374'; 145 | 146 | before(async () => { 147 | const root = await pkgDir(__dirname); 148 | macaroonDest = join(root, 'test.macaroon'); 149 | await writeFile(macaroonDest, macaroonTxt, 'utf8'); 150 | }); 151 | 152 | after(async () => { 153 | await unlink(macaroonDest); 154 | }); 155 | 156 | it('should set hex encoded macaroon from `macaroonPath`', async () => { 157 | let tests = 0; 158 | 159 | const CustomMetadataStub = () => { /* noop */ }; 160 | CustomMetadataStub.prototype.add = (_, macaroon) => { 161 | tests++; 162 | equal(macaroon, macaroonHex); 163 | }; 164 | 165 | try { 166 | await createChainRpc({ 167 | cert: '123', 168 | macaroonPath: macaroonDest, 169 | grpc: grpcStub({ 170 | Metadata: CustomMetadataStub, 171 | loadPackageDefinition: () => { 172 | throw new Error('force error'); 173 | }, 174 | }), 175 | }); 176 | } catch (e) { /* noop */ } 177 | 178 | equal(tests, 1, 'called Metadata.add with macaroon'); 179 | }); 180 | 181 | it('should set macaroon from `macaroon` hex string', async () => { 182 | let tests = 0; 183 | 184 | const CustomMetadataStub = () => { /* noop */ }; 185 | CustomMetadataStub.prototype.add = (_, macaroon) => { 186 | tests++; 187 | equal(macaroon, macaroonHex); 188 | }; 189 | 190 | try { 191 | await createChainRpc({ 192 | cert: '123', 193 | macaroon: macaroonHex, 194 | grpc: grpcStub({ 195 | Metadata: CustomMetadataStub, 196 | loadPackageDefinition: () => { 197 | throw new Error('force error'); 198 | }, 199 | }), 200 | }); 201 | } catch (e) { /* noop */ } 202 | 203 | equal(tests, 1, 'called Metadata.add with macaroon'); 204 | }); 205 | }); 206 | 207 | describe('grpc lnd/proto instantiation', () => { 208 | // eslint-disable-next-line max-len 209 | it('should load `./lnd/lnd_version/chainrpc/chainnotifier.proto` filename via `grpc.loadSync()`', async () => { 210 | const root = await pkgDir(__dirname); 211 | const expected = join( 212 | root, 213 | `lnd/${packageJson.config['lnd-release-tag']}/chainrpc/chainnotifier.proto`, 214 | ); 215 | 216 | return createChainRpc({ 217 | grpcLoader: { 218 | loadSync(actual) { 219 | equal(actual, expected, 'loaded generated `chainnotifier.proto` via load'); 220 | return {}; 221 | }, 222 | } as unknown as GrpcLoader, 223 | grpc: grpcStub(), 224 | cert: certStub, 225 | }); 226 | }); 227 | 228 | it('should default to server `localhost:10001`', () => { 229 | const expected = 'localhost:10001'; 230 | 231 | return createChainRpc({ 232 | grpc: grpcStub({}, (actual) => { 233 | equal(actual, expected, 'defaults to expected server'); 234 | return new ChainNotifierStub(); 235 | }), 236 | cert: certStub, 237 | }); 238 | }); 239 | 240 | it('should connect to a configured server address', () => { 241 | const expected = 'localhost:30001'; 242 | 243 | return createChainRpc({ 244 | server: expected, 245 | grpc: grpcStub({}, (actual) => { 246 | equal(actual, expected, 'recieved configured server'); 247 | return new ChainNotifierStub(); 248 | }), 249 | cert: certStub, 250 | }); 251 | }); 252 | }); 253 | 254 | describe('proxy instance', () => { 255 | it('should provide access to GRPC Package Definition', () => { 256 | return createChainRpc<{ description: object }>({ 257 | grpc: grpcStub(), 258 | cert: certStub, 259 | }).then((chainrpc) => { 260 | equal(typeof chainrpc.description, 'object'); 261 | }); 262 | }); 263 | 264 | it('should provide access to the chain notifier instance', () => { 265 | const expected = {}; 266 | return createChainRpc<{ chainNotifier: object }>({ 267 | grpc: grpcStub(), 268 | chainNotifier: expected, 269 | cert: certStub}, 270 | ).then( 271 | (chainrpc) => { 272 | equal(chainrpc.chainNotifier, expected); 273 | }, 274 | ); 275 | }); 276 | 277 | it('should provide all chain notifier methods top-level', () => { 278 | return createChainRpc<{ test: () => void }>({ 279 | grpc: grpcStub(), 280 | chainNotifier: {test: () => { /* noop */ }}, 281 | cert: certStub, 282 | }).then((chainrpc) => { 283 | equal(typeof chainrpc.test, 'function'); 284 | }); 285 | }); 286 | }); 287 | }); 288 | 289 | process.on('unhandledRejection', () => { /* noop */ }); 290 | --------------------------------------------------------------------------------