Skip to content

Commit

Permalink
feat(express): lightning wallet creation step two
Browse files Browse the repository at this point in the history
initialize signer lnd and update platform.

Ticket: BTC-1356
  • Loading branch information
saravanan7mani committed Sep 2, 2024
1 parent b293ed9 commit a5b1f23
Show file tree
Hide file tree
Showing 24 changed files with 987 additions and 18 deletions.
5 changes: 4 additions & 1 deletion modules/express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
},
"dependencies": {
"@bitgo/sdk-core": "^28.2.0",
"@bitgo/utxo-lib": "^10.2.0",
"argparse": "^1.0.10",
"bitgo": "^39.1.1",
"bluebird": "^3.5.3",
Expand All @@ -48,7 +49,9 @@
"express": "^4.17.3",
"lodash": "^4.17.20",
"morgan": "^1.9.1",
"superagent": "^9.0.1"
"superagent": "^9.0.1",
"io-ts": "2.1.3",
"io-ts-types": "0.5.16"
},
"devDependencies": {
"@bitgo/public-types": "2.33.4",
Expand Down
4 changes: 4 additions & 0 deletions modules/express/src/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,8 @@ parser.addArgument(['--signerMode'], {
parser.addArgument(['--signerFileSystemPath'], {
help: 'Local path specifying where an Express signer machine keeps encrypted user private keys.',
});

parser.addArgument(['--lightningSignerFileSystemPath'], {
help: 'Local path specifying where an Express machine keeps lightning signer urls.',
});
export const args = () => parser.parseArgs();
11 changes: 11 additions & 0 deletions modules/express/src/clientRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ import { Config } from './config';
import { ApiResponseError } from './errors';
import { promises as fs } from 'fs';
import { retryPromise } from './retryPromise';
import {
handleCreateSignerMacaroon,
handleGetLightningWalletState,
handleInitLightningWallet,
} from './lightning/lightningRoutes';

const { version } = require('bitgo/package.json');
const pjson = require('../package.json');
Expand Down Expand Up @@ -1651,3 +1656,9 @@ export function setupSigningRoutes(app: express.Application, config: Config): vo
promiseWrapper(handleV2OFCSignPayloadInExtSigningMode)
);
}

export function setupLightningRoutes(app: express.Application, config: Config): void {
app.post('/api/v2/:coin/initwallet', parseBody, prepareBitGo(config), promiseWrapper(handleInitLightningWallet));
app.post('/api/v2/:coin/signermacaroon', parseBody, prepareBitGo(config), promiseWrapper(handleCreateSignerMacaroon));
app.get('/api/v2/:coin/wallet/:id/state', prepareBitGo(config), promiseWrapper(handleGetLightningWalletState));
}
6 changes: 5 additions & 1 deletion modules/express/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface Config {
externalSignerUrl?: string;
signerMode?: boolean;
signerFileSystemPath?: string;
lightningSignerFileSystemPath?: string;
}

export const ArgConfig = (args): Partial<Config> => ({
Expand All @@ -59,6 +60,7 @@ export const ArgConfig = (args): Partial<Config> => ({
externalSignerUrl: args.externalSignerUrl,
signerMode: args.signerMode,
signerFileSystemPath: args.signerFileSystemPath,
lightningSignerFileSystemPath: args.lightningSignerFileSystemPath,
});

export const EnvConfig = (): Partial<Config> => ({
Expand All @@ -80,6 +82,7 @@ export const EnvConfig = (): Partial<Config> => ({
externalSignerUrl: readEnvVar('BITGO_EXTERNAL_SIGNER_URL'),
signerMode: readEnvVar('BITGO_SIGNER_MODE') ? true : undefined,
signerFileSystemPath: readEnvVar('BITGO_SIGNER_FILE_SYSTEM_PATH'),
lightningSignerFileSystemPath: readEnvVar('BITGO_LIGHTNING_SIGNER_FILE_SYSTEM_PATH'),
});

export const DefaultConfig: Config = {
Expand All @@ -102,7 +105,7 @@ export const DefaultConfig: Config = {
* @param url
* @return {string}
*/
function _forceSecureUrl(url: string): string {
export function _forceSecureUrl(url: string): string {
const regex = new RegExp(/(^\w+:|^)\/\//);
if (regex.test(url)) {
return url.replace(/(^\w+:|^)\/\//, 'https://');
Expand Down Expand Up @@ -161,6 +164,7 @@ function mergeConfigs(...configs: Partial<Config>[]): Config {
externalSignerUrl,
signerMode: get('signerMode'),
signerFileSystemPath: get('signerFileSystemPath'),
lightningSignerFileSystemPath: get('lightningSignerFileSystemPath'),
};
}

Expand Down
6 changes: 6 additions & 0 deletions modules/express/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ export class ExternalSignerConfigError extends BitGoJsError {
super(message || 'External signer configuration is invalid');
}
}

export class LightningSignerConfigError extends BitGoJsError {
public constructor(message?: string) {
super(message || 'Lightning signer configuration is invalid');
}
}
37 changes: 30 additions & 7 deletions modules/express/src/expressApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import { Config, config } from './config';
const debug = debugLib('bitgo:express');

import { SSL_OP_NO_TLSv1 } from 'constants';
import { IpcError, NodeEnvironmentError, TlsConfigurationError, ExternalSignerConfigError } from './errors';
import {
IpcError,
NodeEnvironmentError,
TlsConfigurationError,
ExternalSignerConfigError,
LightningSignerConfigError,
} from './errors';

import { Environments } from 'bitgo';
import * as clientRoutes from './clientRoutes';
Expand Down Expand Up @@ -104,7 +110,7 @@ function createHttpServer(app: express.Application): http.Server {
*/
export function startup(config: Config, baseUri: string): () => void {
return function () {
const { env, ipc, customRootUri, customBitcoinNetwork, signerMode } = config;
const { env, ipc, customRootUri, customBitcoinNetwork, signerMode, lightningSignerFileSystemPath } = config;
/* eslint-disable no-console */
console.log('BitGo-Express running');
console.log(`Environment: ${env}`);
Expand All @@ -122,6 +128,9 @@ export function startup(config: Config, baseUri: string): () => void {
if (signerMode) {
console.log(`External signer mode: ${signerMode}`);
}
if (lightningSignerFileSystemPath) {
console.log(`Lightning signer file system path: ${lightningSignerFileSystemPath}`);
}
/* eslint-enable no-console */
};
}
Expand Down Expand Up @@ -156,13 +165,13 @@ export function createBaseUri(config: Config): string {
}

/**
* Check the that the json file containing the external signer private key exists
* Check the that the json file exists
* @param path
*/
function checkSignerPrvPath(path: string) {
function checkJsonFilePath(path: string) {
try {
const privKeyFile = fs.readFileSync(path, { encoding: 'utf8' });
JSON.parse(privKeyFile);
const jsonFile = fs.readFileSync(path, { encoding: 'utf8' });
JSON.parse(jsonFile);
} catch (e) {
throw new Error(`Failed to parse ${path} - ${e.message}`);
}
Expand All @@ -186,6 +195,7 @@ function checkPreconditions(config: Config) {
externalSignerUrl,
signerMode,
signerFileSystemPath,
lightningSignerFileSystemPath,
} = config;

// warn or throw if the NODE_ENV is not production when BITGO_ENV is production - this can leak system info from express
Expand Down Expand Up @@ -229,15 +239,28 @@ function checkPreconditions(config: Config) {
);
}

if (signerMode !== undefined && lightningSignerFileSystemPath !== undefined) {
throw new LightningSignerConfigError(
'signerMode and lightningSignerFileSystemPath cannot be set at the same time.'
);
}

if (signerFileSystemPath !== undefined) {
checkSignerPrvPath(signerFileSystemPath);
checkJsonFilePath(signerFileSystemPath);
}

if (lightningSignerFileSystemPath !== undefined) {
checkJsonFilePath(lightningSignerFileSystemPath);
}
}

export function setupRoutes(app: express.Application, config: Config): void {
if (config.signerMode) {
clientRoutes.setupSigningRoutes(app, config);
} else {
if (config.lightningSignerFileSystemPath) {
clientRoutes.setupLightningRoutes(app, config);
}
clientRoutes.setupAPIRoutes(app, config);
}
}
Expand Down
77 changes: 77 additions & 0 deletions modules/express/src/lightning/codecs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as t from 'io-ts';
import { NonEmptyString } from 'io-ts-types';
import { IPCustomCodec } from '@bitgo/sdk-core';

export const WalletStateCodec = t.keyof({
NON_EXISTING: 1,
LOCKED: 1,
UNLOCKED: 1,
RPC_ACTIVE: 1,
SERVER_ACTIVE: 1,
WAITING_TO_START: 1,
});

export type WalletState = t.TypeOf<typeof WalletStateCodec>;

export const LightningSignerConfigCodec = t.type({
url: NonEmptyString,
tlsCert: NonEmptyString,
});

export type LightningSignerConfig = t.TypeOf<typeof LightningSignerConfigCodec>;

export const LightningSignerConfigsCodec = t.record(t.string, LightningSignerConfigCodec);

export type LightningSignerConfigs = t.TypeOf<typeof LightningSignerConfigsCodec>;

export const GetWalletStateResponseCodec = t.type(
{
state: WalletStateCodec,
},
'GetWalletStateResponse'
);

export type GetWalletStateResponse = t.TypeOf<typeof GetWalletStateResponseCodec>;

export const InitLightningWalletRequestCodec = t.strict(
{
walletId: NonEmptyString,
passphrase: NonEmptyString,
signerIP: IPCustomCodec,
signerTlsCert: NonEmptyString,
signerTlsKey: NonEmptyString,
expressIP: IPCustomCodec,
},
'InitLightningWalletRequest'
);

export type InitLightningWalletRequest = t.TypeOf<typeof InitLightningWalletRequestCodec>;

export const CreateSignerMacaroonRequestCodec = t.strict(
{
walletId: NonEmptyString,
passphrase: NonEmptyString,
watchOnlyIP: IPCustomCodec,
},
'CreateSignerMacaroonRequest'
);

export type CreateSignerMacaroonRequest = t.TypeOf<typeof CreateSignerMacaroonRequestCodec>;

export const InitWalletResponseCodec = t.type(
{
admin_macaroon: NonEmptyString,
},
'InitWalletResponse'
);

export type InitWalletResponse = t.TypeOf<typeof InitWalletResponseCodec>;

export const BakeMacaroonResponseCodec = t.type(
{
macaroon: NonEmptyString,
},
'BakeMacaroonResponse'
);

export type BakeMacaroonResponse = t.TypeOf<typeof BakeMacaroonResponseCodec>;
Loading

0 comments on commit a5b1f23

Please sign in to comment.