PHP WebShell

Текущая директория: /opt/BitGoJS/modules/express/dist/src/lightning

Просмотр файла: lightningSignerRoutes.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleUnlockLightningWallet = exports.handleGetLightningWalletState = exports.handleCreateSignerMacaroon = exports.handleInitLightningWallet = void 0;
const net_1 = require("net");
const sdk_core_1 = require("@bitgo/sdk-core");
const abstract_lightning_1 = require("@bitgo/abstract-lightning");
const utxolib = require("@bitgo/utxo-lib");
const buffer_1 = require("buffer");
const codecs_1 = require("./codecs");
const lndSignerClient_1 = require("./lndSignerClient");
const errors_1 = require("../errors");
async function createSignerMacaroon(lndSignerClient, header, watchOnlyIp) {
    const { macaroon } = await lndSignerClient.bakeMacaroon({ permissions: abstract_lightning_1.signerMacaroonPermissions }, header);
    const macaroonBase64 = watchOnlyIp
        ? (0, abstract_lightning_1.addIPCaveatToMacaroon)(buffer_1.Buffer.from(macaroon, 'hex').toString('base64'), watchOnlyIp)
        : undefined;
    return macaroonBase64 ? buffer_1.Buffer.from(macaroonBase64, 'base64').toString('hex') : macaroon;
}
function getSignerRootKey(passphrase, userMainnetEncryptedPrv, network, decrypt) {
    const userMainnetPrv = decrypt({
        password: passphrase,
        input: userMainnetEncryptedPrv,
    });
    return utxolib.bitgo.keyutil.convertExtendedKeyNetwork(userMainnetPrv, utxolib.networks.bitcoin, network);
}
function getMacaroonRootKey(passphrase, nodeAuthEncryptedPrv, decrypt) {
    const hdNode = utxolib.bip32.fromBase58(decrypt({ password: passphrase, input: nodeAuthEncryptedPrv }));
    if (!hdNode.privateKey) {
        throw new Error('nodeAuthEncryptedPrv is not a private key');
    }
    return hdNode.privateKey.toString('base64');
}
/**
 * Handle the request to initialise remote signer LND for a wallet.
 */
async function handleInitLightningWallet(req) {
    const bitgo = req.bitgo;
    const coinName = req.params.coin;
    if (!(0, abstract_lightning_1.isLightningCoinName)(coinName)) {
        throw new errors_1.ApiResponseError(`Invalid coin ${coinName}. This is not a lightning coin.`, 400);
    }
    const coin = bitgo.coin(coinName);
    const walletId = req.params.id;
    if (typeof walletId !== 'string') {
        throw new errors_1.ApiResponseError(`Invalid wallet id: ${walletId}`, 400);
    }
    const { passphrase, expressHost } = (0, sdk_core_1.decodeOrElse)(codecs_1.InitLightningWalletRequest.name, codecs_1.InitLightningWalletRequest, req.body, (_) => {
        // DON'T throw errors from decodeOrElse. It could leak sensitive information.
        throw new errors_1.ApiResponseError('Invalid request body to initialize lightning wallet', 400);
    });
    const wallet = await coin.wallets().get({ id: walletId, includeBalance: false });
    if (wallet.subType() !== 'lightningSelfCustody') {
        throw new errors_1.ApiResponseError(`not a self custodial lighting wallet ${walletId}`, 400);
    }
    const lndSignerClient = await lndSignerClient_1.LndSignerClient.create(walletId, req.config);
    const userKey = await (0, abstract_lightning_1.getLightningKeychain)(wallet);
    const userKeyEncryptedPrv = userKey.encryptedPrv;
    if (!userKeyEncryptedPrv) {
        throw new errors_1.ApiResponseError('Missing encryptedPrv in user keychain', 400);
    }
    const { nodeAuthKey } = await (0, abstract_lightning_1.getLightningAuthKeychains)(wallet);
    const nodeAuthKeyEncryptedPrv = nodeAuthKey.encryptedPrv;
    if (!nodeAuthKeyEncryptedPrv) {
        throw new errors_1.ApiResponseError('Missing encryptedPrv in node auth keychain', 400);
    }
    const network = (0, abstract_lightning_1.getUtxolibNetwork)(coin.getChain());
    const signerRootKey = getSignerRootKey(passphrase, userKeyEncryptedPrv, network, bitgo.decrypt);
    const macaroonRootKey = getMacaroonRootKey(passphrase, nodeAuthKeyEncryptedPrv, bitgo.decrypt);
    const { admin_macaroon: adminMacaroon } = await lndSignerClient.initWallet({
        // The passphrase at LND can only accommodate a base64 character set
        // For more information, see BTC-1851
        wallet_password: buffer_1.Buffer.from(passphrase).toString('base64'),
        extended_master_key: signerRootKey,
        macaroon_root_key: macaroonRootKey,
    });
    return await (0, abstract_lightning_1.updateWalletCoinSpecific)(wallet, {
        signerAdminMacaroon: expressHost && !!(0, net_1.isIP)(expressHost) ? (0, abstract_lightning_1.addIPCaveatToMacaroon)(adminMacaroon, expressHost) : adminMacaroon,
        watchOnlyAccounts: (0, abstract_lightning_1.createWatchOnly)(signerRootKey, network),
        passphrase,
    });
}
exports.handleInitLightningWallet = handleInitLightningWallet;
/**
 * Handle the request to create a signer macaroon from remote signer LND for a wallet.
 */
