@bitgo/utxo-lib
Version:
Client-side Bitcoin JavaScript library
510 lines • 71.7 kB
JavaScript
"use strict";
/**
* This module provides taproot utilities including the legacy MuSig2 key aggregation
* algorithm used by BitGo's deprecated `p2tr` script type (chains 30, 31).
*
* ## Legacy p2tr vs Standard p2trMusig2
*
* BitGo supports two taproot address types:
*
* 1. **Legacy `p2tr` (chains 30, 31) - DEPRECATED**
* - Uses the `aggregateMuSigPubkeys()` function in this module
* - Based on an older MuSig2 variant that predates BIP327
* - Expects 32-byte x-only pubkeys with even Y coordinates
* - Sorts keys AFTER x-only conversion
* - Corresponds to MuSig2 before the 32-byte to 33-byte key change
* - See: https://github.com/jonasnick/bips/pull/37
*
* 2. **Standard `p2trMusig2` (chains 40, 41) - RECOMMENDED**
* - Uses the `@brandonblack/musig` library (standard BIP327 implementation)
* - Uses full 33-byte compressed pubkeys throughout aggregation
* - Key order affects the resulting aggregate key
* - Fully compatible with the BIP327 specification
*
* ## Key Difference
*
* The critical difference is **when x-only conversion happens**:
* - Legacy: Converts to x-only BEFORE sorting (produces order-independent keys)
* - Standard: Uses 33-byte keys throughout (key order matters)
*
* For the same two pubkeys, these methods produce DIFFERENT aggregate keys because
* the sort order differs between 33-byte and 32-byte representations.
*
* See `modules/utxo-lib/bip-0327/README.md` for detailed comparison and test cases.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.INITIAL_TAPSCRIPT_VERSION = exports.EVEN_Y_COORD_PREFIX = void 0;
exports.aggregateMuSigPubkeys = aggregateMuSigPubkeys;
exports.serializeScriptSize = serializeScriptSize;
exports.hashTapLeaf = hashTapLeaf;
exports.hashTapBranch = hashTapBranch;
exports.calculateTapTweak = calculateTapTweak;
exports.tapTweakPrivkey = tapTweakPrivkey;
exports.tapTweakPubkey = tapTweakPubkey;
exports.getDepthFirstTaptree = getDepthFirstTaptree;
exports.getHuffmanTaptree = getHuffmanTaptree;
exports.getControlBlock = getControlBlock;
exports.parseTaprootWitness = parseTaprootWitness;
exports.parseControlBlock = parseControlBlock;
exports.getTapleafHash = getTapleafHash;
exports.getTaptreeRoot = getTaptreeRoot;
exports.getTweakedOutputKey = getTweakedOutputKey;
exports.createTaprootOutputScript = createTaprootOutputScript;
exports.getTaprootOutputKey = getTaprootOutputKey;
const assert = require("assert");
const FastPriorityQueue = require("fastpriorityqueue");
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
const secp256k1_1 = require("@bitgo/secp256k1");
const varuint = require('varuint-bitcoin');
/**
* The 0x02 prefix indicating an even Y coordinate which is implicitly assumed
* on all 32 byte x-only pub keys as defined in BIP340.
*/
exports.EVEN_Y_COORD_PREFIX = Buffer.of(0x02);
exports.INITIAL_TAPSCRIPT_VERSION = 0xc0;
/**
* Aggregates a list of public keys into a single public key using the legacy MuSig2 algorithm.
*
* This implements the deprecated key aggregation method used by BitGo's `p2tr` script type
* (chains 30, 31). It corresponds to an older variant of MuSig2 that predates the change
* from 32-byte to 33-byte keys in the BIP327 specification.
*
* ## Algorithm
*
* The implementation follows the MuSig2 key aggregation scheme:
*
* ```
* P = sum_i (μ_i * P_i)
* ```
*
* where:
* - `P_i` is the public key of the i-th signer
* - `μ_i` is the MuSig coefficient computed as:
* - `L = TaggedHash("KeyAgg list", P_1 || P_2 || ... || P_n)`
* - `μ_i = TaggedHash("KeyAgg coefficient", L || P_i)` for most keys
* - `μ_i = 1` for the second unique key (optimization to save an exponentiation)
*
* ## Key Characteristics (Legacy Variant)
*
* 1. **X-only pubkeys**: Expects 32-byte x-only pubkeys (assumes even Y coordinates)
* 2. **Pre-aggregation sorting**: Sorts keys in ascending order BEFORE aggregation
* 3. **Order-independent**: Due to sorting, key order doesn't affect the result
* 4. **33-byte internal representation**: Internally prepends 0x02 prefix for elliptic curve operations
*
* ## Differences from Standard MuSig2 (BIP327)
*
* The standard MuSig2 implementation (`@brandonblack/musig` library):
* - Uses 33-byte compressed pubkeys throughout
* - Does NOT sort keys before aggregation (key order matters)
* - Produces different aggregate keys even for the same set of pubkeys
*
* ## Reference Implementation
*
* This corresponds to `key_agg_bitgo_p2tr_legacy()` in the Python reference implementation
* at `modules/utxo-lib/bip-0327/reference.py`.
*
* @param ecc Elliptic curve implementation
* @param pubkeys List of 32-byte x-only public keys to aggregate (must have even Y coordinates)
* @returns 32-byte Buffer representing the x-only aggregate public key
* @throws {Error} if fewer than 2 pubkeys provided or elliptic curve operations fail
*
* @see modules/utxo-lib/bip-0327/README.md for detailed comparison with standard MuSig2
* @see https://github.com/jonasnick/bips/pull/37 for the MuSig2 specification change
*/
function aggregateMuSigPubkeys(ecc, pubkeys) {
// TODO: Consider enforcing key uniqueness.
assert(pubkeys.length > 1, 'at least two pubkeys are required for musig key aggregation');
// LEGACY BEHAVIOR: Sort the keys in ascending order BEFORE aggregation.
// This makes the aggregate key independent of input order.
// Standard MuSig2 (BIP327) does NOT sort, making key order significant.
pubkeys.sort(Buffer.compare);
// Compute the "KeyAgg list" hash L = TaggedHash("KeyAgg list", P_1 || P_2 || ... || P_n)
// This hash is used to derive the MuSig coefficients for each pubkey.
// In the reference implementation (reference.py), this is done in hash_keys().
const L = bitcoinjs_lib_1.crypto.taggedHash('KeyAgg list', Buffer.concat(pubkeys));
// Find the second unique pubkey in the sorted list.
// This key (and any keys identical to it) will use coefficient μ = 1 as an optimization.
// This saves an expensive point multiplication (see MuSig2* appendix in the MuSig2 paper).
// In the reference implementation, this is get_second_key().
const secondUniquePubkey = pubkeys.find((pubkey) => !pubkeys[0].equals(pubkey));
// For each pubkey P_i, compute the tweaked pubkey μ_i * P_i
// where μ_i is the MuSig coefficient for that key.
const tweakedPubkeys = pubkeys.map((pubkey) => {
// Convert 32-byte x-only pubkey to 33-byte compressed format by prepending 0x02.
// This assumes an even Y coordinate (standard for x-only pubkeys in taproot).
// In the reference implementation, this is done implicitly in key_agg() via lift_x().
const xyPubkey = Buffer.concat([exports.EVEN_Y_COORD_PREFIX, pubkey]);
if (secondUniquePubkey !== undefined && secondUniquePubkey.equals(pubkey)) {
// Optimization: The second unique key gets coefficient μ = 1.
// This means μ_i * P_i = 1 * P_i = P_i (no multiplication needed).
// This saves an expensive elliptic curve point multiplication operation.
// See key_agg_coeff_internal() in reference.py where it returns 1 for pk_ == pk2.
return xyPubkey;
}
// Compute the MuSig coefficient: μ_i = TaggedHash("KeyAgg coefficient", L || P_i)
// In the reference implementation, this is key_agg_coeff_internal().
const c = bitcoinjs_lib_1.crypto.taggedHash('KeyAgg coefficient', Buffer.concat([L, pubkey]));
// Compute the tweaked pubkey: μ_i * P_i (elliptic curve point multiplication)
const tweakedPubkey = ecc.pointMultiply(xyPubkey, c);
if (!tweakedPubkey) {
throw new Error('Failed to multiply pubkey by coefficient');
}
return tweakedPubkey;
});
// Sum all the tweaked pubkeys to get the aggregate pubkey:
// P = sum_i (μ_i * P_i) = μ_1*P_1 + μ_2*P_2 + ... + μ_n*P_n
// This is elliptic curve point addition.
const aggregatePubkey = tweakedPubkeys.reduce((prev, curr) => {
const next = ecc.pointAdd(prev, curr);
if (!next)
throw new Error('Failed to sum pubkeys');
return next;
});
// Convert the aggregate pubkey from 33-byte compressed format back to 32-byte x-only format
// by removing the first byte (the Y coordinate prefix).
// In the reference implementation, this is done by get_xonly_pk() which calls xbytes().
return aggregatePubkey.slice(1);
}
/**
* Encodes the length of a script as a bitcoin variable length integer.
* @param script
* @returns
*/
function serializeScriptSize(script) {
return varuint.encode(script.length);
}
/**
* Gets a tapleaf tagged hash from a script.
* @param script
* @returns
*/
function hashTapLeaf(script, leafVersion = exports.INITIAL_TAPSCRIPT_VERSION) {
const size = serializeScriptSize(script);
return bitcoinjs_lib_1.crypto.taggedHash('TapLeaf', Buffer.concat([Buffer.of(leafVersion), size, script]));
}
/**
* Creates a lexicographically sorted tapbranch from two child taptree nodes
* and returns its tagged hash.
* @param child1
* @param child2
* @returns the tagged tapbranch hash
*/
function hashTapBranch(child1, child2) {
// sort the children lexicographically
const sortedChildren = [child1, child2].sort(Buffer.compare);
return bitcoinjs_lib_1.crypto.taggedHash('TapBranch', Buffer.concat(sortedChildren));
}
function calculateTapTweak(pubkey, taptreeRoot) {
if (pubkey.length !== 32) {
throw new Error(`Invalid pubkey size ${pubkey.length}.`);
}
if (taptreeRoot) {
if (taptreeRoot.length !== 32) {
throw new Error(`Invalid taptreeRoot size ${taptreeRoot.length}.`);
}
return bitcoinjs_lib_1.crypto.taggedHash('TapTweak', Buffer.concat([pubkey, taptreeRoot]));
}
// If the spending conditions do not require a script path, the output key should commit to an
// unspendable script path instead of having no script path.
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-22
return bitcoinjs_lib_1.crypto.taggedHash('TapTweak', Buffer.from(pubkey));
}
/**
* Tweaks a privkey, using the tagged hash of its pubkey, and (optionally) a taptree root
* @param ecc Elliptic curve implementation
* @param pubkey public key, used to calculate the tweak
* @param privkey the privkey to tweak
* @param taptreeRoot the taptree root tagged hash
* @returns {Buffer} the tweaked privkey
*/
function tapTweakPrivkey(ecc, pubkey, privkey, taptreeRoot) {
const tapTweak = calculateTapTweak(pubkey, taptreeRoot);
const point = ecc.pointFromScalar(privkey);
if (!point)
throw new Error('Invalid private key');
if (point[0] % 2 === 1)
privkey = ecc.privateNegate(privkey);
const result = ecc.privateAdd(privkey, tapTweak);
if (!result)
throw new Error('Invalid private key');
return result;
}
/**
* Tweaks an internal pubkey, using the tagged hash of itself, and (optionally) a taptree root
* @param ecc Elliptic curve implementation
* @param pubkey the internal pubkey to tweak
* @param taptreeRoot the taptree root tagged hash
* @returns {TweakedPubkey} the tweaked pubkey
*/
function tapTweakPubkey(ecc, pubkey, taptreeRoot) {
const tapTweak = calculateTapTweak(pubkey, taptreeRoot);
const result = ecc.xOnlyPointAddTweak(pubkey, tapTweak);
if (!result)
throw new Error('Invalid pubkey');
return result;
}
function recurseTaptree(leaves, targetDepth = 0) {
const { value, done } = leaves.next();
assert(!done, 'insufficient leaves to reconstruct tap tree');
const [index, leaf] = value;
const tree = {
root: hashTapLeaf(leaf.script, leaf.leafVersion),
paths: [],
};
tree.paths[index] = [];
for (let depth = leaf.depth; depth > targetDepth; depth--) {
const sibling = recurseTaptree(leaves, depth);
tree.paths.forEach((path) => path.push(sibling.root));
sibling.paths.forEach((path) => path.push(tree.root));
tree.root = hashTapBranch(tree.root, sibling.root);
// Merge disjoint sparse arrays of paths into tree.paths
Object.assign(tree.paths, sibling.paths);
}
return tree;
}
/**
* Gets the root hash and hash-paths of a taptree from the depth-first
* construction used in BIP-0371 PSBTs
* @param tree
* @returns {Taptree} the tree, represented by its root hash, and the paths to
* that root from each of the input scripts
*/
function getDepthFirstTaptree(tree) {
const iter = tree.leaves.entries();
const ret = recurseTaptree(iter);
assert(iter.next().done, 'invalid tap tree, no path to some leaves');
return ret;
}
/**
* Gets the root hash of a taptree using a weighted Huffman construction from a
* list of scripts and corresponding weights.
* @param scripts
* @param weights
* @returns {Taptree} the tree, represented by its root hash, and the paths to that root from each of the input scripts
*/
function getHuffmanTaptree(scripts, weights) {
assert(scripts.length > 0, 'at least one script is required to construct a tap tree');
// Create a queue/heap of the provided scripts prioritized according to their
// corresponding weights.
const queue = new FastPriorityQueue((a, b) => {
return a.weight < b.weight;
});
scripts.forEach((script, index) => {
const weight = weights[index] || 1;
assert(weight > 0, 'script weight must be a positive value');
queue.add({
weight,
taggedHash: hashTapLeaf(script),
paths: { [index]: [] },
});
});
// Now that we have a queue of weighted scripts, we begin a loop whereby we
// remove the two lowest weighted items from the queue. We create a tap branch
// node from the two items, and add the branch back to the queue with the
// combined weight of both its children. Each loop reduces the number of items
// in the queue by one, and we repeat until we are left with only one item -
// this becomes the tap tree root.
//
// For example, if we begin with scripts A, B, C, D with weights 6, 3, 1, 1
// After first loop: A(6), B(3), CD(1 + 1)
// After second loop: A(6), B[CD](3 + 2)
// Final loop: A[B[CD]](6+5)
// The final tree will look like:
//
// A[B[CD]]
// / \
// A B[CD]
// / \
// B [CD]
// / \
// C D
//
// This ensures that the spending conditions we believe to have the highest
// probability of being used are further up the tree than less likely scripts,
// thereby reducing the size of the merkle proofs for the more likely scripts.
while (queue.size > 1) {
// We can safely expect two polls to return non-null elements since we've
// checked that the queue has at least two elements before looping.
const child1 = queue.poll();
const child2 = queue.poll();
Object.values(child1.paths).forEach((path) => path.push(child2.taggedHash));
Object.values(child2.paths).forEach((path) => path.push(child1.taggedHash));
queue.add({
taggedHash: hashTapBranch(child1.taggedHash, child2.taggedHash),
weight: child1.weight + child2.weight,
paths: { ...child1.paths, ...child2.paths },
});
}
// After the while loop above completes we should have exactly one element
// remaining in the queue, which we can safely extract below.
const rootNode = queue.poll();
const paths = Object.entries(rootNode.paths).reduce((acc, [index, path]) => {
acc[Number(index)] = path; // TODO: Why doesn't TS know it's a number?
return acc;
}, Array(scripts.length));
return { root: rootNode.taggedHash, paths };
}
function getControlBlock(parity, pubkey, path, leafVersion = exports.INITIAL_TAPSCRIPT_VERSION) {
const parityVersion = leafVersion + parity;
return Buffer.concat([Buffer.of(parityVersion), pubkey, ...path]);
}
/**
* Parses a taproot witness stack and extracts key data elements.
* @param witnessStack
* @returns {ScriptPathWitness|KeyPathWitness} an object representing the
* parsed witness for a script path or key path spend.
* @throws {Error} if the witness stack does not conform to the BIP 341 script validation rules
*/
function parseTaprootWitness(witnessStack) {
let annex;
if (witnessStack.length >= 2 && witnessStack[witnessStack.length - 1][0] === 0x50) {
// If there are at least two witness elements, and the first byte of the last element is
// 0x50, this last element is called annex a and is removed from the witness stack
annex = witnessStack[witnessStack.length - 1];
witnessStack = witnessStack.slice(0, -1);
}
if (witnessStack.length < 1) {
throw new Error('witness stack must have at least one element');
}
else if (witnessStack.length === 1) {
// key path spend
const signature = witnessStack[0];
if (!bitcoinjs_lib_1.script.isCanonicalSchnorrSignature(signature)) {
throw new Error('invalid signature');
}
return { spendType: 'Key', signature, annex };
}
// script path spend
// second to last element is the tapscript
const tapscript = witnessStack[witnessStack.length - 2];
const tapscriptChunks = bitcoinjs_lib_1.script.decompile(tapscript);
if (!tapscriptChunks || tapscriptChunks.length === 0) {
throw new Error('tapscript is not a valid script');
}
// The last stack element is called the control block c, and must have length 33 + 32m,
// for a value of m that is an integer between 0 and 128, inclusive
const controlBlock = witnessStack[witnessStack.length - 1];
if (controlBlock.length < 33 || controlBlock.length > 33 + 32 * 128 || controlBlock.length % 32 !== 1) {
throw new Error('invalid control block length');
}
return {
spendType: 'Script',
scriptSig: witnessStack.slice(0, -2),
tapscript,
controlBlock,
annex,
};
}
/**
* Parses a taproot control block.
* @param ecc Elliptic curve implementation
* @param controlBlock the control block to parse
* @returns {ControlBlock} the parsed control block
* @throws {Error} if the witness stack does not conform to the BIP 341 script validation rules
*/
function parseControlBlock(ecc, controlBlock) {
if ((controlBlock.length - 1) % 32 !== 0) {
throw new TypeError('Invalid control block length');
}
const parity = controlBlock[0] & 0x01;
// Let p = c[1:33] and let P = lift_x(int(p)) where lift_x and [:] are defined as in BIP340.
// Fail if this point is not on the curve
const internalPubkey = controlBlock.slice(1, 33);
if (!ecc.isXOnlyPoint(internalPubkey)) {
throw new Error('internal pubkey is not an EC point');
}
// The leaf version cannot be 0x50 as that would result in ambiguity with the annex.
const leafVersion = controlBlock[0] & 0xfe;
if (leafVersion === 0x50) {
throw new Error('invalid leaf version');
}
const path = [];
for (let j = 33; j < controlBlock.length; j += 32) {
path.push(controlBlock.slice(j, j + 32));
}
return {
parity,
internalPubkey,
leafVersion,
path,
};
}
/**
* Calculates the tapleaf hash from a control block and script.
* @param ecc Elliptic curve implementation
* @param controlBlock the control block, either raw or parsed
* @param tapscript the leaf script corresdponding to the control block
* @returns {Buffer} the tapleaf hash
*/
function getTapleafHash(ecc, controlBlock, tapscript) {
if (Buffer.isBuffer(controlBlock)) {
controlBlock = parseControlBlock(ecc, controlBlock);
}
const { leafVersion } = controlBlock;
return bitcoinjs_lib_1.crypto.taggedHash('TapLeaf', Buffer.concat([Buffer.of(leafVersion), serializeScriptSize(tapscript), tapscript]));
}
/**
* Calculates the taptree root hash from a control block and script.
* @param ecc Elliptic curve implementation
* @param controlBlock the control block, either raw or parsed
* @param tapscript the leaf script corresdponding to the control block
* @param tapleafHash the leaf hash if already calculated
* @returns {Buffer} the taptree root hash
*/
function getTaptreeRoot(ecc, controlBlock, tapscript, tapleafHash) {
if (Buffer.isBuffer(controlBlock)) {
controlBlock = parseControlBlock(ecc, controlBlock);
}
const { path } = controlBlock;
tapleafHash = tapleafHash || getTapleafHash(ecc, controlBlock, tapscript);
// `taptreeMerkleHash` begins as our tapscript tapleaf hash and its value iterates
// through its parent tapbranch hashes until it ends up as the taptree root hash
let taptreeMerkleHash = tapleafHash;
for (const taptreeSiblingHash of path) {
taptreeMerkleHash =
Buffer.compare(taptreeMerkleHash, taptreeSiblingHash) === -1
? bitcoinjs_lib_1.crypto.taggedHash('TapBranch', Buffer.concat([taptreeMerkleHash, taptreeSiblingHash]))
: bitcoinjs_lib_1.crypto.taggedHash('TapBranch', Buffer.concat([taptreeSiblingHash, taptreeMerkleHash]));
}
return taptreeMerkleHash;
}
function getTweakedOutputKey(payment) {
assert(payment.output);
if (payment.output.length === 34) {
return payment.output?.subarray(2);
}
throw new Error(`invalid p2tr tweaked output key size ${payment.output.length}`);
}
/**
* @returns output script for either script path input controlBlock
* & leafScript OR key path input internalPubKey & taptreeRoot
*/
function createTaprootOutputScript(p2trArgs) {
let internalPubKey;
let taptreeRoot;
if ('internalPubKey' in p2trArgs) {
internalPubKey = p2trArgs.internalPubKey;
taptreeRoot = p2trArgs.taptreeRoot;
}
else {
internalPubKey = parseControlBlock(secp256k1_1.ecc, p2trArgs.controlBlock).internalPubkey;
taptreeRoot = getTaptreeRoot(secp256k1_1.ecc, p2trArgs.controlBlock, p2trArgs.leafScript);
}
const outputKey = tapTweakPubkey(secp256k1_1.ecc, internalPubKey, taptreeRoot).xOnlyPubkey;
return bitcoinjs_lib_1.script.compile([bitcoinjs_lib_1.script.OPS.OP_1, Buffer.from(outputKey)]);
}
/**
* @returns x-only taproot output key (tapOutputKey)
*/
function getTaprootOutputKey(outputScript) {
const outputDecompiled = bitcoinjs_lib_1.script.decompile(outputScript);
if (outputDecompiled?.length !== 2) {
throw new Error('invalid taproot output script');
}
const [op1, outputKey] = outputDecompiled;
if (op1 !== bitcoinjs_lib_1.script.OPS.OP_1 || !Buffer.isBuffer(outputKey) || outputKey.length !== 32) {
throw new Error('invalid taproot output script');
}
return outputKey;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFwcm9vdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90YXByb290LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQ0c7OztBQTJFSCxzREE2REM7QUFPRCxrREFFQztBQU9ELGtDQUdDO0FBU0Qsc0NBS0M7QUFFRCw4Q0FjQztBQVVELDBDQWNDO0FBY0Qsd0NBU0M7QUEyQ0Qsb0RBS0M7QUFTRCw4Q0FvRUM7QUFFRCwwQ0FTQztBQThCRCxrREEyQ0M7QUFTRCw4Q0ErQkM7QUFTRCx3Q0FjQztBQVVELHdDQXdCQztBQUVELGtEQU1DO0FBTUQsOERBY0M7QUFLRCxrREFVQztBQWxrQkQsaUNBQWtDO0FBQ2xDLHVEQUF3RDtBQUN4RCxpREFBNEY7QUFDNUYsZ0RBQWlEO0FBQ2pELE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0FBRTNDOzs7R0FHRztBQUNVLFFBQUEsbUJBQW1CLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUN0QyxRQUFBLHlCQUF5QixHQUFHLElBQUksQ0FBQztBQVk5Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBZ0RHO0FBQ0gsU0FBZ0IscUJBQXFCLENBQUMsR0FBMkIsRUFBRSxPQUFpQjtJQUNsRiwyQ0FBMkM7SUFDM0MsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLDZEQUE2RCxDQUFDLENBQUM7SUFFMUYsd0VBQXdFO0lBQ3hFLDJEQUEyRDtJQUMzRCx3RUFBd0U7SUFDeEUsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7SUFFN0IseUZBQXlGO0lBQ3pGLHNFQUFzRTtJQUN0RSwrRUFBK0U7SUFDL0UsTUFBTSxDQUFDLEdBQUcsc0JBQU8sQ0FBQyxVQUFVLENBQUMsYUFB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TUFBTSxTQUFTLEdBQUcsY0FBYyxDQUFDLGVBQU0sRUFBRSxjQUFjLEVBQUUsV0FBVyxDQUFDLENBQUMsV0FBVyxDQUFDO0lBQ2xGLE9BQU8sc0JBQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxzQkFBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDckUsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsbUJBQW1CLENBQUMsWUFBMEM7SUFDNUUsTUFBTSxnQkFBZ0IsR0FBRyxzQkFBTyxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUN6RCxJQUFJLGdCQUFnQixFQUFFLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUNuQyxNQUFNLElBQUksS0FBSyxDQUFDLCtCQUErQixDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUNELE1BQU0sQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLEdBQUcsZ0JBQWdCLENBQUM7SUFDMUMsSUFBSSxHQUFHLEtBQUssc0JBQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ3ZGLE1BQU0sSUFBSSxLQUFLLENBQUMsK0JBQStCLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVGhpcyBtb2R1bGUgcHJvdmlkZXMgdGFwcm9vdCB1dGlsaXRpZXMgaW5jbHVkaW5nIHRoZSBsZWdhY3kgTXVTaWcyIGtleSBhZ2dyZWdhdGlvblxuICogYWxnb3JpdGhtIHVzZWQgYnkgQml0R28ncyBkZXByZWNhdGVkIGBwMnRyYCBzY3JpcHQgdHlwZSAoY2hhaW5zIDMwLCAzMSkuXG4gKlxuICogIyMgTGVnYWN5IHAydHIgdnMgU3RhbmRhcmQgcDJ0ck11c2lnMlxuICpcbiAqIEJpdEdvIHN1cHBvcnRzIHR3byB0YXByb290IGFkZHJlc3MgdHlwZXM6XG4gKlxuICogMS4gKipMZWdhY3kgYHAydHJgIChjaGFpbnMgMzAsIDMxKSAtIERFUFJFQ0FURUQqKlxuICogICAgLSBVc2VzIHRoZSBgYWdncmVnYXRlTXVTaWdQdWJrZXlzKClgIGZ1bmN0aW9uIGluIHRoaXMgbW9kdWxlXG4gKiAgICAtIEJhc2VkIG9uIGFuIG9sZGVyIE11U2lnMiB2YXJpYW50IHRoYXQgcHJlZGF0ZXMgQklQMzI3XG4gKiAgICAtIEV4cGVjdHMgMzItYnl0ZSB4LW9ubHkgcHVia2V5cyB3aXRoIGV2ZW4gWSBjb29yZGluYXRlc1xuICogICAgLSBTb3J0cyBrZXlzIEFGVEVSIHgtb25seSBjb252ZXJzaW9uXG4gKiAgICAtIENvcnJlc3BvbmRzIHRvIE11U2lnMiBiZWZvcmUgdGhlIDMyLWJ5dGUgdG8gMzMtYnl0ZSBrZXkgY2hhbmdlXG4gKiAgICAtIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL2pvbmFzbmljay9iaXBzL3B1bGwvMzdcbiAqXG4gKiAyLiAqKlN0YW5kYXJkIGBwMnRyTXVzaWcyYCAoY2hhaW5zIDQwLCA0MSkgLSBSRUNPTU1FTkRFRCoqXG4gKiAgICAtIFVzZXMgdGhlIGBAYnJhbmRvbmJsYWNrL211c2lnYCBsaWJyYXJ5IChzdGFuZGFyZCBCSVAzMjcgaW1wbGVtZW50YXRpb24pXG4gKiAgICAtIFVzZXMgZnVsbCAzMy1ieXRlIGNvbXByZXNzZWQgcHVia2V5cyB0aHJvdWdob3V0IGFnZ3JlZ2F0aW9uXG4gKiAgICAtIEtleSBvcmRlciBhZmZlY3RzIHRoZSByZXN1bHRpbmcgYWdncmVnYXRlIGtleVxuICogICAgLSBGdWxseSBjb21wYXRpYmxlIHdpdGggdGhlIEJJUDMyNyBzcGVjaWZpY2F0aW9uXG4gKlxuICogIyMgS2V5IERpZmZlcmVuY2VcbiAqXG4gKiBUaGUgY3JpdGljYWwgZGlmZmVyZW5jZSBpcyAqKndoZW4geC1vbmx5IGNvbnZlcnNpb24gaGFwcGVucyoqOlxuICogLSBMZWdhY3k6IENvbnZlcnRzIHRvIHgtb25seSBCRUZPUkUgc29ydGluZyAocHJvZHVjZXMgb3JkZXItaW5kZXBlbmRlbnQga2V5cylcbiAqIC0gU3RhbmRhcmQ6IFVzZXMgMzMtYnl0ZSBrZXlzIHRocm91Z2hvdXQgKGtleSBvcmRlciBtYXR0ZXJzKVxuICpcbiAqIEZvciB0aGUgc2FtZSB0d28gcHVia2V5cywgdGhlc2UgbWV0aG9kcyBwcm9kdWNlIERJRkZFUkVOVCBhZ2dyZWdhdGUga2V5cyBiZWNhdXNlXG4gKiB0aGUgc29ydCBvcmRlciBkaWZmZXJzIGJldHdlZW4gMzMtYnl0ZSBhbmQgMzItYnl0ZSByZXByZXNlbnRhdGlvbnMuXG4gKlxuICogU2VlIGBtb2R1bGVzL3V0eG8tbGliL2JpcC0wMzI3L1JFQURNRS5tZGAgZm9yIGRldGFpbGVkIGNvbXBhcmlzb24gYW5kIHRlc3QgY2FzZXMuXG4gKi9cblxuaW1wb3J0IHsgVGFwVHJlZSBhcyBQc2J0VGFwVHJlZSwgVGFwTGVhZiBhcyBQc2J0VGFwTGVhZiB9IGZyb20gJ2JpcDE3NC9zcmMvbGliL2ludGVyZmFjZXMnO1xuaW1wb3J0IGFzc2VydCA9IHJlcXVpcmUoJ2Fzc2VydCcpO1xuaW1wb3J0IEZhc3RQcmlvcml0eVF1ZXVlID0gcmVxdWlyZSgnZmFzdHByaW9yaXR5cXVldWUnKTtcbmltcG9ydCB7IHNjcmlwdCBhcyBic2NyaXB0LCBjcnlwdG8gYXMgYmNyeXB0bywgcGF5bWVudHMgYXMgYnBheW1lbnRzIH0gZnJvbSAnYml0Y29pbmpzLWxpYic7XG5pbXBvcnQgeyBlY2MgYXMgZWNjTGliIH0gZnJvbSAnQGJpdGdvL3NlY3AyNTZrMSc7XG5jb25zdCB2YXJ1aW50ID0gcmVxdWlyZSgndmFydWludC1iaXRjb2luJyk7XG5cbi8qKlxuICogVGhlIDB4MDIgcHJlZml4IGluZGljYXRpbmcgYW4gZXZlbiBZIGNvb3JkaW5hdGUgd2hpY2ggaXMgaW1wbGljaXRseSBhc3N1bWVkXG4gKiBvbiBhbGwgMzIgYnl0ZSB4LW9ubHkgcHViIGtleXMgYXMgZGVmaW5lZCBpbiBCSVAzNDAuXG4gKi9cbmV4cG9ydCBjb25zdCBFVkVOX1lfQ09PUkRfUFJFRklYID0gQnVmZmVyLm9mKDB4MDIpO1xuZXhwb3J0IGNvbnN0IElOSVRJQUxfVEFQU0NSSVBUX1ZFUlNJT04gPSAweGMwO1xuXG5leHBvcnQgaW50ZXJmYWNlIFRpbnlTZWNwMjU2azFJbnRlcmZhY2Uge1xuICBpc1hPbmx5UG9pbnQocDogVWludDhBcnJheSk6IGJvb2xlYW47XG4gIHhPbmx5UG9pbnRBZGRUd2VhayhwOiBVaW50OEFycmF5LCB0d2VhazogVWludDhBcnJheSk6IFhPbmx5UG9pbnRBZGRUd2Vha1Jlc3VsdCB8IG51bGw7XG4gIHBvaW50RnJvbVNjYWxhcihzazogVWludDhBcnJheSwgY29tcHJlc3NlZD86IGJvb2xlYW4pOiBVaW50OEFycmF5IHwgbnVsbDtcbiAgcG9pbnRNdWx0aXBseShhOiBVaW50OEFycmF5LCBiOiBVaW50OEFycmF5KTogVWludDhBcnJheSB8IG51bGw7XG4gIHBvaW50QWRkKGE6IFVpbnQ4QXJyYXksIGI6IFVpbnQ4QXJyYXkpOiBVaW50OEFycmF5IHwgbnVsbDtcbiAgcHJpdmF0ZUFkZChkOiBVaW50OEFycmF5LCB0d2VhazogVWludDhBcnJheSk6IFVpbnQ4QXJyYXkgfCBudWxsO1xuICBwcml2YXRlTmVnYXRlKGQ6IFVpbnQ4QXJyYXkpOiBVaW50OEFycmF5O1xufVxuXG4vKipcbiAqIEFnZ3JlZ2F0ZXMgYSBsaXN0IG9mIHB1YmxpYyBrZXlzIGludG8gYSBzaW5nbGUgcHVibGljIGtleSB1c2luZyB0aGUgbGVnYWN5IE11U2lnMiBhbGdvcml0aG0uXG4gKlxuICogVGhpcyBpbXBsZW1lbnRzIHRoZSBkZXByZWNhdGVkIGtleSBhZ2dyZWdhdGlvbiBtZXRob2QgdXNlZCBieSBCaXRHbydzIGBwMnRyYCBzY3JpcHQgdHlwZVxuICogKGNoYWlucyAzMCwgMzEpLiBJdCBjb3JyZXNwb25kcyB0byBhbiBvbGRlciB2YXJpYW50IG9mIE11U2lnMiB0aGF0IHByZWRhdGVzIHRoZSBjaGFuZ2VcbiAqIGZyb20gMzItYnl0ZSB0byAzMy1ieXRlIGtleXMgaW4gdGhlIEJJUDMyNyBzcGVjaWZpY2F0aW9uLlxuICpcbiAqICMjIEFsZ29yaXRobVxuICpcbiAqIFRoZSBpbXBsZW1lbnRhdGlvbiBmb2xsb3dzIHRoZSBNdVNpZzIga2V5IGFnZ3JlZ2F0aW9uIHNjaGVtZTpcbiAqXG4gKiBgYGBcbiAqIFAgPSBzdW1faSAozrxfaSAqIFBfaSlcbiAqIGBgYFxuICpcbiAqIHdoZXJlOlxuICogLSBgUF9pYCBpcyB0aGUgcHVibGljIGtleSBvZiB0aGUgaS10aCBzaWduZXJcbiAqIC0gYM68X2lgIGlzIHRoZSBNdVNpZyBjb2VmZmljaWVudCBjb21wdXRlZCBhczpcbiAqICAgLSBgTCA9IFRhZ2dlZEhhc2goXCJLZXlBZ2cgbGlzdFwiLCBQXzEgfHwgUF8yIHx8IC4uLiB8fCBQX24pYFxuICogICAtIGDOvF9pID0gVGFnZ2VkSGFzaChcIktleUFnZyBjb2VmZmljaWVudFwiLCBMIHx8IFBfaSlgIGZvciBtb3N0IGtleXNcbiAqICAgLSBgzrxfaSA9IDFgIGZvciB0aGUgc2Vjb25kIHVuaXF1ZSBrZXkgKG9wdGltaXphdGlvbiB0byBzYXZlIGFuIGV4cG9uZW50aWF0aW9uKVxuICpcbiAqICMjIEtleSBDaGFyYWN0ZXJpc3RpY3MgKExlZ2FjeSBWYXJpYW50KVxuICpcbiAqIDEuICoqWC1vbmx5IHB1YmtleXMqKjogRXhwZWN0cyAzMi1ieXRlIHgtb25seSBwdWJrZXlzIChhc3N1bWVzIGV2ZW4gWSBjb29yZGluYXRlcylcbiAqIDIuICoqUHJlLWFnZ3JlZ2F0aW9uIHNvcnRpbmcqKjogU29ydHMga2V5cyBpbiBhc2NlbmRpbmcgb3JkZXIgQkVGT1JFIGFnZ3JlZ2F0aW9uXG4gKiAzLiAqKk9yZGVyLWluZGVwZW5kZW50Kio6IER1ZSB0byBzb3J0aW5nLCBrZXkgb3JkZXIgZG9lc24ndCBhZmZlY3QgdGhlIHJlc3VsdFxuICogNC4gKiozMy1ieXRlIGludGVybmFsIHJlcHJlc2VudGF0aW9uKio6IEludGVybmFsbHkgcHJlcGVuZHMgMHgwMiBwcmVmaXggZm9yIGVsbGlwdGljIGN1cnZlIG9wZXJhdGlvbnNcbiAqXG4gKiAjIyBEaWZmZXJlbmNlcyBmcm9tIFN0YW5kYXJkIE11U2lnMiAoQklQMzI3KVxuICpcbiAqIFRoZSBzdGFuZGFyZCBNdVNpZzIgaW1wbGVtZW50YXRpb24gKGBAYnJhbmRvbmJsYWNrL211c2lnYCBsaWJyYXJ5KTpcbiAqIC0gVXNlcyAzMy1ieXRlIGNvbXByZXNzZWQgcHVia2V5cyB0aHJvdWdob3V0XG4gKiAtIERvZXMgTk9UIHNvcnQga2V5cyBiZWZvcmUgYWdncmVnYXRpb24gKGtleSBvcmRlciBtYXR0ZXJzKVxuICogLSBQcm9kdWNlcyBkaWZmZXJlbnQgYWdncmVnYXRlIGtleXMgZXZlbiBmb3IgdGhlIHNhbWUgc2V0IG9mIHB1YmtleXNcbiAqXG4gKiAjIyBSZWZlcmVuY2UgSW1wbGVtZW50YXRpb25cbiAqXG4gKiBUaGlzIGNvcnJlc3BvbmRzIHRvIGBrZXlfYWdnX2JpdGdvX3AydHJfbGVnYWN5KClgIGluIHRoZSBQeXRob24gcmVmZXJlbmNlIGltcGxlbWVudGF0aW9uXG4gKiBhdCBgbW9kdWxlcy91dHhvLWxpYi9iaXAtMDMyNy9yZWZlcmVuY2UucHlgLlxuICpcbiAqIEBwYXJhbSBlY2MgRWxsaXB0aWMgY3VydmUgaW1wbGVtZW50YXRpb25cbiAqIEBwYXJhbSBwdWJrZXlzIExpc3Qgb2YgMzItYnl0ZSB4LW9ubHkgcHVibGljIGtleXMgdG8gYWdncmVnYXRlIChtdXN0IGhhdmUgZXZlbiBZIGNvb3JkaW5hdGVzKVxuICogQHJldHVybnMgMzItYnl0ZSBCdWZmZXIgcmVwcmVzZW50aW5nIHRoZSB4LW9ubHkgYWdncmVnYXRlIHB1YmxpYyBrZXlcbiAqIEB0aHJvd3Mge0Vycm9yfSBpZiBmZXdlciB0aGFuIDIgcHVia2V5cyBwcm92aWRlZCBvciBlbGxpcHRpYyBjdXJ2ZSBvcGVyYXRpb25zIGZhaWxcbiAqXG4gKiBAc2VlIG1vZHVsZXMvdXR4by1saWIvYmlwLTAzMjcvUkVBRE1FLm1kIGZvciBkZXRhaWxlZCBjb21wYXJpc29uIHdpdGggc3RhbmRhcmQgTXVTaWcyXG4gKiBAc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9qb25hc25pY2svYmlwcy9wdWxsLzM3IGZvciB0aGUgTXVTaWcyIHNwZWNpZmljYXRpb24gY2hhbmdlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhZ2dyZWdhdGVNdVNpZ1B1YmtleXMoZWNjOiBUaW55U2VjcDI1NmsxSW50ZXJmYWNlLCBwdWJrZXlzOiBCdWZmZXJbXSk6IFVpbnQ4QXJyYXkge1xuICAvLyBUT0RPOiBDb25zaWRlciBlbmZvcmNpbmcga2V5IHVuaXF1ZW5lc3MuXG4gIGFzc2VydChwdWJrZXlzLmxlbmd0aCA+IDEsICdhdCBsZWFzdCB0d28gcHVia2V5cyBhcmUgcmVxdWlyZWQgZm9yIG11c2lnIGtleSBhZ2dyZWdhdGlvbicpO1xuXG4gIC8vIExFR0FDWSBCRUhBVklPUjogU29ydCB0aGUga2V5cyBpbiBhc2NlbmRpbmcgb3JkZXIgQkVGT1JFIGFnZ3JlZ2F0aW9uLlxuICAvLyBUaGlzIG1ha2VzIHRoZSBhZ2dyZWdhdGUga2V5IGluZGVwZW5kZW50IG9mIGlucHV0IG9yZGVyLlxuICAvLyBTdGFuZGFyZCBNdVNpZzIgKEJJUDMyNykgZG9lcyBOT1Qgc29ydCwgbWFraW5nIGtleSBvcmRlciBzaWduaWZpY2FudC5cbiAgcHVia2V5cy5zb3J0KEJ1ZmZlci5jb21wYXJlKTtcblxuICAvLyBDb21wdXRlIHRoZSBcIktleUFnZyBsaXN0XCIgaGFzaCBMID0gVGFnZ2VkSGFzaChcIktleUFnZyBsaXN0XCIsIFBfMSB8fCBQXzIgfHwgLi4uIHx8IFBfbilcbiAgLy8gVGhpcyBoYXNoIGlzIHVzZWQgdG8gZGVyaXZlIHRoZSBNdVNpZyBjb2VmZmljaWVudHMgZm9yIGVhY2ggcHVia2V5LlxuICAvLyBJbiB0aGUgcmVmZXJlbmNlIGltcGxlbWVudGF0aW9uIChyZWZlcmVuY2UucHkpLCB0aGlzIGlzIGRvbmUgaW4gaGFzaF9rZXlzKCkuXG4gIGNvbnN0IEwgPSBiY3J5cHRvLnRhZ2dlZEhhc2goJ0tleUFnZyBsaXN0JywgQnVmZmVyLmNvbmNhdChwdWJrZXlzKSk7XG5cbiAgLy8gRmluZCB0aGUgc2Vjb25kIHVuaXF1ZSBwdWJrZXkgaW4gdGhlIHNvcnRlZCBsaXN0LlxuICAvLyBUaGlzIGtleSAoYW5kIGFueSBrZXlzIGlkZW50aWNhbCB0byBpdCkgd2lsbCB1c2UgY29lZmZpY2llbnQgzrwgPSAxIGFzIGFuIG9wdGltaXphdGlvbi5cbiAgLy8gVGhpcyBzYXZlcyBhbiBleHBlbnNpdmUgcG9pbnQgbXVsdGlwbGljYXRpb24gKHNlZSBNdVNpZzIqIGFwcGVuZGl4IGluIHRoZSBNdVNpZzIgcGFwZXIpLlxuICAvLyBJbiB0aGUgcmVmZXJlbmNlIGltcGxlbWVudGF0aW9uLCB0aGlzIGlzIGdldF9zZWNvbmRfa2V5KCkuXG4gIGNvbnN0IHNlY29uZFVuaXF1ZVB1YmtleSA9IHB1YmtleXMuZmluZCgocHVia2V5KSA9PiAhcHVia2V5c1swXS5lcXVhbHMocHVia2V5KSk7XG5cbiAgLy8gRm9yIGVhY2ggcHVia2V5IFBfaSwgY29tcHV0ZSB0aGUgdHdlYWtlZCBwdWJrZXkgzrxfaSAqIFBfaVxuICAvLyB3aGVyZSDOvF9pIGlzIHRoZSBNdVNpZyBjb2VmZmljaWVudCBmb3IgdGhhdCBrZXkuXG4gIGNvbnN0IHR3ZWFrZWRQdWJrZXlzOiBVaW50OEFycmF5W10gPSBwdWJrZXlzLm1hcCgocHVia2V5KSA9PiB7XG4gICAgLy8gQ29udmVydCAzMi1ieXRlIHgtb25seSBwdWJrZXkgdG8gMzMtYnl0ZSBjb21wcmVzc2VkIGZvcm1hdCBieSBwcmVwZW5kaW5nIDB4MDIuXG4gICAgLy8gVGhpcyBhc3N1bWVzIGFuIGV2ZW4gWSBjb29yZGluYXRlIChzdGFuZGFyZCBmb3IgeC1vbmx5IHB1YmtleXMgaW4gdGFwcm9vdCkuXG4gICAgLy8gSW4gdGhlIHJlZmVyZW5jZSBpbXBsZW1lbnRhdGlvbiwgdGhpcyBpcyBkb25lIGltcGxpY2l0bHkgaW4ga2V5X2FnZygpIHZpYSBsaWZ0X3goKS5cbiAgICBjb25zdCB4eVB1YmtleSA9IEJ1ZmZlci5jb25jYXQoW0VWRU5fWV9DT09SRF9QUkVGSVgsIHB1YmtleV0pO1xuXG4gICAgaWYgKHNlY29uZFVuaXF1ZVB1YmtleSAhPT0gdW5kZWZpbmVkICYmIHNlY29uZFVuaXF1ZVB1YmtleS5lcXVhbHMocHVia2V5KSkge1xuICAgICAgLy8gT3B0aW1pemF0aW9uOiBUaGUgc2Vjb25kIHVuaXF1ZSBrZXkgZ2V0cyBjb2VmZmljaWVudCDOvCA9IDEuXG4gICAgICAvLyBUaGlzIG1lYW5zIM68X2kgKiBQX2kgPSAxICogUF9pID0gUF9pIChubyBtdWx0aXBsaWNhdGlvbiBuZWVkZWQpLlxuICAgICAgLy8gVGhpcyBzYXZlcyBhbiBleHBlbnNpdmUgZWxsaXB0aWMgY3VydmUgcG9pbnQgbXVsdGlwbGljYXRpb24gb3BlcmF0aW9uLlxuICAgICAgLy8gU2VlIGtleV9hZ2dfY29lZmZfaW50ZXJuYWwoKSBpbiByZWZlcmVuY2UucHkgd2hlcmUgaXQgcmV0dXJucyAxIGZvciBwa18gPT0gcGsyLlxuICAgICAgcmV0dXJuIHh5UHVia2V5O1xuICAgIH1cblxuICAgIC8vIENvbXB1dGUgdGhlIE11U2lnIGNvZWZmaWNpZW50OiDOvF9pID0gVGFnZ2VkSGFzaChcIktleUFnZyBjb2VmZmljaWVudFwiLCBMIHx8IFBfaSlcbiAgICAvLyBJbiB0aGUgcmVmZXJlbmNlIGltcGxlbWVudGF0aW9uLCB0aGlzIGlzIGtleV9hZ2dfY29lZmZfaW50ZXJuYWwoKS5cbiAgICBjb25zdCBjID0gYmNyeXB0by50YWdnZWRIYXNoKCdLZXlBZ2cgY29lZmZpY2llbnQnLCBCdWZmZXIuY29uY2F0KFtMLCBwdWJrZXldKSk7XG5cbiAgICAvLyBDb21wdXRlIHRoZSB0d2Vha2VkIHB1YmtleTogzrxfaSAqIFBfaSAoZWxsaXB0aWMgY3VydmUgcG9pbnQgbXVsdGlwbGljYXRpb24pXG4gICAgY29uc3QgdHdlYWtlZFB1YmtleSA9IGVjYy5wb2ludE11bHRpcGx5KHh5UHVia2V5LCBjKTtcbiAgICBpZiAoIXR3ZWFrZWRQdWJrZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRmFpbGVkIHRvIG11bHRpcGx5IHB1YmtleSBieSBjb2VmZmljaWVudCcpO1xuICAgIH1cbiAgICByZXR1cm4gdHdlYWtlZFB1YmtleTtcbiAgfSk7XG5cbiAgLy8gU3VtIGFsbCB0aGUgdHdlYWtlZCBwdWJrZXlzIHRvIGdldCB0aGUgYWdncmVnYXRlIHB1YmtleTpcbiAgLy8gUCA9IHN1bV9pICjOvF9pICogUF9pKSA9IM68XzEqUF8xICsgzrxfMipQXzIgKyAuLi4gKyDOvF9uKlBfblxuICAvLyBUaGlzIGlzIGVsbGlwdGljIGN1cnZlIHBvaW50IGFkZGl0aW9uLlxuICBjb25zdCBhZ2dyZWdhdGVQdWJrZXkgPSB0d2Vha2VkUHVia2V5cy5yZWR1Y2UoKHByZXYsIGN1cnIpID0+IHtcbiAgICBjb25zdCBuZXh0ID0g