PHP WebShell
Текущая директория: /opt/BitGoJS/modules/bitgo/dist/test/v2/unit/coins/utxo/recovery
Просмотр файла: crossChainRecovery.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* @prettier
*/
const assert = require("assert");
const should = require("should");
const nock = require("nock");
const utxolib = require("@bitgo/utxo-lib");
const util_1 = require("../util");
const sdk_test_1 = require("@bitgo/sdk-test");
const nockBitGo_1 = require("../util/nockBitGo");
const transaction_1 = require("../util/transaction");
const keychains_1 = require("../util/keychains");
const mock_1 = require("./mock");
const abstract_utxo_1 = require("@bitgo/abstract-utxo");
const sinon = require("sinon");
function getKeyId(k) {
return (0, sdk_test_1.getSeed)(k.pub).toString('hex');
}
function nockWallet(coin, walletId, walletKeys) {
return [
(0, nockBitGo_1.nockBitGo)()
.get(`/api/v2/${coin.getChain()}/wallet/${walletId}`)
.reply(200, {
id: walletId,
coin: coin.getChain(),
label: 'crossChainRecovery',
keys: walletKeys.map((k) => getKeyId(k)),
})
.persist(),
...walletKeys.map((k) => (0, nockBitGo_1.nockBitGo)()
.get(`/api/v2/${coin.getChain()}/key/${getKeyId(k)}`)
.reply(200, k)
.persist()),
];
}
function nockWalletAddress(coin, walletId, address) {
return (0, nockBitGo_1.nockBitGo)()
.get(`/api/v2/${coin.getChain()}/wallet/${walletId}/address/${address.address}`)
.reply(200, {
address: address.address,
chain: address.chain,
index: address.index,
coin: coin.getChain(),
wallet: walletId,
coinSpecific: address.coinSpecific,
})
.persist();
}
/**
* Setup test for cross-chain recovery.
*
* Users can receive deposits on wallet addresses that are on a different chain.
*
* For instance, a user can receive litecoin on a bitcoin wallet.
* This means that the litecoin blockchain has a transaction with outputs that are spendable
* with keys that were originally created for a BitGo BTC wallet.
* In this example, LTC is the "source coin" and BTC is the "recovery coin"
* In cases like these we must use construct a transaction for litecoin network using keys of the
* bitcoin wallet.
*
* @param sourceCoin - the coin to construct the transaction for
* @param recoveryCoin - the coin the receiving wallet was set up for
*/
function run(sourceCoin, recoveryCoin) {
describe(`Cross-Chain Recovery [sourceCoin=${sourceCoin.getChain()} recoveryCoin=${recoveryCoin.getChain()}]`, function () {
const walletKeys = (0, util_1.getDefaultWalletKeys)();
const recoveryWalletId = '5abacebe28d72fbd07e0b8cbba0ff39e';
// the address the accidental deposit went to, in both sourceCoin and addressCoin formats
const [depositAddressSourceCoin, depositAddressRecoveryCoin] = [sourceCoin, recoveryCoin].map((coin) => coin.generateAddress({ keychains: util_1.keychainsBase58, index: 0 }));
// the address where we want to recover our funds to
const recoveryAddress = sourceCoin.generateAddress({ keychains: util_1.keychainsBase58, index: 1 }).address;
const nocks = [];
let depositTx;
function getDepositUnspents() {
return [
(0, util_1.mockUnspent)(sourceCoin.network, walletKeys, 'p2sh', 0, (sourceCoin.amountType === 'bigint' ? BigInt('10999999800000001') : 1e8)),
];
}
function getDepositTransaction() {
return (0, transaction_1.createFullSignedTransaction)(sourceCoin.network, getDepositUnspents(), depositAddressSourceCoin.address, (0, keychains_1.getDefaultWalletUnspentSigner)());
}
before('prepare deposit tx', function () {
depositTx = getDepositTransaction();
});
function getRecoveryUnspents() {
return [
{
id: depositTx.getId(),
address: depositAddressSourceCoin.address,
chain: depositAddressSourceCoin.chain,
index: depositAddressSourceCoin.index,
value: depositTx.outs[0].value,
},
];
}
before('setup nocks', function () {
nocks.push(...nockWallet(recoveryCoin, recoveryWalletId, util_1.keychainsBase58));
nocks.push(nockWalletAddress(recoveryCoin, recoveryWalletId, depositAddressRecoveryCoin));
});
after(function () {
nocks.forEach((n) => n.done());
});
after(function () {
nock.cleanAll();
});
afterEach(function () {
sinon.restore();
});
function testMatchFixture(name, getRecoveryResult) {
it(`should match fixture (${name})`, async function () {
const recovery = getRecoveryResult();
let recoveryObj = {
...recovery,
tx: (0, util_1.transactionHexToObj)(recovery.txHex, sourceCoin.network, sourceCoin.amountType),
};
if (sourceCoin.amountType === 'bigint') {
recoveryObj = JSON.parse(JSON.stringify(recoveryObj, (k, v) => {
if (typeof v === 'bigint') {
return v.toString();
}
else {
return v;
}
}));
}
(0, util_1.shouldEqualJSON)(recoveryObj, await (0, util_1.getFixture)(sourceCoin, `recovery/crossChainRecovery-${recoveryCoin.getChain()}-${name}`, recoveryObj));
});
}
function checkRecoveryTransactionSignature(tx) {
if (typeof tx === 'string') {
tx = utxolib.bitgo.createTransactionFromBuffer(Buffer.from(tx, 'hex'), sourceCoin.network, {
amountType: sourceCoin.amountType,
});
}
const unspents = getRecoveryUnspents();
should.equal(tx.ins.length, unspents.length);
tx.ins.forEach((input, i) => {
assert(typeof tx !== 'string');
utxolib.bitgo
.verifySignatureWithUnspent(tx, i, getRecoveryUnspents(), walletKeys)
.should.eql([true, false, false]);
});
}
it('should test signed cross chain recovery', async () => {
const getRecoveryProviderStub = sinon
.stub(abstract_utxo_1.AbstractUtxoCoin.prototype, 'getRecoveryProvider')
.returns(new mock_1.MockCrossChainRecoveryProvider(sourceCoin, getDepositUnspents(), depositTx));
const params = {
recoveryCoin,
txid: depositTx.getId(),
recoveryAddress,
wallet: recoveryWalletId,
};
const signedRecovery = (await sourceCoin.recoverFromWrongChain({
...params,
xprv: util_1.keychainsBase58[0].prv,
}));
should.equal(getRecoveryProviderStub.callCount, 1);
testMatchFixture('signed', () => signedRecovery);
it('should have valid signatures for signed recovery', function () {
checkRecoveryTransactionSignature(signedRecovery.txHex);
});
});
it('should test unsigned cross chain recovery', async () => {
const getRecoveryProviderStub = sinon
.stub(abstract_utxo_1.AbstractUtxoCoin.prototype, 'getRecoveryProvider')
.returns(new mock_1.MockCrossChainRecoveryProvider(sourceCoin, getDepositUnspents(), depositTx));
const params = {
recoveryCoin,
txid: depositTx.getId(),
recoveryAddress,
wallet: recoveryWalletId,
};
const unsignedRecovery = (await sourceCoin.recoverFromWrongChain({
...params,
signed: false,
}));
should.equal(getRecoveryProviderStub.callCount, 1);
testMatchFixture('unsigned', () => unsignedRecovery);
it('should be signable for unsigned recovery', async function () {
const signedTx = await sourceCoin.signTransaction({
txPrebuild: unsignedRecovery,
prv: util_1.keychainsBase58[0].prv,
pubs: util_1.keychainsBase58.map((k) => k.pub),
});
checkRecoveryTransactionSignature(signedTx.txHex);
});
});
});
}
function isSupportedCrossChainRecovery(sourceCoin, recoveryCoin) {
return abstract_utxo_1.supportedCrossChainRecoveries[sourceCoin.getFamily()]?.includes(recoveryCoin.getFamily());
}
util_1.utxoCoins.forEach((coin) => {
util_1.utxoCoins
.filter((otherCoin) => coin !== otherCoin &&
isSupportedCrossChainRecovery(coin, otherCoin) &&
((utxolib.isMainnet(coin.network) && utxolib.isMainnet(otherCoin.network)) ||
(utxolib.isTestnet(coin.network) && utxolib.isTestnet(otherCoin.network))))
.forEach((otherCoin) => {
if (coin.amountType === 'bigint') {
run(coin, otherCoin);
}
else {
run(coin, otherCoin);
}
});
});
describe(`Cross-Chain Recovery getWallet`, async function () {
const bitgo = util_1.defaultBitGo;
const recoveryCoin = (0, util_1.getUtxoCoin)('btc');
const recoveryWalletId = '5abacebe28d72fbd07e0b8cbba0ff39e';
it('should search v1 wallets if the v2 endpoint responds with a 4xx error', async function () {
const errorResponses = [400, 404];
for (const error of errorResponses) {
const nockV2Wallet = (0, nockBitGo_1.nockBitGo)(bitgo)
.get(`/api/v2/${recoveryCoin.getChain()}/wallet/${recoveryWalletId}`)
.reply(error);
const nockV1Wallet = (0, nockBitGo_1.nockBitGo)(bitgo).get(`/api/v1/wallet/${recoveryWalletId}`).reply(error);
await assert.rejects(() => (0, abstract_utxo_1.getWallet)(bitgo, recoveryCoin, recoveryWalletId), Error(`could not get wallet ${recoveryWalletId} from v1 or v2: ApiResponseError: ${error}`));
nockV2Wallet.done();
nockV1Wallet.done();
}
});
it('should throw an error if the v2 endpoint responds with a 5xx error', async function () {
const errorResponses = [500];
for (const error of errorResponses) {
const nockV2Wallet = (0, nockBitGo_1.nockBitGo)(bitgo)
.get(`/api/v2/${recoveryCoin.getChain()}/wallet/${recoveryWalletId}`)
.reply(error);
await assert.rejects(() => (0, abstract_utxo_1.getWallet)(bitgo, recoveryCoin, recoveryWalletId), {
name: 'ApiResponseError',
status: 500,
result: {},
invalidToken: false,
needsOTP: false,
});
nockV2Wallet.done();
}
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3Jvc3NDaGFpblJlY292ZXJ5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vdGVzdC92Mi91bml0L2NvaW5zL3V0eG8vcmVjb3ZlcnkvY3Jvc3NDaGFpblJlY292ZXJ5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUE7O0dBRUc7QUFDSCxpQ0FBaUM7QUFDakMsaUNBQWlDO0FBQ2pDLDZCQUE2QjtBQUM3QiwyQ0FBMkM7QUFFM0Msa0NBV2lCO0FBQ2pCLDhDQUEwQztBQUMxQyxpREFBOEM7QUFDOUMscURBQWtFO0FBQ2xFLGlEQUFrRTtBQUNsRSxpQ0FBd0Q7QUFDeEQsd0RBTThCO0FBQzlCLCtCQUErQjtBQUkvQixTQUFTLFFBQVEsQ0FBQyxDQUFpQjtJQUNqQyxPQUFPLElBQUEsa0JBQU8sRUFBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQ3hDLENBQUM7QUFFRCxTQUFTLFVBQVUsQ0FBQyxJQUFzQixFQUFFLFFBQWdCLEVBQUUsVUFBa0M7SUFDOUYsT0FBTztRQUNMLElBQUEscUJBQVMsR0FBRTthQUNSLEdBQUcsQ0FBQyxXQUFXLElBQUksQ0FBQyxRQUFRLEVBQUUsV0FBVyxRQUFRLEVBQUUsQ0FBQzthQUNwRCxLQUFLLENBQUMsR0FBRyxFQUFFO1lBQ1YsRUFBRSxFQUFFLFFBQVE7WUFDWixJQUFJLEVBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRTtZQUNyQixLQUFLLEVBQUUsb0JBQW9CO1lBQzNCLElBQUksRUFBRSxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDekMsQ0FBQzthQUNELE9BQU8sRUFBRTtRQUNaLEdBQUcsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQ3RCLElBQUEscUJBQVMsR0FBRTthQUNSLEdBQUcsQ0FBQyxX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O2dCQUMzRSxJQUFJLEVBQUUsa0JBQWtCO2dCQUN4QixNQUFNLEVBQUUsR0FBRztnQkFDWCxNQUFNLEVBQUUsRUFBRTtnQkFDVixZQUFZLEVBQUUsS0FBSztnQkFDbkIsUUFBUSxFQUFFLEtBQUs7YUFDaEIsQ0FBQyxDQUFDO1lBQ0gsWUFBWSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3RCLENBQUM7SUFDSCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAcHJldHRpZXJcbiAqL1xuaW1wb3J0ICogYXMgYXNzZXJ0IGZyb20gJ2Fzc2VydCc7XG5pbXBvcnQgKiBhcyBzaG91bGQgZnJvbSAnc2hvdWxkJztcbmltcG9ydCAqIGFzIG5vY2sgZnJvbSAnbm9jayc7XG5pbXBvcnQgKiBhcyB1dHhvbGliIGZyb20gJ0BiaXRnby91dHhvLWxpYic7XG5pbXBvcnQgeyBUcmlwbGUgfSBmcm9tICdAYml0Z28vc2RrLWNvcmUnO1xuaW1wb3J0IHtcbiAgZ2V0Rml4dHVyZSxcbiAga2V5Y2hhaW5zQmFzZTU4LFxuICBLZXljaGFpbkJhc2U1OCxcbiAgbW9ja1Vuc3BlbnQsXG4gIHNob3VsZEVxdWFsSlNPTixcbiAgdXR4b0NvaW5zLFxuICB0cmFuc2FjdGlvbkhleFRvT2JqLFxuICBnZXREZWZhdWx0V2FsbGV0S2V5cyxcbiAgZGVmYXVsdEJpdEdvLFxuICBnZXRVdHhvQ29pbixcbn0gZnJvbSAnLi4vdXRpbCc7XG5pbXBvcnQgeyBnZXRTZWVkIH0gZnJvbSAnQGJpdGdvL3Nkay10ZXN0JztcbmltcG9ydCB7IG5vY2tCaXRHbyB9IGZyb20gJy4uL3V0aWwvbm9ja0JpdEdvJztcbmltcG9ydCB7IGNyZWF0ZUZ1bGxTaWduZWRUcmFuc2FjdGlvbiB9IGZyb20gJy4uL3V0aWwvdHJhbnNhY3Rpb24nO1xuaW1wb3J0IHsgZ2V0RGVmYXVsdFdhbGxldFVuc3BlbnRTaWduZXIgfSBmcm9tICcuLi91dGlsL2tleWNoYWlucyc7XG5pbXBvcnQgeyBNb2NrQ3Jvc3NDaGFpblJlY292ZXJ5UHJvdmlkZXIgfSBmcm9tICcuL21vY2snO1xuaW1wb3J0IHtcbiAgQWJzdHJhY3RVdHhvQ29pbixcbiAgQ3Jvc3NDaGFpblJlY292ZXJ5U2lnbmVkLFxuICBDcm9zc0NoYWluUmVjb3ZlcnlVbnNpZ25lZCxcbiAgZ2V0V2FsbGV0LFxuICBzdXBwb3J0ZWRDcm9zc0NoYWluUmVjb3Zlcmllcyxcbn0gZnJvbSAnQGJpdGdvL2Fic3RyYWN0LXV0eG8nO1xuaW1wb3J0ICogYXMgc2lub24gZnJvbSAnc2lub24nO1xuXG50eXBlIFdhbGxldFVuc3BlbnQ8VE51bWJlciBleHRlbmRzIG51bWJlciB8IGJpZ2ludCA9IG51bWJlcj4gPSB1dHhvbGliLmJpdGdvLldhbGxldFVuc3BlbnQ8VE51bWJlcj47XG5cbmZ1bmN0aW9uIGdldEtleUlkKGs6IEtleWNoYWluQmFzZTU4KTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldFNlZWQoay5wdWIpLnRvU3RyaW5nKCdoZXgnKTtcbn1cblxuZnVuY3Rpb24gbm9ja1dhbGxldChjb2luOiBBYnN0cmFjdFV0eG9Db2luLCB3YWxsZXRJZDogc3RyaW5nLCB3YWxsZXRLZXlzOiBUcmlwbGU8S2V5Y2hhaW5CYXNlNTg+KTogbm9jay5TY29wZVtdIHtcbiAgcmV0dXJuIFtcbiAgICBub2NrQml0R28oKVxuICAgICAgLmdldChgL2FwaS92Mi8ke2NvaW4uZ2V0Q2hhaW4oKX0vd2FsbGV0LyR7d2FsbGV0SWR9YClcbiAgICAgIC5yZXBseSgyMDAsIHtcbiAgICAgICAgaWQ6IHdhbGxldElkLFxuICAgICAgICBjb2luOiBjb2luLmdldENoYWluKCksXG4gICAgICAgIGxhYmVsOiAnY3Jvc3NDaGFpblJlY292ZXJ5JyxcbiAgICAgICAga2V5czogd2FsbGV0S2V5cy5tYXAoKGspID0+IGdldEtleUlkKGspKSxcbiAgICAgIH0pXG4gICAgICAucGVyc2lzdCgpLFxuICAgIC4uLndhbGxldEtleXMubWFwKChrKSA9PlxuICAgICAgbm9ja0JpdEdvKClcbiAgICAgICAgLmdldChgL2FwaS92Mi8ke2NvaW4uZ2V0Q2hhaW4oKX0va2V5LyR7Z2V0S2V5SWQoayl9YClcbiAgICAgICAgLnJlcGx5KDIwMCwgaylcbiAgICAgICAgLnBlcnNpc3QoKVxuICAgICksXG4gIF07XG59XG5cbnR5cGUgQWRkcmVzcyA9IHtcbiAgYWRkcmVzczogc3RyaW5nO1xuICBjaGFpbjogbnVtYmVyO1xuICBpbmRleDogbnVtYmVyO1xuICBjb2luU3BlY2lmaWM6IHVua25vd247XG59O1xuXG5mdW5jdGlvbiBub2NrV2FsbGV0QWRkcmVzcyhjb2luOiBBYnN0cmFjdFV0eG9Db2luLCB3YWxsZXRJZDogc3RyaW5nLCBhZGRyZXNzOiBBZGRyZXNzKTogbm9jay5TY29wZSB7XG4gIHJldHVybiBub2NrQml0R28oKVxuICAgIC5nZXQoYC9hcGkvdjIvJHtjb2luLmdldENoYWluKCl9L3dhbGxldC8ke3dhbGxldElkfS9hZGRyZXNzLyR7YWRkcmVzcy5hZGRyZXNzfWApXG4gICAgLnJlcGx5KDIwMCwge1xuICAgICAgYWRkcmVzczogYWRkcmVzcy5hZGRyZXNzLFxuICAgICAgY2hhaW46IGFkZHJlc3MuY2hhaW4sXG4gICAgICBpbmRleDogYWRkcmVzcy5pbmRleCxcbiAgICAgIGNvaW46IGNvaW4uZ2V0Q2hhaW4oKSxcbiAgICAgIHdhbGxldDogd2FsbGV0SWQsXG4gICAgICBjb2luU3BlY2lmaWM6IGFkZHJlc3MuY29pblNwZWNpZmljLFxuICAgIH0pXG4gICAgLnBlcnNpc3QoKTtcbn1cblxuLyoqXG4gKiBTZXR1cCB0ZXN0IGZvciBjcm9zcy1jaGFpbiByZWNvdmVyeS5cbiAqXG4gKiBVc2VycyBjYW4gcmVjZWl2ZSBkZXBvc2l0cyBvbiB3YWxsZXQgYWRkcmVzc2VzIHRoYXQgYXJlIG9uIGEgZGlmZmVyZW50IGNoYWluLlxuICpcbiAqIEZvciBpbnN0YW5jZSwgYSB1c2VyIGNhbiByZWNlaXZlIGxpdGVjb2luIG9uIGEgYml0Y29pbiB3YWxsZXQuXG4gKiBUaGlzIG1lYW5zIHRoYXQgdGhlIGxpdGVjb2luIGJsb2NrY2hhaW4gaGFzIGEgdHJhbnNhY3Rpb24gd2l0aCBvdXRwdXRzIHRoYXQgYXJlIHNwZW5kYWJsZVxuICogd2l0aCBrZXlzIHRoYXQgd2VyZSBvcmlnaW5hbGx5IGNyZWF0ZWQgZm9yIGEgQml0R28gQlRDIHdhbGxldC5cbiAqIEluIHRoaXMgZXhhbXBsZSwgTFRDIGlzIHRoZSBcInNvdXJjZSBjb2luXCIgYW5kIEJUQyBpcyB0aGUgXCJyZWNvdmVyeSBjb2luXCJcbiAqIEluIGNhc2VzIGxpa2UgdGhlc2Ugd2UgbXVzdCB1c2UgY29uc3RydWN0IGEgdHJhbnNhY3Rpb24gZm9yIGxpdGVjb2luIG5ldHdvcmsgdXNpbmcga2V5cyBvZiB0aGVcbiAqIGJpdGNvaW4gd2FsbGV0LlxuICpcbiAqIEBwYXJhbSBzb3VyY2VDb2luIC0gdGhlIGNvaW4gdG8gY29uc3RydWN0IHRoZSB0cmFuc2FjdGlvbiBmb3JcbiAqIEBwYXJhbSByZWNvdmVyeUNvaW4gLSB0aGUgY29pbiB0aGUgcmVjZWl2aW5nIHdhbGxldCB3YXMgc2V0IHVwIGZvclxuICovXG5mdW5jdGlvbiBydW48VE51bWJlciBleHRlbmRzIG51bWJlciB8IGJpZ2ludCA9IG51bWJlcj4oc291cmNlQ29pbjogQWJzdHJhY3RVdHhvQ29pbiwgcmVjb3ZlcnlDb2luOiBBYnN0cmFjdFV0eG9Db2luKSB7XG4gIGRlc2NyaWJlKGBDcm9zcy1DaGFpbiBSZWNvdmVyeSBbc291cmNlQ29pbj0ke3NvdXJjZUNvaW4uZ2V0Q2hhaW4oKX0gcmVjb3ZlcnlDb2luPSR7cmVjb3ZlcnlDb2luLmdldENoYWluKCl9XWAsIGZ1bmN0aW9uICgpIHtcbiAgICBjb25zdCB3YWxsZXRLZXlzID0gZ2V0RGVmYXVsdFdhbGxldEtleXMoKTtcbiAgICBjb25zdCByZWNvdmVyeVdhbGxldElkID0gJzVhYmFjZWJlMjhkNzJmYmQwN2UwYjhjYmJhMGZmMzllJztcbiAgICAvLyB0aGUgYWRkcmVzcyB0aGUgYWNjaWRlbnRhbCBkZXBvc2l0IHdlbnQgdG8sIGluIGJvdGggc291cmNlQ29pbiBhbmQgYWRkcmVzc0NvaW4gZm9ybWF0c1xuICAgIGNvbnN0IFtkZXBvc2l0QWRkcmVzc1NvdXJjZUNvaW4sIGRlcG9zaXRBZGRyZXNzUmVjb3ZlcnlDb2luXSA9IFtzb3VyY2VDb2luLCByZWNvdmVyeUNvaW5dLm1hcCgoY29pbikgPT5cbiAgICAgIGNvaW4uZ2VuZXJhdGVBZGRyZXNzKHsga2V5Y2hhaW5zOiBrZXljaGFpbnNCYXNlNTgsIGluZGV4OiAwIH0pXG4gICAgKTtcbiAgICAvLyB0aGUgYWRkcmVzcyB3aGVyZSB3ZSB3YW50IHRvIHJlY292ZXIgb3VyIGZ1bmRzIHRvXG4gICAgY29uc3QgcmVjb3ZlcnlBZGRyZXNzID0gc291cmNlQ29pbi5nZW5lcmF0ZUFkZHJlc3MoeyBrZXljaGFpbnM6IGtleWNoYWluc0Jhc2U1OCwgaW5kZXg6IDEgfSkuYWRkcmVzcztcbiAgICBjb25zdCBub2Nrczogbm9jay5TY29wZVtdID0gW107XG5cbiAgICBsZXQgZGVwb3NpdFR4OiB1dHhvbGliLmJpdGdvLlV0eG9UcmFuc2FjdGlvbjxUTnVtYmVyPjtcblxuICAgIGZ1bmN0aW9uIGdldERlcG9zaXRVbnNwZW50cygpOiB1dHhvbGliLmJpdGdvLlVuc3BlbnQ8VE51bWJlcj5bXSB7XG4gICAgICByZXR1cm4gW1xuICAgICAgICBtb2NrVW5zcGVudDxUTnVtYmVyPihcbiAgICAgICAgICBzb3VyY2VDb2luLm5ldHdvcmssXG4gICAgICAgICAgd2FsbGV0S2V5cyxcbiAgICAgICAgICAncDJzaCcsXG4gICAgICAgICAgMCxcbiAgICAgICAgICAoc291cmNlQ29pbi5hbW91bnRUeXBlID09PSAnYmlnaW50JyA/IEJpZ0ludCgnMTA5OTk5OTk4MDAwMDAwMDEnKSA6IDFlOCkgYXMgVE51bWJlclxuICAgICAgICApLFxuICAgICAgXTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXREZXBvc2l0VHJhbnNhY3Rpb24oKTogdXR4b2xpYi5iaXRnby5VdHhvVHJhbnNhY3Rpb248VE51bWJlcj4ge1xuICAgICAgcmV0dXJuIGNyZWF0ZUZ1bGxTaWduZWRUcmFuc2FjdGlvbjxUTnVtYmVyPihcbiAgICAgICAgc291cmNlQ29pbi5uZXR3b3JrLFxuICAgICAgICBnZXREZXBvc2l0VW5zcGVudHMoKSxcbiAgICAgICAgZGVwb3NpdEFkZHJlc3NTb3VyY2VDb2luLmFkZHJlc3MsXG4gICAgICAgIGdldERlZmF1bHRXYWxsZXRVbnNwZW50U2lnbmVyKClcbiAgICAgICk7XG4gICAgfVxuXG4gICAgYmVmb3JlKCdwcmVwYXJlIGRlcG9zaXQgdHgnLCBmdW5jdGlvbiAoKSB7XG4gICAgICBkZXBvc2l0VHggPSBnZXREZXBvc2l0VHJhbnNhY3Rpb24oKTtcbiAgICB9KTtcblxuICAgIGZ1bmN0aW9uIGdldFJlY292ZXJ5VW5zcGVudHMoKTogV2FsbGV0VW5zcGVudDxUTnVtYmVyPltdIHtcbiAgICAgIHJldHVybiBbXG4gICAgICAgIHtcbiAgICAgICAgICBpZDogZGVwb3NpdFR4LmdldElkKCksXG4gICAgICAgICAgYWRkcmVzczogZGVwb3NpdEFkZHJlc3NTb3VyY2VDb2luLmFkZHJlc3MsXG4gICAgICAgICAgY2hhaW46IGRlcG9zaXRBZGRyZXNzU291cmNlQ29pbi5jaGFpbiBhcyB1dHhvbGliLmJpdGdvLkNoYWluQ29kZSxcbiAgICAgICAgICBpbmRleDogZGVwb3NpdEFkZHJlc3NTb3VyY2VDb2luLmluZGV4LFxuICAgICAgICAgIHZhbHVlOiBkZXBvc2l0VHgub3V0c1swXS52YWx1ZSxcbiAgICAgICAgfSxcbiAgICAgIF07XG4gICAgfVxuXG4gICAgYmVmb3JlKCdzZXR1cCBub2NrcycsIGZ1bmN0aW9uICgpIHtcbiAgICAgIG5vY2tzLnB1c2goLi4ubm9ja1dhbGxldChyZWNvdmVyeUNvaW4sIHJlY292ZXJ5V2FsbGV0SWQsIGtleWNoYWluc0Jhc2U1OCkpO1xuICAgICAgbm9ja3MucHVzaChub2NrV2FsbGV0QWRkcmVzcyhyZWNvdmVyeUNvaW4sIHJlY292ZXJ5V2FsbGV0SWQsIGRlcG9zaXRBZGRyZXNzUmVjb3ZlcnlDb2luKSk7XG4gICAgfSk7XG5cbiAgICBhZnRlcihmdW5jdGlvbiAoKSB7XG4gICAgICBub2Nrcy5mb3JFYWNoKChuKSA9PiBuLmRvbmUoKSk7XG4gICAgfSk7XG5cbiAgICBhZnRlcihmdW5jdGlvbiAoKSB7XG4gICAgICBub2NrLmNsZWFuQWxsKCk7XG4gICAgfSk7XG5cbiAgICBhZnRlckVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgc2lub24ucmVzdG9yZSgpO1xuICAgIH0pO1xuXG4gICAgZnVuY3Rpb24gdGVzdE1hdGNoRml4dHVyZShcbiAgICAgIG5hbWU6IHN0cmluZyxcbiAgICAgIGdldFJlY292ZXJ5UmVzdWx0OiAoKSA9PiBDcm9zc0NoYWluUmVjb3ZlcnlTaWduZWQ8VE51bWJlcj4gfCBDcm9zc0NoYWluUmVjb3ZlcnlVbnNpZ25lZDxUTnVtYmVyPlxuICAgICkge1xuICAgICAgaXQoYHNob3VsZCBtYXRjaCBmaXh0dXJlICgke25hbWV9KWAsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgY29uc3QgcmVjb3ZlcnkgPSBnZXRSZWNvdmVyeVJlc3VsdCgpO1xuICAgICAgICBsZXQgcmVjb3ZlcnlPYmogPSB7XG4gICAgICAgICAgLi4ucmVjb3ZlcnksXG4gICAgICAgICAgdHg6IHRyYW5zYWN0aW9uSGV4VG9PYmoocmVjb3ZlcnkudHhIZXggYXMgc3RyaW5nLCBzb3VyY2VDb2luLm5ldHdvcmssIHNvdXJjZUNvaW4uYW1vdW50VHlwZSksXG4gICAgICAgIH07XG4gICAgICAgIGlmIChzb3VyY2VDb2luLmFtb3VudFR5cGUgPT09ICdiaWdpbnQnKSB7XG4gICAgICAgICAgcmVjb3ZlcnlPYmogPSBKU09OLnBhcnNlKFxuICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkocmVjb3ZlcnlPYmosIChrLCB2KSA9PiB7XG4gICAgICAgICAgICAgIGlmICh0eXBlb2YgdiA9PT0gJ2JpZ2ludCcpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdi50b1N0cmluZygpO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJldHVybiB2O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KVxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgc2hvdWxkRXF1YWxKU09OKFxuICAgICAgICAgIHJlY292ZXJ5T2JqLFxuICAgICAgICAgIGF3YWl0IGdldEZpeHR1cmUoc291cmNlQ29pbiwgYHJlY292ZXJ5L2Nyb3NzQ2hhaW5SZWNvdmVyeS0ke3JlY292ZXJ5Q29pbi5nZXRDaGFpbigpfS0ke25hbWV9YCwgcmVjb3ZlcnlPYmopXG4gICAgICAgICk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBjaGVja1JlY292ZXJ5VHJhbnNhY3Rpb25TaWduYXR1cmUodHg6IHN0cmluZyB8IHV0eG9saWIuYml0Z28uVXR4b1RyYW5zYWN0aW9uPFROdW1iZXI+KSB7XG4gICAgICBpZiAodHlwZW9mIHR4ID09PSAnc3RyaW5nJykge1xuICAgICAgICB0eCA9IHV0eG9saWIuYml0Z28uY3JlYXRlVHJhbnNhY3Rpb25Gcm9tQnVmZmVyPFROdW1iZXI+KEJ1ZmZlci5mcm9tKHR4LCAnaGV4JyksIHNvdXJjZUNvaW4ubmV0d29yaywge1xuICAgICAgICAgIGFtb3VudFR5cGU6IHNvdXJjZUNvaW4uYW1vdW50VHlwZSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICBjb25zdCB1bnNwZW50cyA9IGdldFJlY292ZXJ5VW5zcGVudHMoKTtcbiAgICAgIHNob3VsZC5lcXVhbCh0eC5pbnMubGVuZ3RoLCB1bnNwZW50cy5sZW5ndGgpO1xuICAgICAgdHguaW5zLmZvckVhY2goKGlucHV0LCBpKSA9PiB7XG4gICAgICAgIGFzc2VydCh0eXBlb2YgdHggIT09ICdzdHJpbmcnKTtcbiAgICAgICAgdXR4b2xpYi5iaXRnb1xuICAgICAgICAgIC52ZXJpZnlTaWduYXR1cmVXaXRoVW5zcGVudDxUTnVtYmVyPih0eCwgaSwgZ2V0UmVjb3ZlcnlVbnNwZW50cygpLCB3YWxsZXRLZXlzKVxuICAgICAgICAgIC5zaG91bGQuZXFsKFt0cnVlLCBmYWxzZSwgZmFsc2VdKTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGl0KCdzaG91bGQgdGVzdCBzaWduZWQgY3Jvc3MgY2hhaW4gcmVjb3ZlcnknLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBnZXRSZWNvdmVyeVByb3ZpZGVyU3R1YiA9IHNpbm9uXG4gICAgICAgIC5zdHViKEFic3RyYWN0VXR4b0NvaW4ucHJvdG90eXBlLCAnZ2V0UmVjb3ZlcnlQcm92aWRlcicpXG4gICAgICAgIC5yZXR1cm5zKG5ldyBNb2NrQ3Jvc3NDaGFpblJlY292ZXJ5UHJvdmlkZXI8VE51bWJlcj4oc291cmNlQ29pbiwgZ2V0RGVwb3NpdFVuc3BlbnRzKCksIGRlcG9zaXRUeCkpO1xuICAgICAgY29uc3QgcGFyYW1zID0ge1xuICAgICAgICByZWNvdmVyeUNvaW4sXG4gICAgICAgIHR4aWQ6IGRlcG9zaXRUeC5nZXRJZCgpLFxuICAgICAgICByZWNvdmVyeUFkZHJlc3MsXG4gICAgICAgIHdhbGxldDogcmVjb3ZlcnlXYWxsZXRJZCxcbiAgICAgIH07XG4gICAgICBjb25zdCBzaWduZWRSZWNvdmVyeSA9IChhd2FpdCBzb3VyY2VDb2luLnJlY292ZXJGcm9tV3JvbmdDaGFpbjxUTnVtYmVyPih7XG4gICAgICAgIC4uLnBhcmFtcyxcbiAgICAgICAgeHBydjoga2V5Y2hhaW5zQmFzZTU4WzBdLnBydixcbiAgICAgIH0pKSBhcyBDcm9zc0NoYWluUmVjb3ZlcnlTaWduZWQ8VE51bWJlcj47XG4gICAgICBzaG91bGQuZXF1YWwoZ2V0UmVjb3ZlcnlQcm92aWRlclN0dWIuY2FsbENvdW50LCAxKTtcblxuICAgICAgdGVzdE1hdGNoRml4dHVyZSgnc2lnbmVkJywgKCkgPT4gc2lnbmVkUmVjb3ZlcnkpO1xuXG4gICAgICBpdCgnc2hvdWxkIGhhdmUgdmFsaWQgc2lnbmF0dXJlcyBmb3Igc2lnbmVkIHJlY292ZXJ5JywgZnVuY3Rpb24gKCkge1xuICAgICAgICBjaGVja1JlY292ZXJ5VHJhbnNhY3Rpb25TaWduYXR1cmUoc2lnbmVkUmVjb3ZlcnkudHhIZXggYXMgc3RyaW5nKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCB0ZXN0IHVuc2lnbmVkIGNyb3NzIGNoYWluIHJlY292ZXJ5JywgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgZ2V0UmVjb3ZlcnlQcm92aWRlclN0dWIgPSBzaW5vblxuICAgICAgICAuc3R1YihBYnN0cmFjdFV0eG9Db2luLnByb3RvdHlwZSwgJ2dldFJlY292ZXJ5UHJvdmlkZXInKVxuICAgICAgICAucmV0dXJucyhuZXcgTW9ja0Nyb3NzQ2hhaW5SZWNvdmVyeVByb3ZpZGVyPFROdW1iZXI+KHNvdXJjZUNvaW4sIGdldERlcG9zaXRVbnNwZW50cygpLCBkZXBvc2l0VHgpKTtcbiAgICAgIGNvbnN0IHBhcmFtcyA9IHtcbiAgICAgICAgcmVjb3ZlcnlDb2luLFxuICAgICAgICB0eGlkOiBkZXBvc2l0VHguZ2V0SWQoKSxcbiAgICAgICAgcmVjb3ZlcnlBZGRyZXNzLFxuICAgICAgICB3YWxsZXQ6IHJlY292ZXJ5V2FsbGV0SWQsXG4gICAgICB9O1xuICAgICAgY29uc3QgdW5zaWduZWRSZWNvdmVyeSA9IChhd2FpdCBzb3VyY2VDb2luLnJlY292ZXJGcm9tV3JvbmdDaGFpbjxUTnVtYmVyPih7XG4gICAgICAgIC4uLnBhcmFtcyxcbiAgICAgICAgc2lnbmVkOiBmYWxzZSxcbiAgICAgIH0pKSBhcyBDcm9zc0NoYWluUmVjb3ZlcnlVbnNpZ25lZDxUTnVtYmVyPjtcbiAgICAgIHNob3VsZC5lcXVhbChnZXRSZWNvdmVyeVByb3ZpZGVyU3R1Yi5jYWxsQ291bnQsIDEpO1xuXG4gICAgICB0ZXN0TWF0Y2hGaXh0dXJlKCd1bnNpZ25lZCcsICgpID0+IHVuc2lnbmVkUmVjb3ZlcnkpO1xuXG4gICAgICBpdCgnc2hvdWxkIGJlIHNpZ25hYmxlIGZvciB1bnNpZ25lZCByZWNvdmVyeScsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgY29uc3Qgc2lnbmVkVHggPSBhd2FpdCBzb3VyY2VDb2luLnNpZ25UcmFuc2FjdGlvbjxUTnVtYmVyPih7XG4gICAgICAgICAgdHhQcmVidWlsZDogdW5zaWduZWRSZWNvdmVyeSxcbiAgICAgICAgICBwcnY6IGtleWNoYWluc0Jhc2U1OFswXS5wcnYsXG4gICAgICAgICAgcHViczoga2V5Y2hhaW5zQmFzZTU4Lm1hcCgoaykgPT4gay5wdWIpIGFzIFRyaXBsZTxzdHJpbmc+LFxuICAgICAgICB9KTtcbiAgICAgICAgY2hlY2tSZWNvdmVyeVRyYW5zYWN0aW9uU2lnbmF0dXJlKChzaWduZWRUeCBhcyB7IHR4SGV4OiBzdHJpbmcgfSkudHhIZXgpO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH0pO1xufVxuXG5mdW5jdGlvbiBpc1N1cHBvcnRlZENyb3NzQ2hhaW5SZWNvdmVyeShzb3VyY2VDb2luOiBBYnN0cmFjdFV0eG9Db2luLCByZWNvdmVyeUNvaW46IEFic3RyYWN0VXR4b0NvaW4pOiBib29sZWFuIHtcbiAgcmV0dXJuIHN1cHBvcnRlZENyb3NzQ2hhaW5SZWNvdmVyaWVzW3NvdXJjZUNvaW4uZ2V0RmFtaWx5KCldPy5pbmNsdWRlcyhyZWNvdmVyeUNvaW4uZ2V0RmFtaWx5KCkpO1xufVxuXG51dHhvQ29pbnMuZm9yRWFjaCgoY29pbikgPT4ge1xuICB1dHhvQ29pbnNcbiAgICAuZmlsdGVyKFxuICAgICAgKG90aGVyQ29pbikgPT5cbiAgICAgICAgY29pbiAhPT0gb3RoZXJDb2luICYmXG4gICAgICAgIGlzU3VwcG9ydGVkQ3Jvc3NDaGFpblJlY292ZXJ5KGNvaW4sIG90aGVyQ29pbikgJiZcbiAgICAgICAgKCh1dHhvbGliLmlzTWFpbm5ldChjb2luLm5ldHdvcmspICYmIHV0eG9saWIuaXNNYWlubmV0KG90aGVyQ29pbi5uZXR3b3JrKSkgfHxcbiAgICAgICAgICAodXR4b2xpYi5pc1Rlc3RuZXQoY29pbi5uZXR3b3JrKSAmJiB1dHhvbGliLmlzVGVzdG5ldChvdGhlckNvaW4ubmV0d29yaykpKVxuICAgIClcbiAgICAuZm9yRWFjaCgob3RoZXJDb2luKSA9PiB7XG4gICAgICBpZiAoY29pbi5hbW91bnRUeXBlID09PSAnYmlnaW50Jykge1xuICAgICAgICBydW48YmlnaW50Pihjb2luLCBvdGhlckNvaW4pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcnVuKGNvaW4sIG90aGVyQ29pbik7XG4gICAgICB9XG4gICAgfSk7XG59KTtcblxuZGVzY3JpYmUoYENyb3NzLUNoYWluIFJlY292ZXJ5IGdldFdhbGxldGAsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgY29uc3QgYml0Z28gPSBkZWZhdWx0Qml0R287XG4gIGNvbnN0IHJlY292ZXJ5Q29pbiA9IGdldFV0eG9Db2luKCdidGMnKTtcbiAgY29uc3QgcmVjb3ZlcnlXYWxsZXRJZCA9ICc1YWJhY2ViZTI4ZDcyZmJkMDdlMGI4Y2JiYTBmZjM5ZSc7XG5cbiAgaXQoJ3Nob3VsZCBzZWFyY2ggdjEgd2FsbGV0cyBpZiB0aGUgdjIgZW5kcG9pbnQgcmVzcG9uZHMgd2l0aCBhIDR4eCBlcnJvcicsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICBjb25zdCBlcnJvclJlc3BvbnNlcyA9IFs0MDAsIDQwNF07XG5cbiAgICBmb3IgKGNvbnN0IGVycm9yIG9mIGVycm9yUmVzcG9uc2VzKSB7XG4gICAgICBjb25zdCBub2NrVjJXYWxsZXQgPSBub2NrQml0R28oYml0Z28pXG4gICAgICAgIC5nZXQoYC9hcGkvdjIvJHtyZWNvdmVyeUNvaW4uZ2V0Q2hhaW4oKX0vd2FsbGV0LyR7cmVjb3ZlcnlXYWxsZXRJZH1gKVxuICAgICAgICAucmVwbHkoZXJyb3IpO1xuICAgICAgY29uc3Qgbm9ja1YxV2FsbGV0ID0gbm9ja0JpdEdvKGJpdGdvKS5nZXQoYC9hcGkvdjEvd2FsbGV0LyR7cmVjb3ZlcnlXYWxsZXRJZH1gKS5yZXBseShlcnJvcik7XG4gICAgICBhd2FpdCBhc3NlcnQucmVqZWN0cyhcbiAgICAgICAgKCkgPT4gZ2V0V2FsbGV0KGJpdGdvLCByZWNvdmVyeUNvaW4sIHJlY292ZXJ5V2FsbGV0SWQpLFxuICAgICAgICBFcnJvcihgY291bGQgbm90IGdldCB3YWxsZXQgJHtyZWNvdmVyeVdhbGxldElkfSBmcm9tIHYxIG9yIHYyOiBBcGlSZXNwb25zZUVycm9yOiAke2Vycm9yfWApXG4gICAgICApO1xuICAgICAgbm9ja1YyV2FsbGV0LmRvbmUoKTtcbiAgICAgIG5vY2tWMVdhbGxldC5kb25lKCk7XG4gICAgfVxuICB9KTtcblxuICBpdCgnc2hvdWxkIHRocm93IGFuIGVycm9yIGlmIHRoZSB2MiBlbmRwb2ludCByZXNwb25kcyB3aXRoIGEgNXh4IGVycm9yJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgIGNvbnN0IGVycm9yUmVzcG9uc2VzID0gWzUwMF07XG4gICAgZm9yIChjb25zdCBlcnJvciBvZiBlcnJvclJlc3BvbnNlcykge1xuICAgICAgY29uc3Qgbm9ja1YyV2FsbGV0ID0gbm9ja0JpdEdvKGJpdGdvKVxuICAgICAgICAuZ2V0KGAvYXBpL3YyLyR7cmVjb3ZlcnlDb2luLmdldENoYWluKCl9L3dhbGxldC8ke3JlY292ZXJ5V2FsbGV0SWR9YClcbiAgICAgICAgLnJlcGx5KGVycm9yKTtcbiAgICAgIGF3YWl0IGFzc2VydC5yZWplY3RzKCgpID0+IGdldFdhbGxldChiaXRnbywgcmVjb3ZlcnlDb2luLCByZWNvdmVyeVdhbGxldElkKSwge1xuICAgICAgICBuYW1lOiAnQXBpUmVzcG9uc2VFcnJvcicsXG4gICAgICAgIHN0YXR1czogNTAwLFxuICAgICAgICByZXN1bHQ6IHt9LFxuICAgICAgICBpbnZhbGlkVG9rZW46IGZhbHNlLFxuICAgICAgICBuZWVkc09UUDogZmFsc2UsXG4gICAgICB9KTtcbiAgICAgIG5vY2tWMldhbGxldC5kb25lKCk7XG4gICAgfVxuICB9KTtcbn0pO1xuIl19Выполнить команду
Для локальной разработки. Не используйте в интернете!