PHP WebShell

Текущая директория: /opt/BitGoJS/modules/utxo-lib/dist/test/bitgo/wallet

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

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const assert = require("assert");
const src_1 = require("../../../src");
const bitgo_1 = require("../../../src/bitgo");
const testutil_1 = require("../../../src/testutil");
const transaction_util_1 = require("../../transaction_util");
const mock_1 = require("../../../src/testutil/mock");
const CHANGE_INDEX = 100;
const FEE = BigInt(100);
function getScriptTypes2Of3() {
    // FIXME(BG-66941): p2trMusig2 signing does not work in this test suite yet
    //  because the test suite is written with TransactionBuilder
    return bitgo_1.outputScripts.scriptTypes2Of3.filter((scriptType) => scriptType !== 'p2trMusig2');
}
describe('WalletUnspent', function () {
    const network = src_1.networks.bitcoin;
    const walletKeys = (0, testutil_1.getDefaultWalletKeys)();
    const hash = Buffer.alloc(32).fill(0xff);
    hash[0] = 0; // show endianness
    const input = { hash, index: 0 };
    const expectedOutPoint = {
        txid: 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00',
        vout: 0,
    };
    it('parses and formats txid', function () {
        assert.deepStrictEqual((0, bitgo_1.getOutputIdForInput)(input), expectedOutPoint);
        assert.deepStrictEqual((0, bitgo_1.formatOutputId)(expectedOutPoint), 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00:0');
        assert.deepStrictEqual((0, bitgo_1.parseOutputId)((0, bitgo_1.formatOutputId)(expectedOutPoint)), expectedOutPoint);
    });
    it('identifies wallet unspents', function () {
        const unspent = {
            id: (0, bitgo_1.formatOutputId)(expectedOutPoint),
            address: (0, bitgo_1.getWalletAddress)(walletKeys, 0, 0, network),
            value: 1e8,
        };
        assert.strictEqual((0, bitgo_1.isWalletUnspent)(unspent), false);
        assert.strictEqual((0, bitgo_1.isWalletUnspent)({ ...unspent, chain: 0, index: 0 }), true);
    });
    function constructAndSignTransactionUsingPsbt(unspents, signer, cosigner, outputType) {
        const psbt = (0, bitgo_1.createPsbtForNetwork)({ network });
        const total = BigInt((0, bitgo_1.unspentSum)(unspents, 'bigint'));
        (0, bitgo_1.addWalletOutputToPsbt)(psbt, walletKeys, (0, bitgo_1.getInternalChainCode)(outputType), CHANGE_INDEX, total - FEE);
        unspents.forEach((u) => {
            if ((0, bitgo_1.isWalletUnspent)(u)) {
                (0, bitgo_1.addWalletUnspentToPsbt)(psbt, u, walletKeys, signer, cosigner);
            }
            else {
                throw new Error(`invalid unspent`);
            }
        });
        // TODO: Test rederiving scripts from PSBT and keys only
        psbt.signAllInputsHD(walletKeys[signer]);
        psbt.signAllInputsHD(walletKeys[cosigner]);
        assert(psbt.validateSignaturesOfAllInputs());
        psbt.finalizeAllInputs();
        // extract transaction has a return type of Transaction instead of UtxoTransaction
        const tx = psbt.extractTransaction();
        const psbt2 = (0, bitgo_1.createPsbtFromTransaction)(tx, unspents.map((u) => ({ ...(0, bitgo_1.toPrevOutput)(u, network), prevTx: u.prevTx })));
        assert(psbt2.validateSignaturesOfAllInputs());
        return tx;
    }
    function constructAndSignTransactionUsingTransactionBuilder(unspents, signer, cosigner, amountType = 'number', outputType) {
        const txb = (0, bitgo_1.createTransactionBuilderForNetwork)(network);
        const total = BigInt((0, bitgo_1.unspentSum)(unspents, amountType));
        // Kinda weird, treating entire value as change, but tests the relevant paths
        txb.addOutput((0, bitgo_1.getWalletAddress)(walletKeys, (0, bitgo_1.getInternalChainCode)(outputType), CHANGE_INDEX, network), (0, bitgo_1.toTNumber)(total - FEE, amountType));
        unspents.forEach((u) => {
            (0, bitgo_1.addToTransactionBuilder)(txb, u);
        });
        unspents.forEach((u, i) => {
            if ((0, mock_1.isReplayProtectionUnspent)(u, network)) {
                (0, bitgo_1.signInputP2shP2pk)(txb, i, mock_1.replayProtectionKeyPair);
            }
        });
        [
            bitgo_1.WalletUnspentSigner.from(walletKeys, walletKeys[signer], walletKeys[cosigner]),
            bitgo_1.WalletUnspentSigner.from(walletKeys, walletKeys[cosigner], walletKeys[signer]),
        ].forEach((walletSigner, nSignature) => {
            unspents.forEach((u, i) => {
                if ((0, bitgo_1.isWalletUnspent)(u)) {
                    (0, bitgo_1.signInputWithUnspent)(txb, i, u, walletSigner);
                }
                else if ((0, mock_1.isReplayProtectionUnspent)(u, network)) {
                    return;
                }
                else {
                    throw new Error(`unexpected unspent ${u.id}`);
                }
            });
            const tx = nSignature === 0 ? txb.buildIncomplete() : txb.build();
            // Verify each signature for the unspent
            unspents.forEach((u, i) => {
                if ((0, mock_1.isReplayProtectionUnspent)(u, network)) {
                    // signature verification not implemented for replay protection unspents
                    return;
                }
                assert.deepStrictEqual((0, bitgo_1.verifySignatureWithUnspent)(tx, i, unspents, walletKeys), walletKeys.triple.map((k) => k === walletKeys[signer] || (nSignature === 1 && k === walletKeys[cosigner])));
            });
        });
        return txb.build();
    }
    function validateLockTimeAndSequence(transaction) {
        // locktime should default to 0 and sequence to 0xffffffff for all inputs
        assert.deepStrictEqual(transaction.locktime, 0);
        const inputs = transaction.ins;
        for (const input of inputs) {
            assert.deepStrictEqual(input.sequence, 0xffffffff);
        }
    }
    function runTestSignUnspents({ inputScriptTypes, outputScriptType, signer, cosigner, amountType, testOutputAmount, }) {
        it(`can be signed [inputs=${inputScriptTypes} signer=${signer} cosigner=${cosigner} amountType=${amountType}]`, function () {
            const unspents = inputScriptTypes.map((t, i) => {
                if (bitgo_1.outputScripts.isScriptType2Of3(t)) {
                    return (0, mock_1.mockWalletUnspent)(network, testOutputAmount, {
                        keys: walletKeys,
                        chain: (0, bitgo_1.getExternalChainCode)(t),
                        vout: i,
                    });
                }
                if (t === 'p2shP2pk') {
                    return (0, mock_1.mockReplayProtectionUnspent)(network, (0, bitgo_1.toTNumber)(1000, amountType));
                }
                throw new Error(`invalid input type ${t}`);
            });
            const txbTransaction = constructAndSignTransactionUsingTransactionBuilder(unspents, signer, cosigner, amountType, outputScriptType);
            validateLockTimeAndSequence(txbTransaction);
            if (amountType === 'bigint') {
                if (inputScriptTypes.includes('p2shP2pk')) {
                    // FIMXE(BG-47824): add p2shP2pk support for Psbt
                    return;
                }
                const psbtTransaction = constructAndSignTransactionUsingPsbt(unspents, signer, cosigner, outputScriptType);
                assert.deepStrictEqual(txbTransaction.toBuffer(), psbtTransaction.toBuffer());
                validateLockTimeAndSequence(psbtTransaction);
            }
        });
    }
    function getInputScripts() {
        return getScriptTypes2Of3().flatMap((t) => [
            [t, t],
            [t, t, 'p2shP2pk'],
        ]);
    }
    function getSignerPairs() {
        const keyNames = ['user', 'backup', 'bitgo'];
        return keyNames.flatMap((signer) => keyNames.flatMap((cosigner) => (signer === cosigner ? [] : [[signer, cosigner]])));
    }
    getInputScripts().forEach((inputScriptTypes) => {
        getSignerPairs().forEach(([signer, cosigner]) => {
            runTestSignUnspents({
                inputScriptTypes,
                outputScriptType: 'p2sh',
                signer,
                cosigner,
                amountType: 'number',
                testOutputAmount: transaction_util_1.defaultTestOutputAmount,
            });
            runTestSignUnspents({
                inputScriptTypes,
                outputScriptType: 'p2sh',
                signer,
                cosigner,
                amountType: 'bigint',
                testOutputAmount: BigInt('10000000000000000'),
            });
        });
    });
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV2FsbGV0VW5zcGVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Rlc3QvYml0Z28vd2FsbGV0L1dhbGxldFVuc3BlbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxpQ0FBaUM7QUFFakMsc0NBQXFEO0FBQ3JELDhDQTBCNEI7QUFFNUIsb0RBQTZEO0FBQzdELDZEQUFpRTtBQUNqRSxxREFLb0M7QUFFcEMsTUFBTSxZQUFZLEdBQUcsR0FBRyxDQUFDO0FBQ3pCLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUl4QixTQUFTLGtCQUFrQjtJQUN6QiwyRUFBMkU7SUFDM0UsNkRBQTZEO0lBQzdELE9BQU8scUJBQWEsQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxVQUFVLEtBQUssWUFBWSxDQUFDLENBQUM7QUFDM0YsQ0FBQztBQUVELFFBQVEsQ0FBQyxlQUFlLEVBQUU7SUFDeEIsTUFBTSxPQUFPLEdBQUcsY0FBUSxDQUFDLE9BQU8sQ0FBQztJQUNqQyxNQUFNLFVBQVUsR0FBRyxJQUFBLCtCQUFvQixHQUFFLENBQUM7SUFDMUMsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDekMsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLGtCQUFrQjtJQUMvQixNQUFNLEtBQUssR0FBRyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFDakMsTUFBTSxnQkFBZ0IsR0FBZTtRQUNuQyxJQUFJLEVBQUUsa0VBQWtFO1FBQ3hFLElBQUksRUFBRSxDQUFDO0tBQ1IsQ0FBQztJQUVGLEVBQUUsQ0FBQyx5QkFBeUIsRUFBRTtRQUM1QixNQUFNLENBQUMsZUFBZSxDQUFDLElBQUEsMkJBQW1CLEVBQUMsS0FBSyxDQUFDLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztRQUNyRSxNQUFNLENBQUMsZUFBZSxDQUNwQixJQUFBLHNCQUFjLEVBQUMsZ0JBQWdCLENBQUMsRUFDaEMsb0VBQW9FLENBQ3JFLENBQUM7UUFDRixNQUFNLENBQUMsZUFBZSxDQUFDLElBQUEscUJBQWEsRUFBQyxJQUFBLHNCQUFjLEVBQUMsZ0JBQWdCLENBQUMsQ0FBQyxFQUFFLGdCQUFnQixDQUFDLENBQUM7SUFDNUYsQ0FBQyxDQUFDLENBQUM7SUFFSCxFQUFFLENBQUMsNEJBQTRCLEVBQUU7UUFDL0IsTUFBTSxPQUFPLEdBQVk7WUFDdkIsRUFBRSxFQUFFLElBQUEsc0JBQWMsRUFBQyxnQkFBZ0IsQ0FBQztZQUNwQyxPQUFPLEVBQUUsSUFBQSx3QkFBZ0IsRUFBQyxVQUFVLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxPQUFPLENBQUM7WUFDcEQsS0FBSyxFQUFFLEdBQUc7U0FDWCxDQUFDO1FBQ0YsTUFBTSxDQUFDLFdBQVcsQ0FBQyxJQUFBLHVCQUFlLEVBQUMsT0FBTyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDcEQsTUFBTSxDQUFDLFdBQVcsQ0FBQyxJQUFBLHVCQUFlLEVBQUMsRUFBRSxHQUFHLE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQWEsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzNGLENBQUMsQ0FBQyxDQUFDO0lBRUgsU0FBUyxvQ0FBb0MsQ0FDM0MsUUFBbUQsRUFDbkQsTUFBZSxFQUNmLFFBQWlCLEVBQ2pCLFVBQXdDO1FBRXhDLE1BQU0sSUFBSSxHQUFHLElBQUEsNEJBQW9CLEVBQUMsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQy9DLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxJQUFBLGtCQUFVLEVBQVMsUUFBUSxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDN0QsSUFBQSw2QkFBcUIsRUFBQyxJQUFJLEVBQUUsVUFBVSxFQUFFLElBQUEsNEJBQW9CLEVBQUMsVUFBVSxDQUFDLEVBQUUsWUFBWSxFQUFFLEtBQUssR0FBRyxHQUFHLENBQUMsQ0FBQztRQUVyRyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7WUFDckIsSUFBSSxJQUFBLHVCQUFlLEVBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDdkIsSUFBQSw4QkFBc0IsRUFBQyxJQUFJLEVBQUUsQ0FBQyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDaEUsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLE1BQU0sSUFBSSxLQUFLLENBQUMsaUJBQWlCLENBQUMsQ0FBQztZQUNyQyxDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7UUFFSCx3REFBd0Q7UUFDeEQsSUFBSSxDQUFDLGVBQWUsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUN6QyxJQUFJLENBQUMsZUFBZSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sQ0FBQyxJQUFJLENBQUMsNkJBQTZCLEVBQUUsQ0FBQyxDQUFDO1FBQzdDLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1FBQ3pCLGtGQUFrRjtRQUNsRixNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsa0JBQWtCLEVBQTZCLENBQUM7UUFFaEUsTUFBTSxLQUFLLEdBQUcsSUFBQSxpQ0FBeUIsRUFDckMsRUFBRSxFQUNGLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxHQUFHLElBQUEsb0JBQVksRUFBUyxDQUFDLEVBQUUsT0FBTyxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQ2pGLENBQUM7UUFDRixNQUFNLENBQUMsS0FBSyxDQUFDLDZCQUE2QixFQUFFLENBQUMsQ0FBQztRQUM5QyxPQUFPLEVBQUUsQ0FBQztJQUNaLENBQUM7SUFFRCxTQUFTLGtEQUFrRCxDQUN6RCxRQUE0QixFQUM1QixNQUFjLEVBQ2QsUUFBZ0IsRUFDaEIsYUFBa0MsUUFBUSxFQUMxQyxVQUF3QztRQUV4QyxNQUFNLEdBQUcsR0FBRyxJQUFBLDBDQUFrQyxFQUFVLE9BQU8sQ0FBQyxDQUFDO1FBQ2pFLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxJQUFBLGtCQUFVLEVBQVUsUUFBUSxFQUFFLFVBQVUsQ0FBQyxDQUFDLENBQUM7UUFDaEUsNkVBQTZFO1FBQzdFLEdBQUcsQ0FBQyxTQUFTLENBQ1gsSUFBQSx3QkFBZ0IsRUFBQyxVQUFVLEVBQUUsSUFBQSw0QkFBb0IsRUFBQyxVQUFVLENBQUMsRUFBRSxZQUFZLEVBQUUsT0FBTyxDQUFDLEVBQ3JGLElBQUEsaUJBQVMsRUFBVSxLQUFLLEdBQUcsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUM1QyxDQUFDO1FBQ0YsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFO1lBQ3JCLElBQUEsK0JBQXVCLEVBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ2xDLENBQUMsQ0FBQyxDQUFDO1FBQ0gsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUN4QixJQUFJLElBQUEsZ0NBQXlCLEVBQUMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQzFDLElBQUEseUJBQWlCLEVBQUMsR0FBRyxFQUFFLENBQUMsRUFBRSw4QkFBdUIsQ0FBQyxDQUFDO1lBQ3JELENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztRQUVIO1lBQ0UsMkJBQW1CLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxVQUFVLENBQUMsTUFBTSxDQUFDLEVBQUUsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQzlFLDJCQUFtQixDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsVUFBVSxDQUFDLFFBQVEsQ0FBQyxFQUFFLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztTQUMvRSxDQUFDLE9BQU8sQ0FBQyxDQUFDLFlBQVksRUFBRSxVQUFVLEVBQUUsRUFBRTtZQUNyQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFO2dCQUN4QixJQUFJLElBQUEsdUJBQWUsRUFBQyxDQUFDLENBQUMsRUFBRSxDQUFDO29CQUN2QixJQUFBLDRCQUFvQixFQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLFlBQVksQ0FBQyxDQUFDO2dCQUNoRCxDQUFDO3FCQUFNLElBQUksSUFBQSxnQ0FBeUIsRUFBQyxDQUFDLEVBQUUsT0FBTyxDQUFDLEVBQUUsQ0FBQztvQkFDakQsT0FBTztnQkFDVCxDQUFDO3FCQUFNLENBQUM7b0JBQ04sTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0JBQ2hELENBQUM7WUFDSCxDQUFDLENBQUMsQ0FBQztZQUVILE1BQU0sRUFBRSxHQUFHLFVBQVUsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxlQUFlLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2xFLHdDQUF3QztZQUN4QyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFO2dCQUN4QixJQUFJLElBQUEsZ0NBQXlCLEVBQUMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxFQUFFLENBQUM7b0JBQzFDLHdFQUF3RTtvQkFDeEUsT0FBTztnQkFDVCxDQUFDO2dCQUNELE1BQU0sQ0FBQyxlQUFlLENBQ3BCLElBQUEsa0NBQTBCLEVBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxRQUFRLEVBQUUsVUFBVSxDQUFDLEVBQ3ZELFVBQVUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssVUFBVSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FDM0csQ0FBQztZQUNKLENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7UUFFSCxPQUFPLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUNyQixDQUFDO0lBRUQsU0FBUywyQkFBMkIsQ0FDbEMsV0FBMkQ7UUFFM0QseUVBQXlFO1FBQ3pFLE1BQU0sQ0FBQyxlQUFlLENBQUMsV0FBVyxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNoRCxNQUFNLE1BQU0sR0FBRyxXQUFXLENBQUMsR0FBRyxDQUFDO1FBQy9CLEtBQUssTUFBTSxLQUFLLElBQUksTUFBTSxFQUFFLENBQUM7WUFDM0IsTUFBTSxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFLFVBQVUsQ0FBQyxDQUFDO1FBQ3JELENBQUM7SUFDSCxDQUFDO0lBRUQsU0FBUyxtQkFBbUIsQ0FBa0MsRUFDNUQsZ0JBQWdCLEVBQ2hCLGdCQUFnQixFQUNoQixNQUFNLEVBQ04sUUFBUSxFQUNSLFVBQVUsRUFDVixnQkFBZ0IsR0FRakI7UUFDQyxFQUFFLENBQUMseUJBQXlCLGdCQUFnQixXQUFXLE1BQU0sYUFBYSxRQUFRLGVBQWUsVUFBVSxHQUFHLEVBQUU7WUFDOUcsTUFBTSxRQUFRLEdBQUcsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBb0IsRUFBRTtnQkFDL0QsSUFBSSxxQkFBYSxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7b0JBQ3RDLE9BQU8sSUFBQSx3QkFBaUIsRUFBQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUU7d0JBQ2xELElBQUksRUFBRSxVQUFVO3dCQUNoQixLQUFLLEVBQUUsSUFBQSw0QkFBb0IsRUFBQyxDQUFDLENBQUM7d0JBQzlCLElBQUksRUFBRSxDQUFDO3FCQUNSLENBQUMsQ0FBQztnQkFDTCxDQUFDO2dCQUVELElBQUksQ0FBQyxLQUFLLFVBQVUsRUFBRSxDQUFDO29CQUNyQixPQUFPLElBQUEsa0NBQTJCLEVBQUMsT0FBTyxFQUFFLElBQUEsaUJBQVMsRUFBQyxJQUFLLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQztnQkFDNUUsQ0FBQztnQkFFRCxNQUFNLElBQUksS0FBSyxDQUFDLHNCQUFzQixDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQzdDLENBQUMsQ0FBQyxDQUFDO1lBRUgsTUFBTSxjQUFjLEdBQUcsa0RBQWtELENBQ3ZFLFFBQVEsRUFDUixNQUFNLEVBQ04sUUFBUSxFQUNSLFVBQVUsRUFDVixnQkFBZ0IsQ0FDakIsQ0FBQztZQUNGLDJCQUEyQixDQUFDLGNBQWMsQ0FBQyxDQUFDO1lBQzVDLElBQUksVUFBVSxLQUFLLFFBQVEsRUFBRSxDQUFDO2dCQUM1QixJQUFJLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO29CQUMxQyxpREFBaUQ7b0JBQ2pELE9BQU87Z0JBQ1QsQ0FBQztnQkFDRCxNQUFNLGVBQWUsR0FBRyxvQ0FBb0MsQ0FDMUQsUUFBNkIsRUFDN0IsTUFBTSxFQUNOLFFBQVEsRUFDUixnQkFBZ0IsQ0FDakIsQ0FBQztnQkFDRixNQUFNLENBQUMsZUFBZSxDQUFDLGNBQWMsQ0FBQyxRQUFRLEVBQUUsRUFBRSxlQUFlLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDOUUsMkJBQTJCLENBQUMsZUFBZSxDQUFDLENBQUM7WUFDL0MsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELFNBQVMsZUFBZTtRQUN0QixPQUFPLGtCQUFrQixFQUFFLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUN6QyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDTixDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsVUFBVSxDQUFDO1NBQ25CLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxTQUFTLGNBQWM7UUFDckIsTUFBTSxRQUFRLEdBQWMsQ0FBQyxNQUFNLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3hELE9BQU8sUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQ2pDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxRQUFRLEVBQXdCLEVBQUUsQ0FBQyxDQUFDLE1BQU0sS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FDeEcsQ0FBQztJQUNKLENBQUM7SUFFRCxlQUFlLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxnQkFBZ0IsRUFBRSxFQUFFO1FBQzdDLGNBQWMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxFQUFFLEVBQUU7WUFDOUMsbUJBQW1CLENBQUM7Z0JBQ2xCLGdCQUFnQjtnQkFDaEIsZ0JBQWdCLEVBQUUsTUFBTTtnQkFDeEIsTUFBTTtnQkFDTixRQUFRO2dCQUNSLFVBQVUsRUFBRSxRQUFRO2dCQUNwQixnQkFBZ0IsRUFBRSwwQ0FBdUI7YUFDMUMsQ0FBQyxDQUFDO1lBQ0gsbUJBQW1CLENBQVM7Z0JBQzFCLGdCQUFnQjtnQkFDaEIsZ0JBQWdCLEVBQUUsTUFBTTtnQkFDeEIsTUFBTTtnQkFDTixRQUFRO2dCQUNSLFVBQVUsRUFBRSxRQUFRO2dCQUNwQixnQkFBZ0IsRUFBRSxNQUFNLENBQUMsbUJBQW1CLENBQUM7YUFDOUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgYXNzZXJ0IGZyb20gJ2Fzc2VydCc7XG5cbmltcG9ydCB7IFRyYW5zYWN0aW9uLCBuZXR3b3JrcyB9IGZyb20gJy4uLy4uLy4uL3NyYyc7XG5pbXBvcnQge1xuICBpc1dhbGxldFVuc3BlbnQsXG4gIGZvcm1hdE91dHB1dElkLFxuICBnZXRPdXRwdXRJZEZvcklucHV0LFxuICBwYXJzZU91dHB1dElkLFxuICBUeE91dFBvaW50LFxuICBVbnNwZW50LFxuICBjcmVhdGVUcmFuc2FjdGlvbkJ1aWxkZXJGb3JOZXR3b3JrLFxuICBnZXRJbnRlcm5hbENoYWluQ29kZSxcbiAgZ2V0RXh0ZXJuYWxDaGFpbkNvZGUsXG4gIGFkZFRvVHJhbnNhY3Rpb25CdWlsZGVyLFxuICBzaWduSW5wdXRXaXRoVW5zcGVudCxcbiAgV2FsbGV0VW5zcGVudFNpZ25lcixcbiAgb3V0cHV0U2NyaXB0cyxcbiAgdW5zcGVudFN1bSxcbiAgZ2V0V2FsbGV0QWRkcmVzcyxcbiAgdmVyaWZ5U2lnbmF0dXJlV2l0aFVuc3BlbnQsXG4gIHRvVE51bWJlcixcbiAgVXR4b1RyYW5zYWN0aW9uLFxuICBjcmVhdGVQc2J0Rm9yTmV0d29yayxcbiAgY3JlYXRlUHNidEZyb21UcmFuc2FjdGlvbixcbiAgYWRkV2FsbGV0VW5zcGVudFRvUHNidCxcbiAgYWRkV2FsbGV0T3V0cHV0VG9Qc2J0LFxuICB0b1ByZXZPdXRwdXQsXG4gIEtleU5hbWUsXG4gIHNpZ25JbnB1dFAyc2hQMnBrLFxufSBmcm9tICcuLi8uLi8uLi9zcmMvYml0Z28nO1xuXG5pbXBvcnQgeyBnZXREZWZhdWx0V2FsbGV0S2V5cyB9IGZyb20gJy4uLy4uLy4uL3NyYy90ZXN0dXRpbCc7XG5pbXBvcnQgeyBkZWZhdWx0VGVzdE91dHB1dEFtb3VudCB9IGZyb20gJy4uLy4uL3RyYW5zYWN0aW9uX3V0aWwnO1xuaW1wb3J0IHtcbiAgbW9ja1dhbGxldFVuc3BlbnQsXG4gIGlzUmVwbGF5UHJvdGVjdGlvblVuc3BlbnQsXG4gIG1vY2tSZXBsYXlQcm90ZWN0aW9uVW5zcGVudCxcbiAgcmVwbGF5UHJvdGVjdGlvbktleVBhaXIsXG59IGZyb20gJy4uLy4uLy4uL3NyYy90ZXN0dXRpbC9tb2NrJztcblxuY29uc3QgQ0hBTkdFX0lOREVYID0gMTAwO1xuY29uc3QgRkVFID0gQmlnSW50KDEwMCk7XG5cbnR5cGUgSW5wdXRUeXBlID0gb3V0cHV0U2NyaXB0cy5TY3JpcHRUeXBlMk9mMyB8ICdwMnNoUDJwayc7XG5cbmZ1bmN0aW9uIGdldFNjcmlwdFR5cGVzMk9mMygpIHtcbiAgLy8gRklYTUUoQkctNjY5NDEpOiBwMnRyTXVzaWcyIHNpZ25pbmcgZG9lcyBub3Qgd29yayBpbiB0aGlzIHRlc3Qgc3VpdGUgeWV0XG4gIC8vICBiZWNhdXNlIHRoZSB0ZXN0IHN1aXRlIGlzIHdyaXR0ZW4gd2l0aCBUcmFuc2FjdGlvbkJ1aWxkZXJcbiAgcmV0dXJuIG91dHB1dFNjcmlwdHMuc2NyaXB0VHlwZXMyT2YzLmZpbHRlcigoc2NyaXB0VHlwZSkgPT4gc2NyaXB0VHlwZSAhPT0gJ3AydHJNdXNpZzInKTtcbn1cblxuZGVzY3JpYmUoJ1dhbGxldFVuc3BlbnQnLCBmdW5jdGlvbiAoKSB7XG4gIGNvbnN0IG5ldHdvcmsgPSBuZXR3b3Jrcy5iaXRjb2luO1xuICBjb25zdCB3YWxsZXRLZXlzID0gZ2V0RGVmYXVsdFdhbGxldEtleXMoKTtcbiAgY29uc3QgaGFzaCA9IEJ1ZmZlci5hbGxvYygzMikuZmlsbCgweGZmKTtcbiAgaGFzaFswXSA9IDA7IC8vIHNob3cgZW5kaWFubmVzc1xuICBjb25zdCBpbnB1dCA9IHsgaGFzaCwgaW5kZXg6IDAgfTtcbiAgY29uc3QgZXhwZWN0ZWRPdXRQb2ludDogVHhPdXRQb2ludCA9IHtcbiAgICB0eGlkOiAnZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYwMCcsXG4gICAgdm91dDogMCxcbiAgfTtcblxuICBpdCgncGFyc2VzIGFuZCBmb3JtYXRzIHR4aWQnLCBmdW5jdGlvbiAoKSB7XG4gICAgYXNzZXJ0LmRlZXBTdHJpY3RFcXVhbChnZXRPdXRwdXRJZEZvcklucHV0KGlucHV0KSwgZXhwZWN0ZWRPdXRQb2ludCk7XG4gICAgYXNzZXJ0LmRlZXBTdHJpY3RFcXVhbChcbiAgICAgIGZvcm1hdE91dHB1dElkKGV4cGVjdGVkT3V0UG9pbnQpLFxuICAgICAgJ2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMDA6MCdcbiAgICApO1xuICAgIGFzc2VydC5kZWVwU3RyaWN0RXF1YWwocGFyc2VPdXRwdXRJZChmb3JtYXRPdXRwdXRJZChleHBlY3RlZE91dFBvaW50KSksIGV4cGVjdGVkT3V0UG9pbnQpO1xuICB9KTtcblxuICBpdCgnaWRlbnRpZmllcyB3YWxsZXQgdW5zcGVudHMnLCBmdW5jdGlvbiAoKSB7XG4gICAgY29uc3QgdW5zcGVudDogVW5zcGVudCA9IHtcbiAgICAgIGlkOiBmb3JtYXRPdXRwdXRJZChleHBlY3RlZE91dFBvaW50KSxcbiAgICAgIGFkZHJlc3M6IGdldFdhbGxldEFkZHJlc3Mod2FsbGV0S2V5cywgMCwgMCwgbmV0d29yayksXG4gICAgICB2YWx1ZTogMWU4LFxuICAgIH07XG4gICAgYXNzZXJ0LnN0cmljdEVxdWFsKGlzV2FsbGV0VW5zcGVudCh1bnNwZW50KSwgZmFsc2UpO1xuICAgIGFzc2VydC5zdHJpY3RFcXVhbChpc1dhbGxldFVuc3BlbnQoeyAuLi51bnNwZW50LCBjaGFpbjogMCwgaW5kZXg6IDAgfSBhcyBVbnNwZW50KSwgdHJ1ZSk7XG4gIH0pO1xuXG4gIGZ1bmN0aW9uIGNvbnN0cnVjdEFuZFNpZ25UcmFuc2FjdGlvblVzaW5nUHNidChcbiAgICB1bnNwZW50czogKFVuc3BlbnQ8YmlnaW50PiAmIHsgcHJldlR4PzogQnVmZmVyIH0pW10sXG4gICAgc2lnbmVyOiBLZXlOYW1lLFxuICAgIGNvc2lnbmVyOiBLZXlOYW1lLFxuICAgIG91dHB1dFR5cGU6IG91dHB1dFNjcmlwdHMuU2NyaXB0VHlwZTJPZjNcbiAgKTogVHJhbnNhY3Rpb248YmlnaW50PiB7XG4gICAgY29uc3QgcHNidCA9IGNyZWF0ZVBzYnRGb3JOZXR3b3JrKHsgbmV0d29yayB9KTtcbiAgICBjb25zdCB0b3RhbCA9IEJpZ0ludCh1bnNwZW50U3VtPGJpZ2ludD4odW5zcGVudHMsICdiaWdpbnQnKSk7XG4gICAgYWRkV2FsbGV0T3V0cHV0VG9Qc2J0KHBzYnQsIHdhbGxldEtleXMsIGdldEludGVybmFsQ2hhaW5Db2RlKG91dHB1dFR5cGUpLCBDSEFOR0VfSU5ERVgsIHRvdGFsIC0gRkVFKTtcblxuICAgIHVuc3BlbnRzLmZvckVhY2goKHUpID0+IHtcbiAgICAgIGlmIChpc1dhbGxldFVuc3BlbnQodSkpIHtcbiAgICAgICAgYWRkV2FsbGV0VW5zcGVudFRvUHNidChwc2J0LCB1LCB3YWxsZXRLZXlzLCBzaWduZXIsIGNvc2lnbmVyKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgaW52YWxpZCB1bnNwZW50YCk7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICAvLyBUT0RPOiBUZXN0IHJlZGVyaXZpbmcgc2NyaXB0cyBmcm9tIFBTQlQgYW5kIGtleXMgb25seVxuICAgIHBzYnQuc2lnbkFsbElucHV0c0hEKHdhbGxldEtleXNbc2lnbmVyXSk7XG4gICAgcHNidC5zaWduQWxsSW5wdXRzSEQod2FsbGV0S2V5c1tjb3NpZ25lcl0pO1xuICAgIGFzc2VydChwc2J0LnZhbGlkYXRlU2lnbmF0dXJlc09mQWxsSW5wdXRzKCkpO1xuICAgIHBzYnQuZmluYWxpemVBbGxJbnB1dHMoKTtcbiAgICAvLyBleHRyYWN0IHRyYW5zYWN0aW9uIGhhcyBhIHJldHVybiB0eXBlIG9mIFRyYW5zYWN0aW9uIGluc3RlYWQgb2YgVXR4b1RyYW5zYWN0aW9uXG4gICAgY29uc3QgdHggPSBwc2J0LmV4dHJhY3RUcmFuc2FjdGlvbigpIGFzIFV0eG9UcmFuc2FjdGlvbjxiaWdpbnQ+O1xuXG4gICAgY29uc3QgcHNidDIgPSBjcmVhdGVQc2J0RnJvbVRyYW5zYWN0aW9uKFxuICAgICAgdHgsXG4gICAgICB1bnNwZW50cy5tYXAoKHUpID0+ICh7IC4uLnRvUHJldk91dHB1dDxiaWdpbnQ+KHUsIG5ldHdvcmspLCBwcmV2VHg6IHUucHJldlR4IH0pKVxuICAgICk7XG4gICAgYXNzZXJ0KHBzYnQyLnZhbGlkYXRlU2lnbmF0dXJlc09mQWxsSW5wdXRzKCkpO1xuICAgIHJldHVybiB0eDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNvbnN0cnVjdEFuZFNpZ25UcmFuc2FjdGlvblVzaW5nVHJhbnNhY3Rpb25CdWlsZGVyPFROdW1iZXIgZXh0ZW5kcyBudW1iZXIgfCBiaWdpbnQ+KFxuICAgIHVuc3BlbnRzOiBVbnNwZW50PFROdW1iZXI+W10sXG4gICAgc2lnbmVyOiBzdHJpbmcsXG4gICAgY29zaWduZXI6IHN0cmluZyxcbiAgICBhbW91bnRUeXBlOiAnbnVtYmVyJyB8ICdiaWdpbnQnID0gJ251bWJlcicsXG4gICAgb3V0cHV0VHlwZTogb3V0cHV0U2NyaXB0cy5TY3JpcHRUeXBlMk9mM1xuICApOiBVdHhvVHJhbnNhY3Rpb248VE51bWJlcj4ge1xuICAgIGNvbnN0IHR4YiA9IGNyZWF0ZVRyYW5zYWN0aW9uQnVpbGRlckZvck5ldHdvcms8VE51bWJlcj4obmV0d29yayk7XG4gICAgY29uc3QgdG90YWwgPSBCaWdJbnQodW5zcGVudFN1bTxUTnVtYmVyPih1bnNwZW50cywgYW1vdW50VHlwZSkpO1xuICAgIC8vIEtpbmRhIHdlaXJkLCB0cmVhdGluZyBlbnRpcmUgdmFsdWUgYXMgY2hhbmdlLCBidXQgdGVzdHMgdGhlIHJlbGV2YW50IHBhdGhzXG4gICAgdHhiLmFkZE91dHB1dChcbiAgICAgIGdldFdhbGxldEFkZHJlc3Mod2FsbGV0S2V5cywgZ2V0SW50ZXJuYWxDaGFpbkNvZGUob3V0cHV0VHlwZSksIENIQU5HRV9JTkRFWCwgbmV0d29yayksXG4gICAgICB0b1ROdW1iZXI8VE51bWJlcj4odG90YWwgLSBGRUUsIGFtb3VudFR5cGUpXG4gICAgKTtcbiAgICB1bnNwZW50cy5mb3JFYWNoKCh1KSA9PiB7XG4gICAgICBhZGRUb1RyYW5zYWN0aW9uQnVpbGRlcih0eGIsIHUpO1xuICAgIH0pO1xuICAgIHVuc3BlbnRzLmZvckVhY2goKHUsIGkpID0+IHtcbiAgICAgIGlmIChpc1JlcGxheVByb3RlY3Rpb25VbnNwZW50KHUsIG5ldHdvcmspKSB7XG4gICAgICAgIHNpZ25JbnB1dFAyc2hQMnBrKHR4YiwgaSwgcmVwbGF5UHJvdGVjdGlvbktleVBhaXIpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgW1xuICAgICAgV2FsbGV0VW5zcGVudFNpZ25lci5mcm9tKHdhbGxldEtleXMsIHdhbGxldEtleXNbc2lnbmVyXSwgd2FsbGV0S2V5c1tjb3NpZ25lcl0pLFxuICAgICAgV2FsbGV0VW5zcGVudFNpZ25lci5mcm9tKHdhbGxldEtleXMsIHdhbGxldEtleXNbY29zaWduZXJdLCB3YWxsZXRLZXlzW3NpZ25lcl0pLFxuICAgIF0uZm9yRWFjaCgod2FsbGV0U2lnbmVyLCBuU2lnbmF0dXJlKSA9PiB7XG4gICAgICB1bnNwZW50cy5mb3JFYWNoKCh1LCBpKSA9PiB7XG4gICAgICAgIGlmIChpc1dhbGxldFVuc3BlbnQodSkpIHtcbiAgICAgICAgICBzaWduSW5wdXRXaXRoVW5zcGVudCh0eGIsIGksIHUsIHdhbGxldFNpZ25lcik7XG4gICAgICAgIH0gZWxzZSBpZiAoaXNSZXBsYXlQcm90ZWN0aW9uVW5zcGVudCh1LCBuZXR3b3JrKSkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYHVuZXhwZWN0ZWQgdW5zcGVudCAke3UuaWR9YCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCB0eCA9IG5TaWduYXR1cmUgPT09IDAgPyB0eGIuYnVpbGRJbmNvbXBsZXRlKCkgOiB0eGIuYnVpbGQoKTtcbiAgICAgIC8vIFZlcmlmeSBlYWNoIHNpZ25hdHVyZSBmb3IgdGhlIHVuc3BlbnRcbiAgICAgIHVuc3BlbnRzLmZvckVhY2goKHUsIGkpID0+IHtcbiAgICAgICAgaWYgKGlzUmVwbGF5UHJvdGVjdGlvblVuc3BlbnQodSwgbmV0d29yaykpIHtcbiAgICAgICAgICAvLyBzaWduYXR1cmUgdmVyaWZpY2F0aW9uIG5vdCBpbXBsZW1lbnRlZCBmb3IgcmVwbGF5IHByb3RlY3Rpb24gdW5zcGVudHNcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgYXNzZXJ0LmRlZXBTdHJpY3RFcXVhbChcbiAgICAgICAgICB2ZXJpZnlTaWduYXR1cmVXaXRoVW5zcGVudCh0eCwgaSwgdW5zcGVudHMsIHdhbGxldEtleXMpLFxuICAgICAgICAgIHdhbGxldEtleXMudHJpcGxlLm1hcCgoaykgPT4gayA9PT0gd2FsbGV0S2V5c1tzaWduZXJdIHx8IChuU2lnbmF0dXJlID09PSAxICYmIGsgPT09IHdhbGxldEtleXNbY29zaWduZXJdKSlcbiAgICAgICAgKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHR4Yi5idWlsZCgpO1xuICB9XG5cbiAgZnVuY3Rpb24gdmFsaWRhdGVMb2NrVGltZUFuZFNlcXVlbmNlPFROdW1iZXIgZXh0ZW5kcyBudW1iZXIgfCBiaWdpbnQ+KFxuICAgIHRyYW5zYWN0aW9uOiBVdHhvVHJhbnNhY3Rpb248VE51bWJlcj4gfCBUcmFuc2FjdGlvbjxiaWdpbnQ+XG4gICkge1xuICAgIC8vIGxvY2t0aW1lIHNob3VsZCBkZWZhdWx0IHRvIDAgYW5kIHNlcXVlbmNlIHRvIDB4ZmZmZmZmZmYgZm9yIGFsbCBpbnB1dHNcbiAgICBhc3NlcnQuZGVlcFN0cmljdEVxdWFsKHRyYW5zYWN0aW9uLmxvY2t0aW1lLCAwKTtcbiAgICBjb25zdCBpbnB1dHMgPSB0cmFuc2FjdGlvbi5pbnM7XG4gICAgZm9yIChjb25zdCBpbnB1dCBvZiBpbnB1dHMpIHtcbiAgICAgIGFzc2VydC5kZWVwU3RyaWN0RXF1YWwoaW5wdXQuc2VxdWVuY2UsIDB4ZmZmZmZmZmYpO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHJ1blRlc3RTaWduVW5zcGVudHM8VE51bWJlciBleHRlbmRzIG51bWJlciB8IGJpZ2ludD4oe1xuICAgIGlucHV0U2NyaXB0VHlwZXMsXG4gICAgb3V0cHV0U2NyaXB0VHlwZSxcbiAgICBzaWduZXIsXG4gICAgY29zaWduZXIsXG4gICAgYW1vdW50VHlwZSxcbiAgICB0ZXN0T3V0cHV0QW1vdW50LFxuICB9OiB7XG4gICAgaW5wdXRTY3JpcHRUeXBlczogSW5wdXRUeXBlW107XG4gICAgb3V0cHV0U2NyaXB0VHlwZTogb3V0cHV0U2NyaXB0cy5TY3JpcHRUeXBlMk9mMztcbiAgICBzaWduZXI6IEtleU5hbWU7XG4gICAgY29zaWduZXI6IEtleU5hbWU7XG4gICAgYW1vdW50VHlwZTogJ251bWJlcicgfCAnYmlnaW50JztcbiAgICB0ZXN0T3V0cHV0QW1vdW50OiBUTnVtYmVyO1xuICB9KSB7XG4gICAgaXQoYGNhbiBiZSBzaWduZWQgW2lucHV0cz0ke2lucHV0U2NyaXB0VHlwZXN9IHNpZ25lcj0ke3NpZ25lcn0gY29zaWduZXI9JHtjb3NpZ25lcn0gYW1vdW50VHlwZT0ke2Ftb3VudFR5cGV9XWAsIGZ1bmN0aW9uICgpIHtcbiAgICAgIGNvbnN0IHVuc3BlbnRzID0gaW5wdXRTY3JpcHRUeXBlcy5tYXAoKHQsIGkpOiBVbnNwZW50PFROdW1iZXI+ID0+IHtcbiAgICAgICAgaWYgKG91dHB1dFNjcmlwdHMuaXNTY3JpcHRUeXBlMk9mMyh0KSkge1xuICAgICAgICAgIHJldHVybiBtb2NrV2FsbGV0VW5zcGVudChuZXR3b3JrLCB0ZXN0T3V0cHV0QW1vdW50LCB7XG4gICAgICAgICAgICBrZXlzOiB3YWxsZXRLZXlzLFxuICAgICAgICAgICAgY2hhaW46IGdldEV4dGVybmFsQ2hhaW5Db2RlKHQpLFxuICAgICAgICAgICAgdm91dDogaSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0ID09PSAncDJzaFAycGsnKSB7XG4gICAgICAgICAgcmV0dXJuIG1vY2tSZXBsYXlQcm90ZWN0aW9uVW5zcGVudChuZXR3b3JrLCB0b1ROdW1iZXIoMV8wMDAsIGFtb3VudFR5cGUpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgaW52YWxpZCBpbnB1dCB0eXBlICR7dH1gKTtcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCB0eGJUcmFuc2FjdGlvbiA9IGNvbnN0cnVjdEFuZFNpZ25UcmFuc2FjdGlvblVzaW5nVHJhbnNhY3Rpb25CdWlsZGVyKFxuICAgICAgICB1bnNwZW50cyxcbiAgICAgICAgc2lnbmVyLFxuICAgICAgICBjb3NpZ25lcixcbiAgICAgICAgYW1vdW50VHlwZSxcbiAgICAgICAgb3V0cHV0U2NyaXB0VHlwZVxuICAgICAgKTtcbiAgICAgIHZhbGlkYXRlTG9ja1RpbWVBbmRTZXF1ZW5jZSh0eGJUcmFuc2FjdGlvbik7XG4gICAgICBpZiAoYW1vdW50VHlwZSA9PT0gJ2JpZ2ludCcpIHtcbiAgICAgICAgaWYgKGlucHV0U2NyaXB0VHlwZXMuaW5jbHVkZXMoJ3Ayc2hQMnBrJykpIHtcbiAgICAgICAgICAvLyBGSU1YRShCRy00NzgyNCk6IGFkZCBwMnNoUDJwayBzdXBwb3J0IGZvciBQc2J0XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHBzYnRUcmFuc2FjdGlvbiA9IGNvbnN0cnVjdEFuZFNpZ25UcmFuc2FjdGlvblVzaW5nUHNidChcbiAgICAgICAgICB1bnNwZW50cyBhcyBVbnNwZW50PGJpZ2ludD5bXSxcbiAgICAgICAgICBzaWduZXIsXG4gICAgICAgICAgY29zaWduZXIsXG4gICAgICAgICAgb3V0cHV0U2NyaXB0VHlwZVxuICAgICAgICApO1xuICAgICAgICBhc3NlcnQuZGVlcFN0cmljdEVxdWFsKHR4YlRyYW5zYWN0aW9uLnRvQnVmZmVyKCksIHBzYnRUcmFuc2FjdGlvbi50b0J1ZmZlcigpKTtcbiAgICAgICAgdmFsaWRhdGVMb2NrVGltZUFuZFNlcXVlbmNlKHBzYnRUcmFuc2FjdGlvbik7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBmdW5jdGlvbiBnZXRJbnB1dFNjcmlwdHMoKTogSW5wdXRUeXBlW11bXSB7XG4gICAgcmV0dXJuIGdldFNjcmlwdFR5cGVzMk9mMygpLmZsYXRNYXAoKHQpID0+IFtcbiAgICAgIFt0LCB0XSxcbiAgICAgIFt0LCB0LCAncDJzaFAycGsnXSxcbiAgICBdKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGdldFNpZ25lclBhaXJzKCk6IFtzaWduZXI6IEtleU5hbWUsIGNvc2lnbmVyOiBLZXlOYW1lXVtdIHtcbiAgICBjb25zdCBrZXlOYW1lczogS2V5TmFtZVtdID0gWyd1c2VyJywgJ2JhY2t1cCcsICdiaXRnbyddO1xuICAgIHJldHVybiBrZXlOYW1lcy5mbGF0TWFwKChzaWduZXIpID0+XG4gICAgICBrZXlOYW1lcy5mbGF0TWFwKChjb3NpZ25lcik6IFtLZXlOYW1lLCBLZXlOYW1lXVtdID0+IChzaWduZXIgPT09IGNvc2lnbmVyID8gW10gOiBbW3NpZ25lciwgY29zaWduZXJdXSkpXG4gICAgKTtcbiAgfVxuXG4gIGdldElucHV0U2NyaXB0cygpLmZvckVhY2goKGlucHV0U2NyaXB0VHlwZXMpID0+IHtcbiAgICBnZXRTaWduZXJQYWlycygpLmZvckVhY2goKFtzaWduZXIsIGNvc2lnbmVyXSkgPT4ge1xuICAgICAgcnVuVGVzdFNpZ25VbnNwZW50cyh7XG4gICAgICAgIGlucHV0U2NyaXB0VHlwZXMsXG4gICAgICAgIG91dHB1dFNjcmlwdFR5cGU6ICdwMnNoJyxcbiAgICAgICAgc2lnbmVyLFxuICAgICAgICBjb3NpZ25lcixcbiAgICAgICAgYW1vdW50VHlwZTogJ251bWJlcicsXG4gICAgICAgIHRlc3RPdXRwdXRBbW91bnQ6IGRlZmF1bHRUZXN0T3V0cHV0QW1vdW50LFxuICAgICAgfSk7XG4gICAgICBydW5UZXN0U2lnblVuc3BlbnRzPGJpZ2ludD4oe1xuICAgICAgICBpbnB1dFNjcmlwdFR5cGVzLFxuICAgICAgICBvdXRwdXRTY3JpcHRUeXBlOiAncDJzaCcsXG4gICAgICAgIHNpZ25lcixcbiAgICAgICAgY29zaWduZXIsXG4gICAgICAgIGFtb3VudFR5cGU6ICdiaWdpbnQnLFxuICAgICAgICB0ZXN0T3V0cHV0QW1vdW50OiBCaWdJbnQoJzEwMDAwMDAwMDAwMDAwMDAwJyksXG4gICAgICB9KTtcbiAgICB9KTtcbiAgfSk7XG59KTtcbiJdfQ==

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


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