async function handleCreateSignerMacaroon(req) {
    const bitgo = req.bitgo;
    const coinName = req.params.coin;
    if (!(0, abstract_lightning_1.isLightningCoinName)(coinName)) {
        throw new errors_1.ApiResponseError(`Invalid coin to create signer macaroon: ${coinName}. Must be a lightning coin.`, 400);
    }
    const coin = bitgo.coin(coinName);
    const walletId = req.params.id;
    if (typeof walletId !== 'string') {
        throw new errors_1.ApiResponseError(`Invalid wallet id: ${walletId}`, 400);
    }
    const { passphrase, addIpCaveatToMacaroon } = (0, sdk_core_1.decodeOrElse)(codecs_1.CreateSignerMacaroonRequest.name, codecs_1.CreateSignerMacaroonRequest, req.body, (_) => {
        // DON'T throw errors from decodeOrElse. It could leak sensitive information.
        throw new errors_1.ApiResponseError('Invalid request body to create signer macaroon', 400);
    });
    const wallet = await coin.wallets().get({ id: walletId, includeBalance: false });
    if (wallet.subType() !== 'lightningSelfCustody') {
        throw new errors_1.ApiResponseError(`not a self custodial lighting wallet ${walletId}`, 400);
    }
    const watchOnlyIp = wallet.coinSpecific()?.watchOnlyExternalIp;
    if (!watchOnlyIp && addIpCaveatToMacaroon) {
        throw new errors_1.ApiResponseError('Cannot create signer macaroon because the external IP is not set. This can take some time. Contact support@bitgo.com if longer than 24 hours.', 400);
    }
    if (watchOnlyIp && !(0, net_1.isIP)(watchOnlyIp)) {
        throw new errors_1.ApiResponseError(`Invalid IP address: ${watchOnlyIp}. Contact support@bitgo.com`, 500);
    }
    const lndSignerClient = await lndSignerClient_1.LndSignerClient.create(walletId, req.config);
    const encryptedSignerAdminMacaroon = wallet.coinSpecific()?.encryptedSignerAdminMacaroon;
    if (!encryptedSignerAdminMacaroon) {
        throw new errors_1.ApiResponseError('Missing encryptedSignerAdminMacaroon in wallet', 400);
    }
    const adminMacaroon = bitgo.decrypt({
        password: passphrase,
        input: encryptedSignerAdminMacaroon,
    });
    const signerMacaroon = await createSignerMacaroon(lndSignerClient, { adminMacaroonHex: buffer_1.Buffer.from(adminMacaroon, 'base64').toString('hex') }, addIpCaveatToMacaroon ? watchOnlyIp : null);
    return await (0, abstract_lightning_1.updateWalletCoinSpecific)(wallet, {
        signerMacaroon,
        passphrase,
    });
}
exports.handleCreateSignerMacaroon = handleCreateSignerMacaroon;
/**
 * Handle the request to get the state of a wallet from the signer.
 */
async function handleGetLightningWalletState(req) {
    const coinName = req.params.coin;
    if (!(0, abstract_lightning_1.isLightningCoinName)(coinName)) {
        throw new errors_1.ApiResponseError(`Invalid coin to get lightning wallet state: ${coinName}`, 400);
    }
    const walletId = req.params.id;
    if (typeof walletId !== 'string') {
        throw new errors_1.ApiResponseError(`Invalid wallet id: ${walletId}`, 400);
    }
    const lndSignerClient = await lndSignerClient_1.LndSignerClient.create(walletId, req.config);
    return await lndSignerClient.getWalletState();
}
exports.handleGetLightningWalletState = handleGetLightningWalletState;
/**
 * Handle the request to unlock a wallet in the signer.
 */
async function handleUnlockLightningWallet(req) {
    const coinName = req.params.coin;
    if (!(0, abstract_lightning_1.isLightningCoinName)(coinName)) {
        throw new errors_1.ApiResponseError(`Invalid coin to unlock lightning wallet: ${coinName}`, 400);
    }
    const walletId = req.params.id;
    if (typeof walletId !== 'string') {
        throw new errors_1.ApiResponseError(`Invalid wallet id: ${walletId}`, 400);
    }
    const { passphrase } = (0, sdk_core_1.decodeOrElse)(codecs_1.UnlockLightningWalletRequest.name, codecs_1.UnlockLightningWalletRequest, req.body, (_) => {
        // DON'T throw errors from decodeOrElse. It could leak sensitive information.
        throw new errors_1.ApiResponseError('Invalid request body to unlock lightning wallet', 400);
    });
    const lndSignerClient = await lndSignerClient_1.LndSignerClient.create(walletId, req.config);
    // The passphrase at LND can only accommodate a base64 character set
    // For more information, see BTC-1851
    await lndSignerClient.unlockWallet({
        wallet_password: buffer_1.Buffer.from(passphrase).toString('base64'),
    });
    return { message: 'ok' };
}
exports.handleUnlockLightningWallet = handleUnlockLightningWallet;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlnaHRuaW5nU2lnbmVyUm91dGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpZ2h0bmluZy9saWdodG5pbmdTaWduZXJSb3V0ZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsNkJBQTJCO0FBRTNCLDhDQUErQztBQUMvQyxrRUFTbUM7QUFDbkMsMkNBQTJDO0FBQzNDLG1DQUFnQztBQUVoQyxxQ0FLa0I7QUFDbEIsdURBQW9EO0FBQ3BELHNDQUE2QztBQUk3QyxLQUFLLFVBQVUsb0JBQW9CLENBQ2pDLGVBQWdDLEVBQ2hDLE1BQW9DLEVBQ3BDLFdBQXNDO0lBRXRDLE1BQU0sRUFBRSxRQUFRLEVBQUUsR0FBRyxNQUFNLGVBQWUsQ0FBQyxZQUFZLENBQUMsRUFBRSxXQUFXLEVBQUUsOENBQXlCLEVBQUUsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUM1RyxNQUFNLGNBQWMsR0FBRyxXQUFXO1FBQ2hDLENBQUMsQ0FBQyxJQUFBLDBDQUFxQixFQUFDLGVBQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsRUFBRSxXQUFXLENBQUM7UUFDckYsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUNkLE9BQU8sY0FBYyxDQUFDLENBQUMsQ0FBQyxlQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxRQUFRLENBQUMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQztBQUMzRixDQUFDO0FBRUQsU0FBUyxnQkFBZ0IsQ0FDdkIsVUFBa0IsRUFDbEIsdUJBQStCLEVBQy9CLE9BQXdCLEVBQ3hCLE9BQWdCO0lBRWhCLE1BQU0sY0FBYyxHQUFHLE9BQU8sQ0FBQztRQUM3QixRQUFRLEVBQUUsVUFBVTtRQUNwQixLQUFLLEVBQUUsdUJBQXVCO0tBQy9CLENBQUMsQ0FBQztJQUNILE9BQU8sT0FBTyxDQUFDLEtBQUssQ0FBQyxP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O1FBQ0osNkVBQTZFO1FBQzdFLE1BQU0sSUFBSSx5QkFBZ0IsQ0FBQyxpREFBaUQsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNyRixDQUFDLENBQ0YsQ0FBQztJQUVGLE1BQU0sZUFBZSxHQUFHLE1BQU0saUNBQWUsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMzRSxvRUFBb0U7SUFDcEUscUNBQXFDO0lBQ3JDLE1BQU0sZUFBZSxDQUFDLFlBQVksQ0FBQztRQUNqQyxlQUFlLEVBQUUsZUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDO0tBQzVELENBQUMsQ0FBQztJQUNILE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFDM0IsQ0FBQztBQTNCRCxrRUEyQkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBpc0lQIH0gZnJvbSAnbmV0JztcbmltcG9ydCAqIGFzIGV4cHJlc3MgZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgeyBkZWNvZGVPckVsc2UgfSBmcm9tICdAYml0Z28vc2RrLWNvcmUnO1xuaW1wb3J0IHtcbiAgZ2V0VXR4b2xpYk5ldHdvcmssXG4gIHNpZ25lck1hY2Fyb29uUGVybWlzc2lvbnMsXG4gIGNyZWF0ZVdhdGNoT25seSxcbiAgYWRkSVBDYXZlYXRUb01hY2Fyb29uLFxuICBpc0xpZ2h0bmluZ0NvaW5OYW1lLFxuICBnZXRMaWdodG5pbmdLZXljaGFpbixcbiAgZ2V0TGlnaHRuaW5nQXV0aEtleWNoYWlucyxcbiAgdXBkYXRlV2FsbGV0Q29pblNwZWNpZmljLFxufSBmcm9tICdAYml0Z28vYWJzdHJhY3QtbGlnaHRuaW5nJztcbmltcG9ydCAqIGFzIHV0eG9saWIgZnJvbSAnQGJpdGdvL3V0eG8tbGliJztcbmltcG9ydCB7IEJ1ZmZlciB9IGZyb20gJ2J1ZmZlcic7XG5cbmltcG9ydCB7XG4gIENyZWF0ZVNpZ25lck1hY2Fyb29uUmVxdWVzdCxcbiAgR2V0V2FsbGV0U3RhdGVSZXNwb25zZSxcbiAgSW5pdExpZ2h0bmluZ1dhbGxldFJlcXVlc3QsXG4gIFVubG9ja0xpZ2h0bmluZ1dhbGxldFJlcXVlc3QsXG59IGZyb20gJy4vY29kZWNzJztcbmltcG9ydCB7IExuZFNpZ25lckNsaWVudCB9IGZyb20gJy4vbG5kU2lnbmVyQ2xpZW50JztcbmltcG9ydCB7IEFwaVJlc3BvbnNlRXJyb3IgfSBmcm9tICcuLi9lcnJvcnMnO1xuXG50eXBlIERlY3J5cHQgPSAocGFyYW1zOiB7IGlucHV0OiBzdHJpbmc7IHBhc3N3b3JkOiBzdHJpbmcgfSkgPT4gc3RyaW5nO1xuXG5hc3luYyBmdW5jdGlvbiBjcmVhdGVTaWduZXJNYWNhcm9vbihcbiAgbG5kU2lnbmVyQ2xpZW50OiBMbmRTaWduZXJDbGllbnQsXG4gIGhlYWRlcjogeyBhZG1pbk1hY2Fyb29uSGV4OiBzdHJpbmcgfSxcbiAgd2F0Y2hPbmx5SXA6IHN0cmluZyB8IHVuZGVmaW5lZCB8IG51bGxcbik6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHsgbWFjYXJvb24gfSA9IGF3YWl0IGxuZFNpZ25lckNsaWVudC5iYWtlTWFjYXJvb24oeyBwZXJtaXNzaW9uczogc2lnbmVyTWFjYXJvb25QZXJtaXNzaW9ucyB9LCBoZWFkZXIpO1xuICBjb25zdCBtYWNhcm9vbkJhc2U2NCA9IHdhdGNoT25seUlwXG4gICAgPyBhZGRJUENhdmVhdFRvTWFjYXJvb24oQnVmZmVyLmZyb20obWFjYXJvb24sICdoZXgnKS50b1N0cmluZygnYmFzZTY0JyksIHdhdGNoT25seUlwKVxuICAgIDogdW5kZWZpbmVkO1xuICByZXR1cm4gbWFjYXJvb25CYXNlNjQgPyBCdWZmZXIuZnJvbShtYWNhcm9vbkJhc2U2NCwgJ2Jhc2U2NCcpLnRvU3RyaW5nKCdoZXgnKSA6IG1hY2Fyb29uO1xufVxuXG5mdW5jdGlvbiBnZXRTaWduZXJSb290S2V5KFxuICBwYXNzcGhyYXNlOiBzdHJpbmcsXG4gIHVzZXJNYWlubmV0RW5jcnlwdGVkUHJ2OiBzdHJpbmcsXG4gIG5ldHdvcms6IHV0eG9saWIuTmV0d29yayxcbiAgZGVjcnlwdDogRGVjcnlwdFxuKSB7XG4gIGNvbnN0IHVzZXJNYWlubmV0UHJ2ID0gZGVjcnlwdCh7XG4gICAgcGFzc3dvcmQ6IHBhc3NwaHJhc2UsXG4gICAgaW5wdXQ6IHVzZXJNYWlubmV0RW5jcnlwdGVkUHJ2LFxuICB9KTtcbiAgcmV0dXJuIHV0eG9saWIuYml0Z28ua2V5dXRpbC5jb252ZXJ0RXh0ZW5kZWRLZXlOZXR3b3JrKHVzZXJNYWlubmV0UHJ2LCB1dHhvbGliLm5ldHdvcmtzLmJpdGNvaW4sIG5ldHdvcmspO1xufVxuXG5mdW5jdGlvbiBnZXRNYWNhcm9vblJvb3RLZXkocGFzc3BocmFzZTogc3RyaW5nLCBub2RlQXV0aEVuY3J5cHRlZFBydjogc3RyaW5nLCBkZWNyeXB0OiBEZWNyeXB0KSB7XG4gIGNvbnN0IGhkTm9kZSA9IHV0eG9saWIuYmlwMzIuZnJvbUJhc2U1OChkZWNyeXB0KHsgcGFzc3dvcmQ6IHBhc3NwaHJhc2UsIGlucHV0OiBub2RlQXV0aEVuY3J5cHRlZFBydiB9KSk7XG4gIGlmICghaGROb2RlLnByaXZhdGVLZXkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ25vZGVBdXRoRW5jcnlwdGVkUHJ2IGlzIG5vdCBhIHByaXZhdGUga2V5Jyk7XG4gIH1cbiAgcmV0dXJuIGhkTm9kZS5wcml2YXRlS2V5LnRvU3RyaW5nKCdiYXNlNjQnKTtcbn1cblxuLyoqXG4gKiBIYW5kbGUgdGhlIHJlcXVlc3QgdG8gaW5pdGlhbGlzZSByZW1vdGUgc2lnbmVyIExORCBmb3IgYSB3YWxsZXQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVJbml0TGlnaHRuaW5nV2FsbGV0KHJlcTogZXhwcmVzcy5SZXF1ZXN0KTogUHJvbWlzZTx1bmtub3duPiB7XG4gIGNvbnN0IGJpdGdvID0gcmVxLmJpdGdvO1xuICBjb25zdCBjb2luTmFtZSA9IHJlcS5wYXJhbXMuY29pbjtcbiAgaWYgKCFpc0xpZ2h0bmluZ0NvaW5OYW1lKGNvaW5OYW1lKSkge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKGBJbnZhbGlkIGNvaW4gJHtjb2luTmFtZX0uIFRoaXMgaXMgbm90IGEgbGlnaHRuaW5nIGNvaW4uYCwgNDAwKTtcbiAgfVxuICBjb25zdCBjb2luID0gYml0Z28uY29pbihjb2luTmFtZSk7XG5cbiAgY29uc3Qgd2FsbGV0SWQgPSByZXEucGFyYW1zLmlkO1xuICBpZiAodHlwZW9mIHdhbGxldElkICE9PSAnc3RyaW5nJykge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKGBJbnZhbGlkIHdhbGxldCBpZDogJHt3YWxsZXRJZH1gLCA0MDApO1xuICB9XG5cbiAgY29uc3QgeyBwYXNzcGhyYXNlLCBleHByZXNzSG9zdCB9ID0gZGVjb2RlT3JFbHNlKFxuICAgIEluaXRMaWdodG5pbmdXYWxsZXRSZXF1ZXN0Lm5hbWUsXG4gICAgSW5pdExpZ2h0bmluZ1dhbGxldFJlcXVlc3QsXG4gICAgcmVxLmJvZHksXG4gICAgKF8pID0+IHtcbiAgICAgIC8vIERPTidUIHRocm93IGVycm9ycyBmcm9tIGRlY29kZU9yRWxzZS4gSXQgY291bGQgbGVhayBzZW5zaXRpdmUgaW5mb3JtYXRpb24uXG4gICAgICB0aHJvdyBuZXcgQXBpUmVzcG9uc2VFcnJvcignSW52YWxpZCByZXF1ZXN0IGJvZHkgdG8gaW5pdGlhbGl6ZSBsaWdodG5pbmcgd2FsbGV0JywgNDAwKTtcbiAgICB9XG4gICk7XG5cbiAgY29uc3Qgd2FsbGV0ID0gYXdhaXQgY29pbi53YWxsZXRzKCkuZ2V0KHsgaWQ6IHdhbGxldElkLCBpbmNsdWRlQmFsYW5jZTogZmFsc2UgfSk7XG4gIGlmICh3YWxsZXQuc3ViVHlwZSgpICE9PSAnbGlnaHRuaW5nU2VsZkN1c3RvZHknKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoYG5vdCBhIHNlbGYgY3VzdG9kaWFsIGxpZ2h0aW5nIHdhbGxldCAke3dhbGxldElkfWAsIDQwMCk7XG4gIH1cbiAgY29uc3QgbG5kU2lnbmVyQ2xpZW50ID0gYXdhaXQgTG5kU2lnbmVyQ2xpZW50LmNyZWF0ZSh3YWxsZXRJZCwgcmVxLmNvbmZpZyk7XG5cbiAgY29uc3QgdXNlcktleSA9IGF3YWl0IGdldExpZ2h0bmluZ0tleWNoYWluKHdhbGxldCk7XG4gIGNvbnN0IHVzZXJLZXlFbmNyeXB0ZWRQcnYgPSB1c2VyS2V5LmVuY3J5cHRlZFBydjtcbiAgaWYgKCF1c2VyS2V5RW5jcnlwdGVkUHJ2KSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoJ01pc3NpbmcgZW5jcnlwdGVkUHJ2IGluIHVzZXIga2V5Y2hhaW4nLCA0MDApO1xuICB9XG4gIGNvbnN0IHsgbm9kZUF1dGhLZXkgfSA9IGF3YWl0IGdldExpZ2h0bmluZ0F1dGhLZXljaGFpbnMod2FsbGV0KTtcbiAgY29uc3Qgbm9kZUF1dGhLZXlFbmNyeXB0ZWRQcnYgPSBub2RlQXV0aEtleS5lbmNyeXB0ZWRQcnY7XG4gIGlmICghbm9kZUF1dGhLZXlFbmNyeXB0ZWRQcnYpIHtcbiAgICB0aHJvdyBuZXcgQXBpUmVzcG9uc2VFcnJvcignTWlzc2luZyBlbmNyeXB0ZWRQcnYgaW4gbm9kZSBhdXRoIGtleWNoYWluJywgNDAwKTtcbiAgfVxuICBjb25zdCBuZXR3b3JrID0gZ2V0VXR4b2xpYk5ldHdvcmsoY29pbi5nZXRDaGFpbigpKTtcbiAgY29uc3Qgc2lnbmVyUm9vdEtleSA9IGdldFNpZ25lclJvb3RLZXkocGFzc3BocmFzZSwgdXNlcktleUVuY3J5cHRlZFBydiwgbmV0d29yaywgYml0Z28uZGVjcnlwdCk7XG4gIGNvbnN0IG1hY2Fyb29uUm9vdEtleSA9IGdldE1hY2Fyb29uUm9vdEtleShwYXNzcGhyYXNlLCBub2RlQXV0aEtleUVuY3J5cHRlZFBydiwgYml0Z28uZGVjcnlwdCk7XG5cbiAgY29uc3QgeyBhZG1pbl9tYWNhcm9vbjogYWRtaW5NYWNhcm9vbiB9ID0gYXdhaXQgbG5kU2lnbmVyQ2xpZW50LmluaXRXYWxsZXQoe1xuICAgIC8vIFRoZSBwYXNzcGhyYXNlIGF0IExORCBjYW4gb25seSBhY2NvbW1vZGF0ZSBhIGJhc2U2NCBjaGFyYWN0ZXIgc2V0XG4gICAgLy8gRm9yIG1vcmUgaW5mb3JtYXRpb24sIHNlZSBCVEMtMTg1MVxuICAgIHdhbGxldF9wYXNzd29yZDogQnVmZmVyLmZyb20ocGFzc3BocmFzZSkudG9TdHJpbmcoJ2Jhc2U2NCcpLFxuICAgIGV4dGVuZGVkX21hc3Rlcl9rZXk6IHNpZ25lclJvb3RLZXksXG4gICAgbWFjYXJvb25fcm9vdF9rZXk6IG1hY2Fyb29uUm9vdEtleSxcbiAgfSk7XG5cbiAgcmV0dXJuIGF3YWl0IHVwZGF0ZVdhbGxldENvaW5TcGVjaWZpYyh3YWxsZXQsIHtcbiAgICBzaWduZXJBZG1pbk1hY2Fyb29uOlxuICAgICAgZXhwcmVzc0hvc3QgJiYgISFpc0lQKGV4cHJlc3NIb3N0KSA/IGFkZElQQ2F2ZWF0VG9NYWNhcm9vbihhZG1pbk1hY2Fyb29uLCBleHByZXNzSG9zdCkgOiBhZG1pbk1hY2Fyb29uLFxuICAgIHdhdGNoT25seUFjY291bnRzOiBjcmVhdGVXYXRjaE9ubHkoc2lnbmVyUm9vdEtleSwgbmV0d29yayksXG4gICAgcGFzc3BocmFzZSxcbiAgfSk7XG59XG5cbi8qKlxuICogSGFuZGxlIHRoZSByZXF1ZXN0IHRvIGNyZWF0ZSBhIHNpZ25lciBtYWNhcm9vbiBmcm9tIHJlbW90ZSBzaWduZXIgTE5EIGZvciBhIHdhbGxldC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUNyZWF0ZVNpZ25lck1hY2Fyb29uKHJlcTogZXhwcmVzcy5SZXF1ZXN0KTogUHJvbWlzZTx1bmtub3duPiB7XG4gIGNvbnN0IGJpdGdvID0gcmVxLmJpdGdvO1xuICBjb25zdCBjb2luTmFtZSA9IHJlcS5wYXJhbXMuY29pbjtcbiAgaWYgKCFpc0xpZ2h0bmluZ0NvaW5OYW1lKGNvaW5OYW1lKSkge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKGBJbnZhbGlkIGNvaW4gdG8gY3JlYXRlIHNpZ25lciBtYWNhcm9vbjogJHtjb2luTmFtZX0uIE11c3QgYmUgYSBsaWdodG5pbmcgY29pbi5gLCA0MDApO1xuICB9XG4gIGNvbnN0IGNvaW4gPSBiaXRnby5jb2luKGNvaW5OYW1lKTtcbiAgY29uc3Qgd2FsbGV0SWQgPSByZXEucGFyYW1zLmlkO1xuICBpZiAodHlwZW9mIHdhbGxldElkICE9PSAnc3RyaW5nJykge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKGBJbnZhbGlkIHdhbGxldCBpZDogJHt3YWxsZXRJZH1gLCA0MDApO1xuICB9XG5cbiAgY29uc3QgeyBwYXNzcGhyYXNlLCBhZGRJcENhdmVhdFRvTWFjYXJvb24gfSA9IGRlY29kZU9yRWxzZShcbiAgICBDcmVhdGVTaWduZXJNYWNhcm9vblJlcXVlc3QubmFtZSxcbiAgICBDcmVhdGVTaWduZXJNYWNhcm9vblJlcXVlc3QsXG4gICAgcmVxLmJvZHksXG4gICAgKF8pID0+IHtcbiAgICAgIC8vIERPTidUIHRocm93IGVycm9ycyBmcm9tIGRlY29kZU9yRWxzZS4gSXQgY291bGQgbGVhayBzZW5zaXRpdmUgaW5mb3JtYXRpb24uXG4gICAgICB0aHJvdyBuZXcgQXBpUmVzcG9uc2VFcnJvcignSW52YWxpZCByZXF1ZXN0IGJvZHkgdG8gY3JlYXRlIHNpZ25lciBtYWNhcm9vbicsIDQwMCk7XG4gICAgfVxuICApO1xuXG4gIGNvbnN0IHdhbGxldCA9IGF3YWl0IGNvaW4ud2FsbGV0cygpLmdldCh7IGlkOiB3YWxsZXRJZCwgaW5jbHVkZUJhbGFuY2U6IGZhbHNlIH0pO1xuICBpZiAod2FsbGV0LnN1YlR5cGUoKSAhPT0gJ2xpZ2h0bmluZ1NlbGZDdXN0b2R5Jykge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKGBub3QgYSBzZWxmIGN1c3RvZGlhbCBsaWdodGluZyB3YWxsZXQgJHt3YWxsZXRJZH1gLCA0MDApO1xuICB9XG4gIGNvbnN0IHdhdGNoT25seUlwID0gd2FsbGV0LmNvaW5TcGVjaWZpYygpPy53YXRjaE9ubHlFeHRlcm5hbElwO1xuICBpZiAoIXdhdGNoT25seUlwICYmIGFkZElwQ2F2ZWF0VG9NYWNhcm9vbikge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKFxuICAgICAgJ0Nhbm5vdCBjcmVhdGUgc2lnbmVyIG1hY2Fyb29uIGJlY2F1c2UgdGhlIGV4dGVybmFsIElQIGlzIG5vdCBzZXQuIFRoaXMgY2FuIHRha2Ugc29tZSB0aW1lLiBDb250YWN0IHN1cHBvcnRAYml0Z28uY29tIGlmIGxvbmdlciB0aGFuIDI0IGhvdXJzLicsXG4gICAgICA0MDBcbiAgICApO1xuICB9XG5cbiAgaWYgKHdhdGNoT25seUlwICYmICFpc0lQKHdhdGNoT25seUlwKSkge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKGBJbnZhbGlkIElQIGFkZHJlc3M6ICR7d2F0Y2hPbmx5SXB9LiBDb250YWN0IHN1cHBvcnRAYml0Z28uY29tYCwgNTAwKTtcbiAgfVxuXG4gIGNvbnN0IGxuZFNpZ25lckNsaWVudCA9IGF3YWl0IExuZFNpZ25lckNsaWVudC5jcmVhdGUod2FsbGV0SWQsIHJlcS5jb25maWcpO1xuXG4gIGNvbnN0IGVuY3J5cHRlZFNpZ25lckFkbWluTWFjYXJvb24gPSB3YWxsZXQuY29pblNwZWNpZmljKCk/LmVuY3J5cHRlZFNpZ25lckFkbWluTWFjYXJvb247XG4gIGlmICghZW5jcnlwdGVkU2lnbmVyQWRtaW5NYWNhcm9vbikge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKCdNaXNzaW5nIGVuY3J5cHRlZFNpZ25lckFkbWluTWFjYXJvb24gaW4gd2FsbGV0JywgNDAwKTtcbiAgfVxuICBjb25zdCBhZG1pbk1hY2Fyb29uID0gYml0Z28uZGVjcnlwdCh7XG4gICAgcGFzc3dvcmQ6IHBhc3NwaHJhc2UsXG4gICAgaW5wdXQ6IGVuY3J5cHRlZFNpZ25lckFkbWluTWFjYXJvb24sXG4gIH0pO1xuXG4gIGNvbnN0IHNpZ25lck1hY2Fyb29uID0gYXdhaXQgY3JlYXRlU2lnbmVyTWFjYXJvb24oXG4gICAgbG5kU2lnbmVyQ2xpZW50LFxuICAgIHsgYWRtaW5NYWNhcm9vbkhleDogQnVmZmVyLmZyb20oYWRtaW5NYWNhcm9vbiwgJ2Jhc2U2NCcpLnRvU3RyaW5nKCdoZXgnKSB9LFxuICAgIGFkZElwQ2F2ZWF0VG9NYWNhcm9vbiA/IHdhdGNoT25seUlwIDogbnVsbFxuICApO1xuXG4gIHJldHVybiBhd2FpdCB1cGRhdGVXYWxsZXRDb2luU3BlY2lmaWMod2FsbGV0LCB7XG4gICAgc2lnbmVyTWFjYXJvb24sXG4gICAgcGFzc3BocmFzZSxcbiAgfSk7XG59XG5cbi8qKlxuICogSGFuZGxlIHRoZSByZXF1ZXN0IHRvIGdldCB0aGUgc3RhdGUgb2YgYSB3YWxsZXQgZnJvbSB0aGUgc2lnbmVyLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaGFuZGxlR2V0TGlnaHRuaW5nV2FsbGV0U3RhdGUocmVxOiBleHByZXNzLlJlcXVlc3QpOiBQcm9taXNlPEdldFdhbGxldFN0YXRlUmVzcG9uc2U+IHtcbiAgY29uc3QgY29pbk5hbWUgPSByZXEucGFyYW1zLmNvaW47XG4gIGlmICghaXNMaWdodG5pbmdDb2luTmFtZShjb2luTmFtZSkpIHtcbiAgICB0aHJvdyBuZXcgQXBpUmVzcG9uc2VFcnJvcihgSW52YWxpZCBjb2luIHRvIGdldCBsaWdodG5pbmcgd2FsbGV0IHN0YXRlOiAke2NvaW5OYW1lfWAsIDQwMCk7XG4gIH1cbiAgY29uc3Qgd2FsbGV0SWQgPSByZXEucGFyYW1zLmlkO1xuICBpZiAodHlwZW9mIHdhbGxldElkICE9PSAnc3RyaW5nJykge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKGBJbnZhbGlkIHdhbGxldCBpZDogJHt3YWxsZXRJZH1gLCA0MDApO1xuICB9XG5cbiAgY29uc3QgbG5kU2lnbmVyQ2xpZW50ID0gYXdhaXQgTG5kU2lnbmVyQ2xpZW50LmNyZWF0ZSh3YWxsZXRJZCwgcmVxLmNvbmZpZyk7XG4gIHJldHVybiBhd2FpdCBsbmRTaWduZXJDbGllbnQuZ2V0V2FsbGV0U3RhdGUoKTtcbn1cblxuLyoqXG4gKiBIYW5kbGUgdGhlIHJlcXVlc3QgdG8gdW5sb2NrIGEgd2FsbGV0IGluIHRoZSBzaWduZXIuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVVbmxvY2tMaWdodG5pbmdXYWxsZXQocmVxOiBleHByZXNzLlJlcXVlc3QpOiBQcm9taXNlPHsgbWVzc2FnZTogc3RyaW5nIH0+IHtcbiAgY29uc3QgY29pbk5hbWUgPSByZXEucGFyYW1zLmNvaW47XG4gIGlmICghaXNMaWdodG5pbmdDb2luTmFtZShjb2luTmFtZSkpIHtcbiAgICB0aHJvdyBuZXcgQXBpUmVzcG9uc2VFcnJvcihgSW52YWxpZCBjb2luIHRvIHVubG9jayBsaWdodG5pbmcgd2FsbGV0OiAke2NvaW5OYW1lfWAsIDQwMCk7XG4gIH1cbiAgY29uc3Qgd2FsbGV0SWQgPSByZXEucGFyYW1zLmlkO1xuICBpZiAodHlwZW9mIHdhbGxldElkICE9PSAnc3RyaW5nJykge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKGBJbnZhbGlkIHdhbGxldCBpZDogJHt3YWxsZXRJZH1gLCA0MDApO1xuICB9XG5cbiAgY29uc3QgeyBwYXNzcGhyYXNlIH0gPSBkZWNvZGVPckVsc2UoXG4gICAgVW5sb2NrTGlnaHRuaW5nV2FsbGV0UmVxdWVzdC5uYW1lLFxuICAgIFVubG9ja0xpZ2h0bmluZ1dhbGxldFJlcXVlc3QsXG4gICAgcmVxLmJvZHksXG4gICAgKF8pID0+IHtcbiAgICAgIC8vIERPTidUIHRocm93IGVycm9ycyBmcm9tIGRlY29kZU9yRWxzZS4gSXQgY291bGQgbGVhayBzZW5zaXRpdmUgaW5mb3JtYXRpb24uXG4gICAgICB0aHJvdyBuZXcgQXBpUmVzcG9uc2VFcnJvcignSW52YWxpZCByZXF1ZXN0IGJvZHkgdG8gdW5sb2NrIGxpZ2h0bmluZyB3YWxsZXQnLCA0MDApO1xuICAgIH1cbiAgKTtcblxuICBjb25zdCBsbmRTaWduZXJDbGllbnQgPSBhd2FpdCBMbmRTaWduZXJDbGllbnQuY3JlYXRlKHdhbGxldElkLCByZXEuY29uZmlnKTtcbiAgLy8gVGhlIHBhc3NwaHJhc2UgYXQgTE5EIGNhbiBvbmx5IGFjY29tbW9kYXRlIGEgYmFzZTY0IGNoYXJhY3RlciBzZXRcbiAgLy8gRm9yIG1vcmUgaW5mb3JtYXRpb24sIHNlZSBCVEMtMTg1MVxuICBhd2FpdCBsbmRTaWduZXJDbGllbnQudW5sb2NrV2FsbGV0KHtcbiAgICB3YWxsZXRfcGFzc3dvcmQ6IEJ1ZmZlci5mcm9tKHBhc3NwaHJhc2UpLnRvU3RyaW5nKCdiYXNlNjQnKSxcbiAgfSk7XG4gIHJldHVybiB7IG1lc3NhZ2U6ICdvaycgfTtcbn1cbiJdfQ==

Выполнить команду


Для локальной разработки. Не используйте в интернете!