@bitgo-beta/abstract-eth 1.0.2-beta.59 → 1.0.2-beta.590
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1329 -0
- package/dist/src/abstractEthLikeCoin.d.ts +10 -5
- package/dist/src/abstractEthLikeCoin.d.ts.map +1 -1
- package/dist/src/abstractEthLikeCoin.js +14 -11
- package/dist/src/abstractEthLikeNewCoins.d.ts +635 -0
- package/dist/src/abstractEthLikeNewCoins.d.ts.map +1 -0
- package/dist/src/abstractEthLikeNewCoins.js +1927 -0
- package/dist/src/ethLikeToken.d.ts +34 -4
- package/dist/src/ethLikeToken.d.ts.map +1 -1
- package/dist/src/ethLikeToken.js +281 -7
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -2
- package/dist/src/lib/contractCall.d.ts +8 -0
- package/dist/src/lib/contractCall.d.ts.map +1 -0
- package/dist/src/lib/contractCall.js +17 -0
- package/dist/src/lib/iface.d.ts +132 -0
- package/dist/src/lib/iface.d.ts.map +1 -0
- package/dist/src/lib/iface.js +8 -0
- package/dist/src/lib/index.d.ts +15 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +46 -0
- package/dist/src/lib/keyPair.d.ts +26 -0
- package/dist/src/lib/keyPair.d.ts.map +1 -0
- package/dist/src/lib/keyPair.js +66 -0
- package/dist/src/lib/transaction.d.ts +64 -0
- package/dist/src/lib/transaction.d.ts.map +1 -0
- package/dist/src/lib/transaction.js +137 -0
- package/dist/src/lib/transactionBuilder.d.ts +245 -0
- package/dist/src/lib/transactionBuilder.d.ts.map +1 -0
- package/dist/src/lib/transactionBuilder.js +726 -0
- package/dist/src/lib/transferBuilder.d.ts +70 -0
- package/dist/src/lib/transferBuilder.d.ts.map +1 -0
- package/dist/src/lib/transferBuilder.js +257 -0
- package/dist/src/lib/transferBuilders/baseNFTTransferBuilder.d.ts +54 -0
- package/dist/src/lib/transferBuilders/baseNFTTransferBuilder.d.ts.map +1 -0
- package/dist/src/lib/transferBuilders/baseNFTTransferBuilder.js +121 -0
- package/dist/src/lib/transferBuilders/index.d.ts +4 -0
- package/dist/src/lib/transferBuilders/index.d.ts.map +1 -0
- package/dist/src/lib/transferBuilders/index.js +20 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC1155.d.ts +16 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC1155.d.ts.map +1 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC1155.js +93 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC721.d.ts +15 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC721.d.ts.map +1 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC721.js +78 -0
- package/dist/src/lib/types.d.ts +39 -0
- package/dist/src/lib/types.d.ts.map +1 -0
- package/dist/src/lib/types.js +137 -0
- package/dist/src/lib/utils.d.ts +263 -0
- package/dist/src/lib/utils.d.ts.map +1 -0
- package/dist/src/lib/utils.js +681 -0
- package/dist/src/lib/walletUtil.d.ts +30 -0
- package/dist/src/lib/walletUtil.d.ts.map +1 -0
- package/dist/src/lib/walletUtil.js +33 -0
- package/dist/tsconfig.tsbuildinfo +1 -7924
- package/package.json +24 -9
|
@@ -0,0 +1,1927 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AbstractEthLikeNewCoins = exports.optionalDeps = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* @prettier
|
|
9
|
+
*/
|
|
10
|
+
const sdk_core_1 = require("@bitgo-beta/sdk-core");
|
|
11
|
+
const sdk_lib_mpc_1 = require("@bitgo-beta/sdk-lib-mpc");
|
|
12
|
+
const statics_1 = require("@bitgo-beta/statics");
|
|
13
|
+
const utxo_lib_1 = require("@bitgo-beta/utxo-lib");
|
|
14
|
+
const tx_1 = require("@ethereumjs/tx");
|
|
15
|
+
const eth_sig_util_1 = require("@metamask/eth-sig-util");
|
|
16
|
+
const bignumber_js_1 = require("bignumber.js");
|
|
17
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
18
|
+
const crypto_1 = require("crypto");
|
|
19
|
+
const debug_1 = __importDefault(require("debug"));
|
|
20
|
+
const ethereumjs_util_1 = require("ethereumjs-util");
|
|
21
|
+
const keccak_1 = __importDefault(require("keccak"));
|
|
22
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
23
|
+
const secp256k1_1 = __importDefault(require("secp256k1"));
|
|
24
|
+
const abstractEthLikeCoin_1 = require("./abstractEthLikeCoin");
|
|
25
|
+
const ethLikeToken_1 = require("./ethLikeToken");
|
|
26
|
+
const lib_1 = require("./lib");
|
|
27
|
+
const debug = (0, debug_1.default)('bitgo:v2:ethlike');
|
|
28
|
+
exports.optionalDeps = {
|
|
29
|
+
get ethAbi() {
|
|
30
|
+
try {
|
|
31
|
+
return require('ethereumjs-abi');
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
debug('unable to load ethereumjs-abi:');
|
|
35
|
+
debug(e.stack);
|
|
36
|
+
throw new sdk_core_1.EthereumLibraryUnavailableError(`ethereumjs-abi`);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
get ethUtil() {
|
|
40
|
+
try {
|
|
41
|
+
return require('ethereumjs-util');
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
debug('unable to load ethereumjs-util:');
|
|
45
|
+
debug(e.stack);
|
|
46
|
+
throw new sdk_core_1.EthereumLibraryUnavailableError(`ethereumjs-util`);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
get EthTx() {
|
|
50
|
+
try {
|
|
51
|
+
return require('@ethereumjs/tx');
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
debug('unable to load @ethereumjs/tx');
|
|
55
|
+
debug(e.stack);
|
|
56
|
+
throw new sdk_core_1.EthereumLibraryUnavailableError(`@ethereumjs/tx`);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
get EthCommon() {
|
|
60
|
+
try {
|
|
61
|
+
return require('@ethereumjs/common');
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
debug('unable to load @ethereumjs/common:');
|
|
65
|
+
debug(e.stack);
|
|
66
|
+
throw new sdk_core_1.EthereumLibraryUnavailableError(`@ethereumjs/common`);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
class AbstractEthLikeNewCoins extends abstractEthLikeCoin_1.AbstractEthLikeCoin {
|
|
71
|
+
constructor(bitgo, staticsCoin) {
|
|
72
|
+
super(bitgo, staticsCoin);
|
|
73
|
+
/**
|
|
74
|
+
* Get the data required to make an ETH function call defined by the given types and values
|
|
75
|
+
*
|
|
76
|
+
* @param {string} functionName - The name of the function being called, e.g. transfer
|
|
77
|
+
* @param types The types of the function call in order
|
|
78
|
+
* @param values The values of the function call in order
|
|
79
|
+
* @return {Buffer} The combined data for the function call
|
|
80
|
+
*/
|
|
81
|
+
this.getMethodCallData = (functionName, types, values) => {
|
|
82
|
+
return Buffer.concat([
|
|
83
|
+
// function signature
|
|
84
|
+
exports.optionalDeps.ethAbi.methodID(functionName, types),
|
|
85
|
+
// function arguments
|
|
86
|
+
exports.optionalDeps.ethAbi.rawEncode(types, values),
|
|
87
|
+
]);
|
|
88
|
+
};
|
|
89
|
+
if (!staticsCoin) {
|
|
90
|
+
throw new Error('missing required constructor parameter staticsCoin');
|
|
91
|
+
}
|
|
92
|
+
this.staticsCoin = staticsCoin;
|
|
93
|
+
this.sendMethodName = 'sendMultiSig';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Method to return the coin's network object
|
|
97
|
+
* @returns {EthLikeNetwork | undefined}
|
|
98
|
+
*/
|
|
99
|
+
getNetwork() {
|
|
100
|
+
var _a;
|
|
101
|
+
return (_a = this.staticsCoin) === null || _a === void 0 ? void 0 : _a.network;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Evaluates whether an address string is valid for this coin
|
|
105
|
+
* @param {string} address
|
|
106
|
+
* @returns {boolean} True if address is the valid ethlike adderss
|
|
107
|
+
*/
|
|
108
|
+
isValidAddress(address) {
|
|
109
|
+
return exports.optionalDeps.ethUtil.isValidAddress(exports.optionalDeps.ethUtil.addHexPrefix(address));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Flag for sending data along with transactions
|
|
113
|
+
* @returns {boolean} True if okay to send tx data (ETH), false otherwise
|
|
114
|
+
*/
|
|
115
|
+
transactionDataAllowed() {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Default expire time for a contract call (1 week)
|
|
120
|
+
* @returns {number} Time in seconds
|
|
121
|
+
*/
|
|
122
|
+
getDefaultExpireTime() {
|
|
123
|
+
return Math.floor(new Date().getTime() / 1000) + 60 * 60 * 24 * 7;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Method to get the custom chain common object based on params from recovery
|
|
127
|
+
* @param {number} chainId - the chain id of the custom chain
|
|
128
|
+
* @returns {EthLikeCommon.default}
|
|
129
|
+
*/
|
|
130
|
+
static getCustomChainCommon(chainId) {
|
|
131
|
+
const coinName = statics_1.CoinMap.coinNameFromChainId(chainId);
|
|
132
|
+
const coin = statics_1.coins.get(coinName);
|
|
133
|
+
const ethLikeCommon = (0, lib_1.getCommon)(coin.network);
|
|
134
|
+
return ethLikeCommon;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Gets correct Eth Common object based on params from either recovery or tx building
|
|
138
|
+
* @param {EIP1559} eip1559 - configs that specify whether we should construct an eip1559 tx
|
|
139
|
+
* @param {ReplayProtectionOptions} replayProtectionOptions - check if chain id supports replay protection
|
|
140
|
+
* @returns {EthLikeCommon.default}
|
|
141
|
+
*/
|
|
142
|
+
static getEthLikeCommon(eip1559, replayProtectionOptions) {
|
|
143
|
+
var _a;
|
|
144
|
+
// if eip1559 params are specified, default to london hardfork, otherwise,
|
|
145
|
+
// default to tangerine whistle to avoid replay protection issues
|
|
146
|
+
const defaultHardfork = !!eip1559 ? 'london' : exports.optionalDeps.EthCommon.Hardfork.TangerineWhistle;
|
|
147
|
+
const ethLikeCommon = AbstractEthLikeNewCoins.getCustomChainCommon(replayProtectionOptions === null || replayProtectionOptions === void 0 ? void 0 : replayProtectionOptions.chain);
|
|
148
|
+
ethLikeCommon.setHardfork((_a = replayProtectionOptions === null || replayProtectionOptions === void 0 ? void 0 : replayProtectionOptions.hardfork) !== null && _a !== void 0 ? _a : defaultHardfork);
|
|
149
|
+
return ethLikeCommon;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Method to build the tx object
|
|
153
|
+
* @param {BuildTransactionParams} params - params to build transaction
|
|
154
|
+
* @returns {EthLikeTxLib.FeeMarketEIP1559Transaction | EthLikeTxLib.Transaction}
|
|
155
|
+
*/
|
|
156
|
+
static buildTransaction(params) {
|
|
157
|
+
// if eip1559 params are specified, default to london hardfork, otherwise,
|
|
158
|
+
// default to tangerine whistle to avoid replay protection issues
|
|
159
|
+
const ethLikeCommon = AbstractEthLikeNewCoins.getEthLikeCommon(params.eip1559, params.replayProtectionOptions);
|
|
160
|
+
const baseParams = {
|
|
161
|
+
to: params.to,
|
|
162
|
+
nonce: params.nonce,
|
|
163
|
+
value: params.value,
|
|
164
|
+
data: params.data,
|
|
165
|
+
gasLimit: new exports.optionalDeps.ethUtil.BN(params.gasLimit),
|
|
166
|
+
};
|
|
167
|
+
const unsignedEthTx = !!params.eip1559
|
|
168
|
+
? exports.optionalDeps.EthTx.FeeMarketEIP1559Transaction.fromTxData({
|
|
169
|
+
...baseParams,
|
|
170
|
+
maxFeePerGas: new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas),
|
|
171
|
+
maxPriorityFeePerGas: new exports.optionalDeps.ethUtil.BN(params.eip1559.maxPriorityFeePerGas),
|
|
172
|
+
}, { common: ethLikeCommon })
|
|
173
|
+
: exports.optionalDeps.EthTx.Transaction.fromTxData({
|
|
174
|
+
...baseParams,
|
|
175
|
+
gasPrice: new exports.optionalDeps.ethUtil.BN(params.gasPrice),
|
|
176
|
+
}, { common: ethLikeCommon });
|
|
177
|
+
return unsignedEthTx;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Query explorer for the balance of an address
|
|
181
|
+
* @param {String} address - the ETHLike address
|
|
182
|
+
* @returns {BigNumber} address balance
|
|
183
|
+
*/
|
|
184
|
+
async queryAddressBalance(address) {
|
|
185
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
186
|
+
module: 'account',
|
|
187
|
+
action: 'balance',
|
|
188
|
+
address: address,
|
|
189
|
+
});
|
|
190
|
+
// throw if the result does not exist or the result is not a valid number
|
|
191
|
+
if (!result || !result.result || isNaN(result.result)) {
|
|
192
|
+
throw new Error(`Could not obtain address balance for ${address} from the explorer, got: ${result.result}`);
|
|
193
|
+
}
|
|
194
|
+
return new exports.optionalDeps.ethUtil.BN(result.result, 10);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* @param {Recipient[]} recipients - the recipients of the transaction
|
|
198
|
+
* @param {number} expireTime - the expire time of the transaction
|
|
199
|
+
* @param {number} contractSequenceId - the contract sequence id of the transaction
|
|
200
|
+
* @returns {string}
|
|
201
|
+
*/
|
|
202
|
+
getOperationSha3ForExecuteAndConfirm(recipients, expireTime, contractSequenceId) {
|
|
203
|
+
if (!recipients || !Array.isArray(recipients)) {
|
|
204
|
+
throw new Error('expecting array of recipients');
|
|
205
|
+
}
|
|
206
|
+
// Right now we only support 1 recipient
|
|
207
|
+
if (recipients.length !== 1) {
|
|
208
|
+
throw new Error('must send to exactly 1 recipient');
|
|
209
|
+
}
|
|
210
|
+
if (!lodash_1.default.isNumber(expireTime)) {
|
|
211
|
+
throw new Error('expireTime must be number of seconds since epoch');
|
|
212
|
+
}
|
|
213
|
+
if (!lodash_1.default.isNumber(contractSequenceId)) {
|
|
214
|
+
throw new Error('contractSequenceId must be number');
|
|
215
|
+
}
|
|
216
|
+
// Check inputs
|
|
217
|
+
recipients.forEach(function (recipient) {
|
|
218
|
+
if (!lodash_1.default.isString(recipient.address) ||
|
|
219
|
+
!exports.optionalDeps.ethUtil.isValidAddress(exports.optionalDeps.ethUtil.addHexPrefix(recipient.address))) {
|
|
220
|
+
throw new Error('Invalid address: ' + recipient.address);
|
|
221
|
+
}
|
|
222
|
+
let amount;
|
|
223
|
+
try {
|
|
224
|
+
amount = new bignumber_js_1.BigNumber(recipient.amount);
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
throw new Error('Invalid amount for: ' + recipient.address + ' - should be numeric');
|
|
228
|
+
}
|
|
229
|
+
recipient.amount = amount.toFixed(0);
|
|
230
|
+
if (recipient.data && !lodash_1.default.isString(recipient.data)) {
|
|
231
|
+
throw new Error('Data for recipient ' + recipient.address + ' - should be of type hex string');
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
const recipient = recipients[0];
|
|
235
|
+
return exports.optionalDeps.ethUtil.bufferToHex(exports.optionalDeps.ethAbi.soliditySHA3(...this.getOperation(recipient, expireTime, contractSequenceId)));
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Get transfer operation for coin
|
|
239
|
+
* @param {Recipient} recipient - recipient info
|
|
240
|
+
* @param {number} expireTime - expiry time
|
|
241
|
+
* @param {number} contractSequenceId - sequence id
|
|
242
|
+
* @returns {Array} operation array
|
|
243
|
+
*/
|
|
244
|
+
getOperation(recipient, expireTime, contractSequenceId) {
|
|
245
|
+
const network = this.getNetwork();
|
|
246
|
+
return [
|
|
247
|
+
['string', 'address', 'uint', 'bytes', 'uint', 'uint'],
|
|
248
|
+
[
|
|
249
|
+
network.nativeCoinOperationHashPrefix,
|
|
250
|
+
new exports.optionalDeps.ethUtil.BN(exports.optionalDeps.ethUtil.stripHexPrefix(recipient.address), 16),
|
|
251
|
+
recipient.amount,
|
|
252
|
+
Buffer.from(exports.optionalDeps.ethUtil.stripHexPrefix(exports.optionalDeps.ethUtil.padToEven(recipient.data || '')), 'hex'),
|
|
253
|
+
expireTime,
|
|
254
|
+
contractSequenceId,
|
|
255
|
+
],
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Queries the contract (via explorer API) for the next sequence ID
|
|
260
|
+
* @param {String} address - address of the contract
|
|
261
|
+
* @returns {Promise<Number>} sequence ID
|
|
262
|
+
*/
|
|
263
|
+
async querySequenceId(address) {
|
|
264
|
+
// Get sequence ID using contract call
|
|
265
|
+
const sequenceIdMethodSignature = exports.optionalDeps.ethAbi.methodID('getNextSequenceId', []);
|
|
266
|
+
const sequenceIdArgs = exports.optionalDeps.ethAbi.rawEncode([], []);
|
|
267
|
+
const sequenceIdData = Buffer.concat([sequenceIdMethodSignature, sequenceIdArgs]).toString('hex');
|
|
268
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
269
|
+
module: 'proxy',
|
|
270
|
+
action: 'eth_call',
|
|
271
|
+
to: address,
|
|
272
|
+
data: sequenceIdData,
|
|
273
|
+
tag: 'latest',
|
|
274
|
+
});
|
|
275
|
+
if (!result || !result.result) {
|
|
276
|
+
throw new Error('Could not obtain sequence ID from explorer, got: ' + result.result);
|
|
277
|
+
}
|
|
278
|
+
const sequenceIdHex = result.result;
|
|
279
|
+
return new exports.optionalDeps.ethUtil.BN(sequenceIdHex.slice(2), 16).toNumber();
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Recover an unsupported token from a BitGo multisig wallet
|
|
283
|
+
* This builds a half-signed transaction, for which there will be an admin route to co-sign and broadcast. Optionally
|
|
284
|
+
* the user can set params.broadcast = true and the half-signed tx will be sent to BitGo for cosigning and broadcasting
|
|
285
|
+
* @param {RecoverTokenOptions} params
|
|
286
|
+
* @param {Wallet} params.wallet - the wallet to recover the token from
|
|
287
|
+
* @param {string} params.tokenContractAddress - the contract address of the unsupported token
|
|
288
|
+
* @param {string} params.recipient - the destination address recovered tokens should be sent to
|
|
289
|
+
* @param {string} params.walletPassphrase - the wallet passphrase
|
|
290
|
+
* @param {string} params.prv - the xprv
|
|
291
|
+
* @param {boolean} params.broadcast - if true, we will automatically submit the half-signed tx to BitGo for cosigning and broadcasting
|
|
292
|
+
* @returns {Promise<RecoverTokenTransaction>}
|
|
293
|
+
*/
|
|
294
|
+
async recoverToken(params) {
|
|
295
|
+
const network = this.getNetwork();
|
|
296
|
+
if (!lodash_1.default.isObject(params)) {
|
|
297
|
+
throw new Error(`recoverToken must be passed a params object. Got ${params} (type ${typeof params})`);
|
|
298
|
+
}
|
|
299
|
+
if (lodash_1.default.isUndefined(params.tokenContractAddress) || !lodash_1.default.isString(params.tokenContractAddress)) {
|
|
300
|
+
throw new Error(`tokenContractAddress must be a string, got ${params.tokenContractAddress} (type ${typeof params.tokenContractAddress})`);
|
|
301
|
+
}
|
|
302
|
+
if (!this.isValidAddress(params.tokenContractAddress)) {
|
|
303
|
+
throw new Error('tokenContractAddress not a valid address');
|
|
304
|
+
}
|
|
305
|
+
if (lodash_1.default.isUndefined(params.wallet) || !(params.wallet instanceof sdk_core_1.Wallet)) {
|
|
306
|
+
throw new Error(`wallet must be a wallet instance, got ${params.wallet} (type ${typeof params.wallet})`);
|
|
307
|
+
}
|
|
308
|
+
if (lodash_1.default.isUndefined(params.recipient) || !lodash_1.default.isString(params.recipient)) {
|
|
309
|
+
throw new Error(`recipient must be a string, got ${params.recipient} (type ${typeof params.recipient})`);
|
|
310
|
+
}
|
|
311
|
+
if (!this.isValidAddress(params.recipient)) {
|
|
312
|
+
throw new Error('recipient not a valid address');
|
|
313
|
+
}
|
|
314
|
+
if (!exports.optionalDeps.ethUtil.bufferToHex || !exports.optionalDeps.ethAbi.soliditySHA3) {
|
|
315
|
+
throw new Error('ethereum not fully supported in this environment');
|
|
316
|
+
}
|
|
317
|
+
// Get token balance from external API
|
|
318
|
+
const coinSpecific = params.wallet.coinSpecific();
|
|
319
|
+
if (!coinSpecific || !lodash_1.default.isString(coinSpecific.baseAddress)) {
|
|
320
|
+
throw new Error('missing required coin specific property baseAddress');
|
|
321
|
+
}
|
|
322
|
+
const recoveryAmount = await this.queryAddressTokenBalance(params.tokenContractAddress, coinSpecific.baseAddress);
|
|
323
|
+
if (params.broadcast) {
|
|
324
|
+
// We're going to create a normal ETH transaction that sends an amount of 0 ETH to the
|
|
325
|
+
// tokenContractAddress and encode the unsupported-token-send data in the data field
|
|
326
|
+
// #tricksy
|
|
327
|
+
const sendMethodArgs = [
|
|
328
|
+
{
|
|
329
|
+
name: '_to',
|
|
330
|
+
type: 'address',
|
|
331
|
+
value: params.recipient,
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: '_value',
|
|
335
|
+
type: 'uint256',
|
|
336
|
+
value: recoveryAmount.toString(10),
|
|
337
|
+
},
|
|
338
|
+
];
|
|
339
|
+
const methodSignature = exports.optionalDeps.ethAbi.methodID('transfer', lodash_1.default.map(sendMethodArgs, 'type'));
|
|
340
|
+
const encodedArgs = exports.optionalDeps.ethAbi.rawEncode(lodash_1.default.map(sendMethodArgs, 'type'), lodash_1.default.map(sendMethodArgs, 'value'));
|
|
341
|
+
const sendData = Buffer.concat([methodSignature, encodedArgs]);
|
|
342
|
+
const broadcastParams = {
|
|
343
|
+
address: params.tokenContractAddress,
|
|
344
|
+
amount: '0',
|
|
345
|
+
data: sendData.toString('hex'),
|
|
346
|
+
};
|
|
347
|
+
if (params.walletPassphrase) {
|
|
348
|
+
broadcastParams.walletPassphrase = params.walletPassphrase;
|
|
349
|
+
}
|
|
350
|
+
else if (params.prv) {
|
|
351
|
+
broadcastParams.prv = params.prv;
|
|
352
|
+
}
|
|
353
|
+
return await params.wallet.send(broadcastParams);
|
|
354
|
+
}
|
|
355
|
+
const recipient = {
|
|
356
|
+
address: params.recipient,
|
|
357
|
+
amount: recoveryAmount.toString(10),
|
|
358
|
+
};
|
|
359
|
+
// This signature will be valid for one week
|
|
360
|
+
const expireTime = Math.floor(new Date().getTime() / 1000) + 60 * 60 * 24 * 7;
|
|
361
|
+
// Get sequence ID. We do this by building a 'fake' eth transaction, so the platform will increment and return us the new sequence id
|
|
362
|
+
// This _does_ require the user to have a non-zero wallet balance
|
|
363
|
+
const { nextContractSequenceId, gasPrice, gasLimit } = (await params.wallet.prebuildTransaction({
|
|
364
|
+
recipients: [
|
|
365
|
+
{
|
|
366
|
+
address: params.recipient,
|
|
367
|
+
amount: '1',
|
|
368
|
+
},
|
|
369
|
+
],
|
|
370
|
+
}));
|
|
371
|
+
// these recoveries need to be processed by support, but if the customer sends any transactions before recovery is
|
|
372
|
+
// complete the sequence ID will be invalid. artificially inflate the sequence ID to allow more time for processing
|
|
373
|
+
const safeSequenceId = nextContractSequenceId + 1000;
|
|
374
|
+
// Build sendData for ethereum tx
|
|
375
|
+
const operationTypes = ['string', 'address', 'uint', 'address', 'uint', 'uint'];
|
|
376
|
+
const operationArgs = [
|
|
377
|
+
// Token operation has prefix has been added here so that ether operation hashes, signatures cannot be re-used for tokenSending
|
|
378
|
+
network.tokenOperationHashPrefix,
|
|
379
|
+
new exports.optionalDeps.ethUtil.BN(exports.optionalDeps.ethUtil.stripHexPrefix(recipient.address), 16),
|
|
380
|
+
recipient.amount,
|
|
381
|
+
new exports.optionalDeps.ethUtil.BN(exports.optionalDeps.ethUtil.stripHexPrefix(params.tokenContractAddress), 16),
|
|
382
|
+
expireTime,
|
|
383
|
+
safeSequenceId,
|
|
384
|
+
];
|
|
385
|
+
const operationHash = exports.optionalDeps.ethUtil.bufferToHex(exports.optionalDeps.ethAbi.soliditySHA3(operationTypes, operationArgs));
|
|
386
|
+
const userPrv = await params.wallet.getPrv({
|
|
387
|
+
prv: params.prv,
|
|
388
|
+
walletPassphrase: params.walletPassphrase,
|
|
389
|
+
});
|
|
390
|
+
const signature = sdk_core_1.Util.ethSignMsgHash(operationHash, sdk_core_1.Util.xprvToEthPrivateKey(userPrv));
|
|
391
|
+
return {
|
|
392
|
+
halfSigned: {
|
|
393
|
+
recipient: recipient,
|
|
394
|
+
expireTime: expireTime,
|
|
395
|
+
contractSequenceId: safeSequenceId,
|
|
396
|
+
operationHash: operationHash,
|
|
397
|
+
signature: signature,
|
|
398
|
+
gasLimit: gasLimit,
|
|
399
|
+
gasPrice: gasPrice,
|
|
400
|
+
tokenContractAddress: params.tokenContractAddress,
|
|
401
|
+
walletId: params.wallet.id(),
|
|
402
|
+
},
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Ensure either enterprise or newFeeAddress is passed, to know whether to create new key or use enterprise key
|
|
407
|
+
* @param {PrecreateBitGoOptions} params
|
|
408
|
+
* @param {string} params.enterprise {String} the enterprise id to associate with this key
|
|
409
|
+
* @param {string} params.newFeeAddress {Boolean} create a new fee address (enterprise not needed in this case)
|
|
410
|
+
* @returns {void}
|
|
411
|
+
*/
|
|
412
|
+
preCreateBitGo(params) {
|
|
413
|
+
// We always need params object, since either enterprise or newFeeAddress is required
|
|
414
|
+
if (!lodash_1.default.isObject(params)) {
|
|
415
|
+
throw new Error(`preCreateBitGo must be passed a params object. Got ${params} (type ${typeof params})`);
|
|
416
|
+
}
|
|
417
|
+
if (lodash_1.default.isUndefined(params.enterprise) && lodash_1.default.isUndefined(params.newFeeAddress)) {
|
|
418
|
+
throw new Error('expecting enterprise when adding BitGo key. If you want to create a new ETH bitgo key, set the newFeeAddress parameter to true.');
|
|
419
|
+
}
|
|
420
|
+
// Check whether key should be an enterprise key or a BitGo key for a new fee address
|
|
421
|
+
if (!lodash_1.default.isUndefined(params.enterprise) && !lodash_1.default.isUndefined(params.newFeeAddress)) {
|
|
422
|
+
throw new Error(`Incompatible arguments - cannot pass both enterprise and newFeeAddress parameter.`);
|
|
423
|
+
}
|
|
424
|
+
if (!lodash_1.default.isUndefined(params.enterprise) && !lodash_1.default.isString(params.enterprise)) {
|
|
425
|
+
throw new Error(`enterprise should be a string - got ${params.enterprise} (type ${typeof params.enterprise})`);
|
|
426
|
+
}
|
|
427
|
+
if (!lodash_1.default.isUndefined(params.newFeeAddress) && !lodash_1.default.isBoolean(params.newFeeAddress)) {
|
|
428
|
+
throw new Error(`newFeeAddress should be a boolean - got ${params.newFeeAddress} (type ${typeof params.newFeeAddress})`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Queries public block explorer to get the next ETHLike coin's nonce that should be used for the given ETH address
|
|
433
|
+
* @param {string} address
|
|
434
|
+
* @returns {Promise<number>}
|
|
435
|
+
*/
|
|
436
|
+
async getAddressNonce(address) {
|
|
437
|
+
// Get nonce for backup key (should be 0)
|
|
438
|
+
let nonce = 0;
|
|
439
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
440
|
+
module: 'account',
|
|
441
|
+
action: 'txlist',
|
|
442
|
+
address,
|
|
443
|
+
});
|
|
444
|
+
if (!result || !Array.isArray(result.result)) {
|
|
445
|
+
throw new Error('Unable to find next nonce from Etherscan, got: ' + JSON.stringify(result));
|
|
446
|
+
}
|
|
447
|
+
const backupKeyTxList = result.result;
|
|
448
|
+
if (backupKeyTxList.length > 0) {
|
|
449
|
+
// Calculate last nonce used
|
|
450
|
+
const outgoingTxs = backupKeyTxList.filter((tx) => tx.from === address);
|
|
451
|
+
nonce = outgoingTxs.length;
|
|
452
|
+
}
|
|
453
|
+
return nonce;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Helper function for recover()
|
|
457
|
+
* This transforms the unsigned transaction information into a format the BitGo offline vault expects
|
|
458
|
+
* @param {UnformattedTxInfo} txInfo - tx info
|
|
459
|
+
* @param {EthLikeTxLib.Transaction | EthLikeTxLib.FeeMarketEIP1559Transaction} ethTx - the ethereumjs tx object
|
|
460
|
+
* @param {string} userKey - the user's key
|
|
461
|
+
* @param {string} backupKey - the backup key
|
|
462
|
+
* @param {Buffer} gasPrice - gas price for the tx
|
|
463
|
+
* @param {number} gasLimit - gas limit for the tx
|
|
464
|
+
* @param {EIP1559} eip1559 - eip1559 params
|
|
465
|
+
* @param {ReplayProtectionOptions} replayProtectionOptions - replay protection options
|
|
466
|
+
* @returns {Promise<OfflineVaultTxInfo>}
|
|
467
|
+
*/
|
|
468
|
+
async formatForOfflineVault(txInfo, ethTx, userKey, backupKey, gasPrice, gasLimit, eip1559, replayProtectionOptions) {
|
|
469
|
+
if (!ethTx.to) {
|
|
470
|
+
throw new Error('Eth tx must have a `to` address');
|
|
471
|
+
}
|
|
472
|
+
const backupHDNode = utxo_lib_1.bip32.fromBase58(backupKey);
|
|
473
|
+
const backupSigningKey = backupHDNode.publicKey;
|
|
474
|
+
const response = {
|
|
475
|
+
tx: ethTx.serialize().toString('hex'),
|
|
476
|
+
userKey,
|
|
477
|
+
backupKey,
|
|
478
|
+
coin: this.getChain(),
|
|
479
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
480
|
+
gasLimit,
|
|
481
|
+
recipients: [txInfo.recipient],
|
|
482
|
+
walletContractAddress: ethTx.to.toString(),
|
|
483
|
+
amount: txInfo.recipient.amount,
|
|
484
|
+
backupKeyNonce: await this.getAddressNonce(`0x${exports.optionalDeps.ethUtil.publicToAddress(backupSigningKey, true).toString('hex')}`),
|
|
485
|
+
eip1559,
|
|
486
|
+
replayProtectionOptions,
|
|
487
|
+
};
|
|
488
|
+
lodash_1.default.extend(response, txInfo);
|
|
489
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
490
|
+
return response;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Helper function for recover()
|
|
494
|
+
* This transforms the unsigned transaction information into a format the BitGo offline vault expects
|
|
495
|
+
* @param {UnformattedTxInfo} txInfo - tx info
|
|
496
|
+
* @param {EthLikeTxLib.Transaction | EthLikeTxLib.FeeMarketEIP1559Transaction} ethTx - the ethereumjs tx object
|
|
497
|
+
* @param {string} userKey - the user's key
|
|
498
|
+
* @param {string} backupKey - the backup key
|
|
499
|
+
* @param {Buffer} gasPrice - gas price for the tx
|
|
500
|
+
* @param {number} gasLimit - gas limit for the tx
|
|
501
|
+
* @param {number} backupKeyNonce - the nonce of the backup key address
|
|
502
|
+
* @param {EIP1559} eip1559 - eip1559 params
|
|
503
|
+
* @param {ReplayProtectionOptions} replayProtectionOptions - replay protection options
|
|
504
|
+
* @returns {Promise<OfflineVaultTxInfo>}
|
|
505
|
+
*/
|
|
506
|
+
formatForOfflineVaultTSS(txInfo, ethTx, userKey, backupKey, gasPrice, gasLimit, backupKeyNonce, eip1559, replayProtectionOptions) {
|
|
507
|
+
if (!ethTx.to) {
|
|
508
|
+
throw new Error('Eth tx must have a `to` address');
|
|
509
|
+
}
|
|
510
|
+
const response = {
|
|
511
|
+
tx: ethTx.serialize().toString('hex'),
|
|
512
|
+
txHex: ethTx.getMessageToSign(false).toString(),
|
|
513
|
+
userKey,
|
|
514
|
+
backupKey,
|
|
515
|
+
coin: this.getChain(),
|
|
516
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
517
|
+
gasLimit,
|
|
518
|
+
recipients: [txInfo.recipient],
|
|
519
|
+
walletContractAddress: ethTx.to.toString(),
|
|
520
|
+
amount: txInfo.recipient.amount,
|
|
521
|
+
backupKeyNonce: backupKeyNonce,
|
|
522
|
+
eip1559,
|
|
523
|
+
replayProtectionOptions,
|
|
524
|
+
};
|
|
525
|
+
lodash_1.default.extend(response, txInfo);
|
|
526
|
+
return response;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Check whether the gas price passed in by user are within our max and min bounds
|
|
530
|
+
* If they are not set, set them to the defaults
|
|
531
|
+
* @param {number} userGasPrice - user defined gas price
|
|
532
|
+
* @returns {number} the gas price to use for this transaction
|
|
533
|
+
*/
|
|
534
|
+
setGasPrice(userGasPrice) {
|
|
535
|
+
if (!userGasPrice) {
|
|
536
|
+
return statics_1.ethGasConfigs.defaultGasPrice;
|
|
537
|
+
}
|
|
538
|
+
const gasPriceMax = statics_1.ethGasConfigs.maximumGasPrice;
|
|
539
|
+
const gasPriceMin = statics_1.ethGasConfigs.minimumGasPrice;
|
|
540
|
+
if (userGasPrice < gasPriceMin || userGasPrice > gasPriceMax) {
|
|
541
|
+
throw new Error(`Gas price must be between ${gasPriceMin} and ${gasPriceMax}`);
|
|
542
|
+
}
|
|
543
|
+
return userGasPrice;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Check whether gas limit passed in by user are within our max and min bounds
|
|
547
|
+
* If they are not set, set them to the defaults
|
|
548
|
+
* @param {number} userGasLimit user defined gas limit
|
|
549
|
+
* @returns {number} the gas limit to use for this transaction
|
|
550
|
+
*/
|
|
551
|
+
setGasLimit(userGasLimit) {
|
|
552
|
+
if (!userGasLimit) {
|
|
553
|
+
return statics_1.ethGasConfigs.defaultGasLimit;
|
|
554
|
+
}
|
|
555
|
+
const gasLimitMax = statics_1.ethGasConfigs.maximumGasLimit;
|
|
556
|
+
const gasLimitMin = statics_1.ethGasConfigs.minimumGasLimit;
|
|
557
|
+
if (userGasLimit < gasLimitMin || userGasLimit > gasLimitMax) {
|
|
558
|
+
throw new Error(`Gas limit must be between ${gasLimitMin} and ${gasLimitMax}`);
|
|
559
|
+
}
|
|
560
|
+
return userGasLimit;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Helper function for signTransaction for the rare case that SDK is doing the second signature
|
|
564
|
+
* Note: we are expecting this to be called from the offline vault
|
|
565
|
+
* @param {SignFinalOptions.txPrebuild} params.txPrebuild
|
|
566
|
+
* @param {string} params.prv
|
|
567
|
+
* @returns {{txHex: string}}
|
|
568
|
+
*/
|
|
569
|
+
async signFinalEthLike(params) {
|
|
570
|
+
var _a;
|
|
571
|
+
const signingKey = new lib_1.KeyPair({ prv: params.prv }).getKeys().prv;
|
|
572
|
+
if (lodash_1.default.isUndefined(signingKey)) {
|
|
573
|
+
throw new Error('missing private key');
|
|
574
|
+
}
|
|
575
|
+
const txBuilder = this.getTransactionBuilder();
|
|
576
|
+
try {
|
|
577
|
+
txBuilder.from((_a = params.txPrebuild.halfSigned) === null || _a === void 0 ? void 0 : _a.txHex);
|
|
578
|
+
}
|
|
579
|
+
catch (e) {
|
|
580
|
+
throw new Error('invalid half-signed transaction');
|
|
581
|
+
}
|
|
582
|
+
txBuilder.sign({ key: signingKey });
|
|
583
|
+
const tx = await txBuilder.build();
|
|
584
|
+
return {
|
|
585
|
+
txHex: tx.toBroadcastFormat(),
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Assemble half-sign prebuilt transaction
|
|
590
|
+
* @param {SignTransactionOptions} params
|
|
591
|
+
*/
|
|
592
|
+
async signTransaction(params) {
|
|
593
|
+
var _a;
|
|
594
|
+
// Normally the SDK provides the first signature for an EthLike tx, but occasionally it provides the second and final one.
|
|
595
|
+
if (params.isLastSignature) {
|
|
596
|
+
// In this case when we're doing the second (final) signature, the logic is different.
|
|
597
|
+
return await this.signFinalEthLike(params);
|
|
598
|
+
}
|
|
599
|
+
const txBuilder = this.getTransactionBuilder();
|
|
600
|
+
txBuilder.from(params.txPrebuild.txHex);
|
|
601
|
+
txBuilder
|
|
602
|
+
.transfer()
|
|
603
|
+
.coin((_a = this.staticsCoin) === null || _a === void 0 ? void 0 : _a.name)
|
|
604
|
+
.key(new lib_1.KeyPair({ prv: params.prv }).getKeys().prv);
|
|
605
|
+
const transaction = await txBuilder.build();
|
|
606
|
+
const recipients = transaction.outputs.map((output) => ({ address: output.address, amount: output.value }));
|
|
607
|
+
const txParams = {
|
|
608
|
+
eip1559: params.txPrebuild.eip1559,
|
|
609
|
+
txHex: transaction.toBroadcastFormat(),
|
|
610
|
+
recipients: recipients,
|
|
611
|
+
expiration: params.txPrebuild.expireTime,
|
|
612
|
+
hopTransaction: params.txPrebuild.hopTransaction,
|
|
613
|
+
custodianTransactionId: params.custodianTransactionId,
|
|
614
|
+
expireTime: params.expireTime,
|
|
615
|
+
contractSequenceId: params.txPrebuild.nextContractSequenceId,
|
|
616
|
+
sequenceId: params.sequenceId,
|
|
617
|
+
};
|
|
618
|
+
return { halfSigned: txParams };
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Method to validate recovery params
|
|
622
|
+
* @param {RecoverOptions} params
|
|
623
|
+
* @returns {void}
|
|
624
|
+
*/
|
|
625
|
+
validateRecoveryParams(params) {
|
|
626
|
+
if (lodash_1.default.isUndefined(params.userKey)) {
|
|
627
|
+
throw new Error('missing userKey');
|
|
628
|
+
}
|
|
629
|
+
if (lodash_1.default.isUndefined(params.backupKey)) {
|
|
630
|
+
throw new Error('missing backupKey');
|
|
631
|
+
}
|
|
632
|
+
if (lodash_1.default.isUndefined(params.walletPassphrase) && !params.userKey.startsWith('xpub') && !params.isTss) {
|
|
633
|
+
throw new Error('missing wallet passphrase');
|
|
634
|
+
}
|
|
635
|
+
if (lodash_1.default.isUndefined(params.walletContractAddress) || !this.isValidAddress(params.walletContractAddress)) {
|
|
636
|
+
throw new Error('invalid walletContractAddress');
|
|
637
|
+
}
|
|
638
|
+
if (lodash_1.default.isUndefined(params.recoveryDestination) || !this.isValidAddress(params.recoveryDestination)) {
|
|
639
|
+
throw new Error('invalid recoveryDestination');
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Method to sign tss recovery transaction
|
|
644
|
+
* @param {ECDSA.KeyCombined} userKeyCombined
|
|
645
|
+
* @param {ECDSA.KeyCombined} backupKeyCombined
|
|
646
|
+
* @param {string} txHex
|
|
647
|
+
* @param {Object} options
|
|
648
|
+
* @param {EcdsaTypes.SerializedNtilde} options.rangeProofChallenge
|
|
649
|
+
* @returns {Promise<ECDSAMethodTypes.Signature>}
|
|
650
|
+
*/
|
|
651
|
+
async signRecoveryTSS(userKeyCombined, backupKeyCombined, txHex, { rangeProofChallenge, } = {}) {
|
|
652
|
+
if (!userKeyCombined || !backupKeyCombined) {
|
|
653
|
+
throw new Error('Missing key combined shares for user or backup');
|
|
654
|
+
}
|
|
655
|
+
const MPC = new sdk_core_1.Ecdsa();
|
|
656
|
+
const signerOneIndex = userKeyCombined.xShare.i;
|
|
657
|
+
const signerTwoIndex = backupKeyCombined.xShare.i;
|
|
658
|
+
rangeProofChallenge =
|
|
659
|
+
rangeProofChallenge !== null && rangeProofChallenge !== void 0 ? rangeProofChallenge : sdk_lib_mpc_1.EcdsaTypes.serializeNtildeWithProofs(await sdk_lib_mpc_1.EcdsaRangeProof.generateNtilde());
|
|
660
|
+
const userToBackupPaillierChallenge = await sdk_lib_mpc_1.EcdsaPaillierProof.generateP((0, sdk_core_1.hexToBigInt)(userKeyCombined.yShares[signerTwoIndex].n));
|
|
661
|
+
const backupToUserPaillierChallenge = await sdk_lib_mpc_1.EcdsaPaillierProof.generateP((0, sdk_core_1.hexToBigInt)(backupKeyCombined.yShares[signerOneIndex].n));
|
|
662
|
+
const userXShare = MPC.appendChallenge(userKeyCombined.xShare, rangeProofChallenge, sdk_lib_mpc_1.EcdsaTypes.serializePaillierChallenge({ p: userToBackupPaillierChallenge }));
|
|
663
|
+
const userYShare = MPC.appendChallenge(userKeyCombined.yShares[signerTwoIndex], rangeProofChallenge, sdk_lib_mpc_1.EcdsaTypes.serializePaillierChallenge({ p: backupToUserPaillierChallenge }));
|
|
664
|
+
const backupXShare = MPC.appendChallenge(backupKeyCombined.xShare, rangeProofChallenge, sdk_lib_mpc_1.EcdsaTypes.serializePaillierChallenge({ p: backupToUserPaillierChallenge }));
|
|
665
|
+
const backupYShare = MPC.appendChallenge(backupKeyCombined.yShares[signerOneIndex], rangeProofChallenge, sdk_lib_mpc_1.EcdsaTypes.serializePaillierChallenge({ p: userToBackupPaillierChallenge }));
|
|
666
|
+
const signShares = await MPC.signShare(userXShare, userYShare);
|
|
667
|
+
const signConvertS21 = await MPC.signConvertStep1({
|
|
668
|
+
xShare: backupXShare,
|
|
669
|
+
yShare: backupYShare,
|
|
670
|
+
kShare: signShares.kShare,
|
|
671
|
+
});
|
|
672
|
+
const signConvertS12 = await MPC.signConvertStep2({
|
|
673
|
+
aShare: signConvertS21.aShare,
|
|
674
|
+
wShare: signShares.wShare,
|
|
675
|
+
});
|
|
676
|
+
const signConvertS21_2 = await MPC.signConvertStep3({
|
|
677
|
+
muShare: signConvertS12.muShare,
|
|
678
|
+
bShare: signConvertS21.bShare,
|
|
679
|
+
});
|
|
680
|
+
const [signCombineOne, signCombineTwo] = [
|
|
681
|
+
MPC.signCombine({
|
|
682
|
+
gShare: signConvertS12.gShare,
|
|
683
|
+
signIndex: {
|
|
684
|
+
i: signConvertS12.muShare.i,
|
|
685
|
+
j: signConvertS12.muShare.j,
|
|
686
|
+
},
|
|
687
|
+
}),
|
|
688
|
+
MPC.signCombine({
|
|
689
|
+
gShare: signConvertS21_2.gShare,
|
|
690
|
+
signIndex: {
|
|
691
|
+
i: signConvertS21_2.signIndex.i,
|
|
692
|
+
j: signConvertS21_2.signIndex.j,
|
|
693
|
+
},
|
|
694
|
+
}),
|
|
695
|
+
];
|
|
696
|
+
const MESSAGE = Buffer.from(txHex, 'hex');
|
|
697
|
+
const [signA, signB] = [
|
|
698
|
+
MPC.sign(MESSAGE, signCombineOne.oShare, signCombineTwo.dShare, (0, keccak_1.default)('keccak256')),
|
|
699
|
+
MPC.sign(MESSAGE, signCombineTwo.oShare, signCombineOne.dShare, (0, keccak_1.default)('keccak256')),
|
|
700
|
+
];
|
|
701
|
+
return MPC.constructSignature([signA, signB]);
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Helper which combines key shares of user and backup
|
|
705
|
+
* @param {string} userPublicOrPrivateKeyShare
|
|
706
|
+
* @param {string} backupPrivateOrPublicKeyShare
|
|
707
|
+
* @param {string} walletPassphrase
|
|
708
|
+
* @returns {[ECDSAMethodTypes.KeyCombined, ECDSAMethodTypes.KeyCombined]}
|
|
709
|
+
*/
|
|
710
|
+
getKeyCombinedFromTssKeyShares(userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, walletPassphrase) {
|
|
711
|
+
let backupPrv;
|
|
712
|
+
let userPrv;
|
|
713
|
+
try {
|
|
714
|
+
backupPrv = this.bitgo.decrypt({
|
|
715
|
+
input: backupPrivateOrPublicKeyShare,
|
|
716
|
+
password: walletPassphrase,
|
|
717
|
+
});
|
|
718
|
+
userPrv = this.bitgo.decrypt({
|
|
719
|
+
input: userPublicOrPrivateKeyShare,
|
|
720
|
+
password: walletPassphrase,
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
catch (e) {
|
|
724
|
+
throw new Error(`Error decrypting backup keychain: ${e.message}`);
|
|
725
|
+
}
|
|
726
|
+
const userSigningMaterial = JSON.parse(userPrv);
|
|
727
|
+
const backupSigningMaterial = JSON.parse(backupPrv);
|
|
728
|
+
if (!userSigningMaterial.backupNShare) {
|
|
729
|
+
throw new Error('Invalid user key - missing backupNShare');
|
|
730
|
+
}
|
|
731
|
+
if (!backupSigningMaterial.userNShare) {
|
|
732
|
+
throw new Error('Invalid backup key - missing userNShare');
|
|
733
|
+
}
|
|
734
|
+
const MPC = new sdk_core_1.Ecdsa();
|
|
735
|
+
const userKeyCombined = MPC.keyCombine(userSigningMaterial.pShare, [
|
|
736
|
+
userSigningMaterial.bitgoNShare,
|
|
737
|
+
userSigningMaterial.backupNShare,
|
|
738
|
+
]);
|
|
739
|
+
const backupKeyCombined = MPC.keyCombine(backupSigningMaterial.pShare, [
|
|
740
|
+
backupSigningMaterial.bitgoNShare,
|
|
741
|
+
backupSigningMaterial.userNShare,
|
|
742
|
+
]);
|
|
743
|
+
if (userKeyCombined.xShare.y !== backupKeyCombined.xShare.y ||
|
|
744
|
+
userKeyCombined.xShare.chaincode !== backupKeyCombined.xShare.chaincode) {
|
|
745
|
+
throw new Error('Common keychains do not match');
|
|
746
|
+
}
|
|
747
|
+
return [userKeyCombined, backupKeyCombined];
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Helper which Adds signatures to tx object and re-serializes tx
|
|
751
|
+
* @param {EthLikeCommon.default} ethCommon
|
|
752
|
+
* @param {EthLikeTxLib.FeeMarketEIP1559Transaction | EthLikeTxLib.Transaction} tx
|
|
753
|
+
* @param {ECDSAMethodTypes.Signature} signature
|
|
754
|
+
* @returns {EthLikeTxLib.FeeMarketEIP1559Transaction | EthLikeTxLib.Transaction}
|
|
755
|
+
*/
|
|
756
|
+
getSignedTxFromSignature(ethCommon, tx, signature) {
|
|
757
|
+
// get signed Tx from signature
|
|
758
|
+
const txData = tx.toJSON();
|
|
759
|
+
const yParity = signature.recid;
|
|
760
|
+
const baseParams = {
|
|
761
|
+
to: txData.to,
|
|
762
|
+
nonce: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.nonce), 'hex'),
|
|
763
|
+
value: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.value), 'hex'),
|
|
764
|
+
gasLimit: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.gasLimit), 'hex'),
|
|
765
|
+
data: txData.data,
|
|
766
|
+
r: (0, ethereumjs_util_1.addHexPrefix)(signature.r),
|
|
767
|
+
s: (0, ethereumjs_util_1.addHexPrefix)(signature.s),
|
|
768
|
+
};
|
|
769
|
+
let finalTx;
|
|
770
|
+
if (txData.maxFeePerGas && txData.maxPriorityFeePerGas) {
|
|
771
|
+
finalTx = tx_1.FeeMarketEIP1559Transaction.fromTxData({
|
|
772
|
+
...baseParams,
|
|
773
|
+
maxPriorityFeePerGas: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.maxPriorityFeePerGas), 'hex'),
|
|
774
|
+
maxFeePerGas: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.maxFeePerGas), 'hex'),
|
|
775
|
+
v: new bn_js_1.default(yParity.toString()),
|
|
776
|
+
}, { common: ethCommon });
|
|
777
|
+
}
|
|
778
|
+
else if (txData.gasPrice) {
|
|
779
|
+
const v = BigInt(35) + BigInt(yParity) + BigInt(ethCommon.chainIdBN().toNumber()) * BigInt(2);
|
|
780
|
+
finalTx = tx_1.Transaction.fromTxData({
|
|
781
|
+
...baseParams,
|
|
782
|
+
v: new bn_js_1.default(v.toString()),
|
|
783
|
+
gasPrice: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.gasPrice.toString()), 'hex'),
|
|
784
|
+
}, { common: ethCommon });
|
|
785
|
+
}
|
|
786
|
+
return finalTx;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Builds a funds recovery transaction without BitGo
|
|
790
|
+
* @param params
|
|
791
|
+
* @param {string} params.userKey - [encrypted] xprv
|
|
792
|
+
* @param {string} params.backupKey - [encrypted] xprv or xpub if the xprv is held by a KRS provider
|
|
793
|
+
* @param {string} params.walletPassphrase - used to decrypt userKey and backupKey
|
|
794
|
+
* @param {string} params.walletContractAddress - the ETH address of the wallet contract
|
|
795
|
+
* @param {string} params.krsProvider - necessary if backup key is held by KRS
|
|
796
|
+
* @param {string} params.recoveryDestination - target address to send recovered funds to
|
|
797
|
+
* @param {string} params.bitgoFeeAddress - wrong chain wallet fee address for evm based cross chain recovery txn
|
|
798
|
+
* @param {string} params.bitgoDestinationAddress - target bitgo address where fee will be sent for evm based cross chain recovery txn
|
|
799
|
+
*/
|
|
800
|
+
async recover(params) {
|
|
801
|
+
if (params.isTss) {
|
|
802
|
+
return this.recoverTSS(params);
|
|
803
|
+
}
|
|
804
|
+
return this.recoverEthLike(params);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Builds a funds recovery transaction without BitGo for non-TSS transaction
|
|
808
|
+
* @param params
|
|
809
|
+
* @param {string} params.userKey [encrypted] xprv or xpub
|
|
810
|
+
* @param {string} params.backupKey [encrypted] xprv or xpub if the xprv is held by a KRS provider
|
|
811
|
+
* @param {string} params.walletPassphrase used to decrypt userKey and backupKey
|
|
812
|
+
* @param {string} params.walletContractAddress the EthLike address of the wallet contract
|
|
813
|
+
* @param {string} params.krsProvider necessary if backup key is held by KRS
|
|
814
|
+
* @param {string} params.recoveryDestination target address to send recovered funds to
|
|
815
|
+
* @param {string} params.bitgoFeeAddress wrong chain wallet fee address for evm based cross chain recovery txn
|
|
816
|
+
* @param {string} params.bitgoDestinationAddress target bitgo address where fee will be sent for evm based cross chain recovery txn
|
|
817
|
+
* @returns {Promise<RecoveryInfo | OfflineVaultTxInfo>}
|
|
818
|
+
*/
|
|
819
|
+
async recoverEthLike(params) {
|
|
820
|
+
var _a, _b, _c;
|
|
821
|
+
// bitgoFeeAddress is only defined when it is a evm cross chain recovery
|
|
822
|
+
// as we use fee from this wrong chain address for the recovery txn on the correct chain.
|
|
823
|
+
if (params.bitgoFeeAddress) {
|
|
824
|
+
return this.recoverEthLikeforEvmBasedRecovery(params);
|
|
825
|
+
}
|
|
826
|
+
this.validateRecoveryParams(params);
|
|
827
|
+
const isUnsignedSweep = (0, sdk_core_1.getIsUnsignedSweep)(params);
|
|
828
|
+
// Clean up whitespace from entered values
|
|
829
|
+
let userKey = params.userKey.replace(/\s/g, '');
|
|
830
|
+
const backupKey = params.backupKey.replace(/\s/g, '');
|
|
831
|
+
const gasLimit = new exports.optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit));
|
|
832
|
+
const gasPrice = params.eip1559
|
|
833
|
+
? new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas)
|
|
834
|
+
: new exports.optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice));
|
|
835
|
+
if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) {
|
|
836
|
+
try {
|
|
837
|
+
userKey = this.bitgo.decrypt({
|
|
838
|
+
input: userKey,
|
|
839
|
+
password: params.walletPassphrase,
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
catch (e) {
|
|
843
|
+
throw new Error(`Error decrypting user keychain: ${e.message}`);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
let backupKeyAddress;
|
|
847
|
+
let backupSigningKey;
|
|
848
|
+
if (isUnsignedSweep) {
|
|
849
|
+
const backupHDNode = utxo_lib_1.bip32.fromBase58(backupKey);
|
|
850
|
+
backupSigningKey = backupHDNode.publicKey;
|
|
851
|
+
backupKeyAddress = `0x${exports.optionalDeps.ethUtil.publicToAddress(backupSigningKey, true).toString('hex')}`;
|
|
852
|
+
}
|
|
853
|
+
else {
|
|
854
|
+
// Decrypt backup private key and get address
|
|
855
|
+
let backupPrv;
|
|
856
|
+
try {
|
|
857
|
+
backupPrv = this.bitgo.decrypt({
|
|
858
|
+
input: backupKey,
|
|
859
|
+
password: params.walletPassphrase,
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
catch (e) {
|
|
863
|
+
throw new Error(`Error decrypting backup keychain: ${e.message}`);
|
|
864
|
+
}
|
|
865
|
+
const keyPair = new lib_1.KeyPair({ prv: backupPrv });
|
|
866
|
+
backupSigningKey = keyPair.getKeys().prv;
|
|
867
|
+
if (!backupSigningKey) {
|
|
868
|
+
throw new Error('no private key');
|
|
869
|
+
}
|
|
870
|
+
backupKeyAddress = keyPair.getAddress();
|
|
871
|
+
}
|
|
872
|
+
const backupKeyNonce = await this.getAddressNonce(backupKeyAddress);
|
|
873
|
+
// get balance of backupKey to ensure funds are available to pay fees
|
|
874
|
+
const backupKeyBalance = await this.queryAddressBalance(backupKeyAddress);
|
|
875
|
+
let totalGasNeeded = gasPrice.mul(gasLimit);
|
|
876
|
+
// On optimism chain, L1 fees is to be paid as well apart from L2 fees
|
|
877
|
+
// So we are adding the amount that can be used up as l1 fees
|
|
878
|
+
if (((_a = this.staticsCoin) === null || _a === void 0 ? void 0 : _a.family) === 'opeth') {
|
|
879
|
+
totalGasNeeded = totalGasNeeded.add(new exports.optionalDeps.ethUtil.BN(statics_1.ethGasConfigs.opethGasL1Fees));
|
|
880
|
+
}
|
|
881
|
+
const weiToGwei = 10 ** 9;
|
|
882
|
+
if (backupKeyBalance.lt(totalGasNeeded)) {
|
|
883
|
+
throw new Error(`Backup key address ${backupKeyAddress} has balance ${(backupKeyBalance / weiToGwei).toString()} Gwei.` +
|
|
884
|
+
`This address must have a balance of at least ${(totalGasNeeded / weiToGwei).toString()}` +
|
|
885
|
+
` Gwei to perform recoveries. Try sending some funds to this address then retry.`);
|
|
886
|
+
}
|
|
887
|
+
// get balance of wallet
|
|
888
|
+
const txAmount = await this.queryAddressBalance(params.walletContractAddress);
|
|
889
|
+
if (new bignumber_js_1.BigNumber(txAmount).isLessThanOrEqualTo(0)) {
|
|
890
|
+
throw new Error('Wallet does not have enough funds to recover');
|
|
891
|
+
}
|
|
892
|
+
// build recipients object
|
|
893
|
+
const recipients = [
|
|
894
|
+
{
|
|
895
|
+
address: params.recoveryDestination,
|
|
896
|
+
amount: txAmount.toString(10),
|
|
897
|
+
},
|
|
898
|
+
];
|
|
899
|
+
// Get sequence ID using contract call
|
|
900
|
+
// we need to wait between making two explorer api calls to avoid getting banned
|
|
901
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
902
|
+
const sequenceId = await this.querySequenceId(params.walletContractAddress);
|
|
903
|
+
let operationHash, signature;
|
|
904
|
+
// Get operation hash and sign it
|
|
905
|
+
if (!isUnsignedSweep) {
|
|
906
|
+
operationHash = this.getOperationSha3ForExecuteAndConfirm(recipients, this.getDefaultExpireTime(), sequenceId);
|
|
907
|
+
signature = sdk_core_1.Util.ethSignMsgHash(operationHash, sdk_core_1.Util.xprvToEthPrivateKey(userKey));
|
|
908
|
+
try {
|
|
909
|
+
sdk_core_1.Util.ecRecoverEthAddress(operationHash, signature);
|
|
910
|
+
}
|
|
911
|
+
catch (e) {
|
|
912
|
+
throw new Error('Invalid signature');
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
const txInfo = {
|
|
916
|
+
recipient: recipients[0],
|
|
917
|
+
expireTime: this.getDefaultExpireTime(),
|
|
918
|
+
contractSequenceId: sequenceId,
|
|
919
|
+
operationHash: operationHash,
|
|
920
|
+
signature: signature,
|
|
921
|
+
gasLimit: gasLimit.toString(10),
|
|
922
|
+
};
|
|
923
|
+
const txBuilder = this.getTransactionBuilder();
|
|
924
|
+
txBuilder.counter(backupKeyNonce);
|
|
925
|
+
txBuilder.contract(params.walletContractAddress);
|
|
926
|
+
let txFee;
|
|
927
|
+
if (params.eip1559) {
|
|
928
|
+
txFee = {
|
|
929
|
+
eip1559: {
|
|
930
|
+
maxPriorityFeePerGas: params.eip1559.maxPriorityFeePerGas,
|
|
931
|
+
maxFeePerGas: params.eip1559.maxFeePerGas,
|
|
932
|
+
},
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
txFee = { fee: gasPrice.toString() };
|
|
937
|
+
}
|
|
938
|
+
txBuilder.fee({
|
|
939
|
+
...txFee,
|
|
940
|
+
gasLimit: gasLimit.toString(),
|
|
941
|
+
});
|
|
942
|
+
const transferBuilder = txBuilder.transfer();
|
|
943
|
+
transferBuilder
|
|
944
|
+
.coin((_b = this.staticsCoin) === null || _b === void 0 ? void 0 : _b.name)
|
|
945
|
+
.amount(recipients[0].amount)
|
|
946
|
+
.contractSequenceId(sequenceId)
|
|
947
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
948
|
+
.to(params.recoveryDestination);
|
|
949
|
+
const tx = await txBuilder.build();
|
|
950
|
+
if (isUnsignedSweep) {
|
|
951
|
+
const response = {
|
|
952
|
+
txHex: tx.toBroadcastFormat(),
|
|
953
|
+
userKey,
|
|
954
|
+
backupKey,
|
|
955
|
+
coin: this.getChain(),
|
|
956
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
957
|
+
gasLimit,
|
|
958
|
+
recipients: [txInfo.recipient],
|
|
959
|
+
walletContractAddress: tx.toJson().to,
|
|
960
|
+
amount: txInfo.recipient.amount,
|
|
961
|
+
backupKeyNonce,
|
|
962
|
+
eip1559: params.eip1559,
|
|
963
|
+
};
|
|
964
|
+
lodash_1.default.extend(response, txInfo);
|
|
965
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
966
|
+
return response;
|
|
967
|
+
}
|
|
968
|
+
txBuilder
|
|
969
|
+
.transfer()
|
|
970
|
+
.coin((_c = this.staticsCoin) === null || _c === void 0 ? void 0 : _c.name)
|
|
971
|
+
.key(new lib_1.KeyPair({ prv: userKey }).getKeys().prv);
|
|
972
|
+
txBuilder.sign({ key: backupSigningKey });
|
|
973
|
+
const signedTx = await txBuilder.build();
|
|
974
|
+
return {
|
|
975
|
+
id: signedTx.toJson().id,
|
|
976
|
+
tx: signedTx.toBroadcastFormat(),
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Builds a unsigned (for cold, custody wallet) or
|
|
981
|
+
* half-signed (for hot wallet) evm cross chain recovery transaction with
|
|
982
|
+
* same expected arguments as recover method.
|
|
983
|
+
* This helps recover funds from evm based wrong chain.
|
|
984
|
+
* @param {RecoverOptions} params
|
|
985
|
+
* @returns {Promise<RecoveryInfo | OfflineVaultTxInfo>}
|
|
986
|
+
*/
|
|
987
|
+
async recoverEthLikeforEvmBasedRecovery(params) {
|
|
988
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
989
|
+
this.validateEvmBasedRecoveryParams(params);
|
|
990
|
+
// Clean up whitespace from entered values
|
|
991
|
+
const userKey = params.userKey.replace(/\s/g, '');
|
|
992
|
+
const bitgoFeeAddress = (_a = params.bitgoFeeAddress) === null || _a === void 0 ? void 0 : _a.replace(/\s/g, '').toLowerCase();
|
|
993
|
+
const bitgoDestinationAddress = (_b = params.bitgoDestinationAddress) === null || _b === void 0 ? void 0 : _b.replace(/\s/g, '').toLowerCase();
|
|
994
|
+
const recoveryDestination = (_c = params.recoveryDestination) === null || _c === void 0 ? void 0 : _c.replace(/\s/g, '').toLowerCase();
|
|
995
|
+
const walletContractAddress = (_d = params.walletContractAddress) === null || _d === void 0 ? void 0 : _d.replace(/\s/g, '').toLowerCase();
|
|
996
|
+
const tokenContractAddress = (_e = params.tokenContractAddress) === null || _e === void 0 ? void 0 : _e.replace(/\s/g, '').toLowerCase();
|
|
997
|
+
let userSigningKey;
|
|
998
|
+
let userKeyPrv;
|
|
999
|
+
if (params.walletPassphrase) {
|
|
1000
|
+
if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) {
|
|
1001
|
+
try {
|
|
1002
|
+
userKeyPrv = this.bitgo.decrypt({
|
|
1003
|
+
input: userKey,
|
|
1004
|
+
password: params.walletPassphrase,
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
catch (e) {
|
|
1008
|
+
throw new Error(`Error decrypting user keychain: ${e.message}`);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
const keyPair = new lib_1.KeyPair({ prv: userKeyPrv });
|
|
1012
|
+
userSigningKey = keyPair.getKeys().prv;
|
|
1013
|
+
if (!userSigningKey) {
|
|
1014
|
+
throw new Error('no private key');
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
const gasLimit = new exports.optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit));
|
|
1018
|
+
const gasPrice = params.eip1559
|
|
1019
|
+
? new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas)
|
|
1020
|
+
: new exports.optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice));
|
|
1021
|
+
const bitgoFeeAddressNonce = await this.getAddressNonce(bitgoFeeAddress);
|
|
1022
|
+
// get balance of bitgoFeeAddress to ensure funds are available to pay fees
|
|
1023
|
+
const bitgoFeeAddressBalance = await this.queryAddressBalance(bitgoFeeAddress);
|
|
1024
|
+
const totalGasNeeded = gasPrice.mul(gasLimit);
|
|
1025
|
+
const weiToGwei = 10 ** 9;
|
|
1026
|
+
if (bitgoFeeAddressBalance.lt(totalGasNeeded)) {
|
|
1027
|
+
throw new Error(`Fee address ${bitgoFeeAddress} has balance ${(bitgoFeeAddressBalance / weiToGwei).toString()} Gwei.` +
|
|
1028
|
+
`This address must have a balance of at least ${(totalGasNeeded / weiToGwei).toString()}` +
|
|
1029
|
+
` Gwei to perform recoveries. Try sending some ${this.getChain()} to this address then retry.`);
|
|
1030
|
+
}
|
|
1031
|
+
if (tokenContractAddress) {
|
|
1032
|
+
return this.recoverEthLikeTokenforEvmBasedRecovery(params, bitgoFeeAddressNonce, gasLimit, gasPrice, userKey, userSigningKey);
|
|
1033
|
+
}
|
|
1034
|
+
// get balance of wallet
|
|
1035
|
+
const txAmount = await this.queryAddressBalance(walletContractAddress);
|
|
1036
|
+
const bitgoFeePercentage = 0; // TODO: BG-71912 can change the fee% here.
|
|
1037
|
+
const bitgoFeeAmount = txAmount * (bitgoFeePercentage / 100);
|
|
1038
|
+
// build recipients object
|
|
1039
|
+
const recipients = [
|
|
1040
|
+
{
|
|
1041
|
+
address: recoveryDestination,
|
|
1042
|
+
amount: new bignumber_js_1.BigNumber(txAmount).minus(bitgoFeeAmount).toFixed(),
|
|
1043
|
+
},
|
|
1044
|
+
];
|
|
1045
|
+
if (bitgoFeePercentage > 0) {
|
|
1046
|
+
if (lodash_1.default.isUndefined(bitgoDestinationAddress) || !this.isValidAddress(bitgoDestinationAddress)) {
|
|
1047
|
+
throw new Error('invalid bitgoDestinationAddress');
|
|
1048
|
+
}
|
|
1049
|
+
recipients.push({
|
|
1050
|
+
address: bitgoDestinationAddress,
|
|
1051
|
+
amount: bitgoFeeAmount.toString(10),
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
// calculate batch data
|
|
1055
|
+
const BATCH_METHOD_NAME = 'batch';
|
|
1056
|
+
const BATCH_METHOD_TYPES = ['address[]', 'uint256[]'];
|
|
1057
|
+
const batchExecutionInfo = this.getBatchExecutionInfo(recipients);
|
|
1058
|
+
const batchData = exports.optionalDeps.ethUtil.addHexPrefix(this.getMethodCallData(BATCH_METHOD_NAME, BATCH_METHOD_TYPES, batchExecutionInfo.values).toString('hex'));
|
|
1059
|
+
// Get sequence ID using contract call
|
|
1060
|
+
// we need to wait between making two explorer api calls to avoid getting banned
|
|
1061
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1062
|
+
const sequenceId = await this.querySequenceId(walletContractAddress);
|
|
1063
|
+
const txInfo = {
|
|
1064
|
+
recipients: recipients,
|
|
1065
|
+
expireTime: this.getDefaultExpireTime(),
|
|
1066
|
+
contractSequenceId: sequenceId,
|
|
1067
|
+
gasLimit: gasLimit.toString(10),
|
|
1068
|
+
isEvmBasedCrossChainRecovery: true,
|
|
1069
|
+
};
|
|
1070
|
+
const network = this.getNetwork();
|
|
1071
|
+
const batcherContractAddress = network === null || network === void 0 ? void 0 : network.batcherContractAddress;
|
|
1072
|
+
const txBuilder = this.getTransactionBuilder();
|
|
1073
|
+
txBuilder.counter(bitgoFeeAddressNonce);
|
|
1074
|
+
txBuilder.contract(walletContractAddress);
|
|
1075
|
+
let txFee;
|
|
1076
|
+
if (params.eip1559) {
|
|
1077
|
+
txFee = {
|
|
1078
|
+
eip1559: {
|
|
1079
|
+
maxPriorityFeePerGas: params.eip1559.maxPriorityFeePerGas,
|
|
1080
|
+
maxFeePerGas: params.eip1559.maxFeePerGas,
|
|
1081
|
+
},
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
txFee = { fee: gasPrice.toString() };
|
|
1086
|
+
}
|
|
1087
|
+
txBuilder.fee({
|
|
1088
|
+
...txFee,
|
|
1089
|
+
gasLimit: gasLimit.toString(),
|
|
1090
|
+
});
|
|
1091
|
+
const transferBuilder = txBuilder.transfer();
|
|
1092
|
+
if (!batcherContractAddress) {
|
|
1093
|
+
transferBuilder
|
|
1094
|
+
.coin((_f = this.staticsCoin) === null || _f === void 0 ? void 0 : _f.name)
|
|
1095
|
+
.amount(batchExecutionInfo.totalAmount)
|
|
1096
|
+
.contractSequenceId(sequenceId)
|
|
1097
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
1098
|
+
.to(recoveryDestination);
|
|
1099
|
+
}
|
|
1100
|
+
else {
|
|
1101
|
+
transferBuilder
|
|
1102
|
+
.coin((_g = this.staticsCoin) === null || _g === void 0 ? void 0 : _g.name)
|
|
1103
|
+
.amount(batchExecutionInfo.totalAmount)
|
|
1104
|
+
.contractSequenceId(sequenceId)
|
|
1105
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
1106
|
+
.to(batcherContractAddress)
|
|
1107
|
+
.data(batchData);
|
|
1108
|
+
}
|
|
1109
|
+
if (params.walletPassphrase) {
|
|
1110
|
+
transferBuilder.key(userSigningKey);
|
|
1111
|
+
}
|
|
1112
|
+
// If the intended chain is arbitrum or optimism, we need to use wallet version 4
|
|
1113
|
+
// since these contracts construct operationHash differently
|
|
1114
|
+
if (params.intendedChain && ['arbeth', 'opeth'].includes(statics_1.coins.get(params.intendedChain).family)) {
|
|
1115
|
+
txBuilder.walletVersion(4);
|
|
1116
|
+
}
|
|
1117
|
+
const tx = await txBuilder.build();
|
|
1118
|
+
const response = {
|
|
1119
|
+
txHex: tx.toBroadcastFormat(),
|
|
1120
|
+
userKey,
|
|
1121
|
+
coin: this.getChain(),
|
|
1122
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1123
|
+
gasLimit,
|
|
1124
|
+
recipients: txInfo.recipients,
|
|
1125
|
+
walletContractAddress: tx.toJson().to,
|
|
1126
|
+
amount: batchExecutionInfo.totalAmount,
|
|
1127
|
+
backupKeyNonce: bitgoFeeAddressNonce,
|
|
1128
|
+
eip1559: params.eip1559,
|
|
1129
|
+
};
|
|
1130
|
+
lodash_1.default.extend(response, txInfo);
|
|
1131
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
1132
|
+
if (params.walletPassphrase) {
|
|
1133
|
+
const halfSignedTxn = {
|
|
1134
|
+
halfSigned: {
|
|
1135
|
+
txHex: tx.toBroadcastFormat(),
|
|
1136
|
+
recipients: txInfo.recipients,
|
|
1137
|
+
expireTime: txInfo.expireTime,
|
|
1138
|
+
},
|
|
1139
|
+
};
|
|
1140
|
+
lodash_1.default.extend(response, halfSignedTxn);
|
|
1141
|
+
const feesUsed = {
|
|
1142
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1143
|
+
gasLimit: exports.optionalDeps.ethUtil.bufferToInt(gasLimit).toFixed(),
|
|
1144
|
+
};
|
|
1145
|
+
response['feesUsed'] = feesUsed;
|
|
1146
|
+
}
|
|
1147
|
+
return response;
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* Query explorer for the balance of an address for a token
|
|
1151
|
+
* @param {string} tokenContractAddress - address where the token smart contract is hosted
|
|
1152
|
+
* @param {string} walletContractAddress - address of the wallet
|
|
1153
|
+
* @returns {BigNumber} token balaance in base units
|
|
1154
|
+
*/
|
|
1155
|
+
async queryAddressTokenBalance(tokenContractAddress, walletContractAddress) {
|
|
1156
|
+
if (!exports.optionalDeps.ethUtil.isValidAddress(tokenContractAddress)) {
|
|
1157
|
+
throw new Error('cannot get balance for invalid token address');
|
|
1158
|
+
}
|
|
1159
|
+
if (!exports.optionalDeps.ethUtil.isValidAddress(walletContractAddress)) {
|
|
1160
|
+
throw new Error('cannot get token balance for invalid wallet address');
|
|
1161
|
+
}
|
|
1162
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
1163
|
+
module: 'account',
|
|
1164
|
+
action: 'tokenbalance',
|
|
1165
|
+
contractaddress: tokenContractAddress,
|
|
1166
|
+
address: walletContractAddress,
|
|
1167
|
+
tag: 'latest',
|
|
1168
|
+
});
|
|
1169
|
+
// throw if the result does not exist or the result is not a valid number
|
|
1170
|
+
if (!result || !result.result || isNaN(result.result)) {
|
|
1171
|
+
throw new Error(`Could not obtain token address balance for ${tokenContractAddress} from Etherscan, got: ${result.result}`);
|
|
1172
|
+
}
|
|
1173
|
+
return new exports.optionalDeps.ethUtil.BN(result.result, 10);
|
|
1174
|
+
}
|
|
1175
|
+
async recoverEthLikeTokenforEvmBasedRecovery(params, bitgoFeeAddressNonce, gasLimit, gasPrice, userKey, userSigningKey) {
|
|
1176
|
+
var _a, _b;
|
|
1177
|
+
// get token balance of wallet
|
|
1178
|
+
const txAmount = await this.queryAddressTokenBalance(params.tokenContractAddress, params.walletContractAddress);
|
|
1179
|
+
// build recipients object
|
|
1180
|
+
const recipients = [
|
|
1181
|
+
{
|
|
1182
|
+
address: params.recoveryDestination,
|
|
1183
|
+
amount: new bignumber_js_1.BigNumber(txAmount).toFixed(),
|
|
1184
|
+
},
|
|
1185
|
+
];
|
|
1186
|
+
// Get sequence ID using contract call
|
|
1187
|
+
// we need to wait between making two explorer api calls to avoid getting banned
|
|
1188
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1189
|
+
const sequenceId = await this.querySequenceId(params.walletContractAddress);
|
|
1190
|
+
const txInfo = {
|
|
1191
|
+
recipients: recipients,
|
|
1192
|
+
expireTime: this.getDefaultExpireTime(),
|
|
1193
|
+
contractSequenceId: sequenceId,
|
|
1194
|
+
gasLimit: gasLimit.toString(10),
|
|
1195
|
+
isEvmBasedCrossChainRecovery: true,
|
|
1196
|
+
};
|
|
1197
|
+
const txBuilder = this.getTransactionBuilder();
|
|
1198
|
+
txBuilder.counter(bitgoFeeAddressNonce);
|
|
1199
|
+
txBuilder.contract(params.walletContractAddress);
|
|
1200
|
+
let txFee;
|
|
1201
|
+
if (params.eip1559) {
|
|
1202
|
+
txFee = {
|
|
1203
|
+
eip1559: {
|
|
1204
|
+
maxPriorityFeePerGas: params.eip1559.maxPriorityFeePerGas,
|
|
1205
|
+
maxFeePerGas: params.eip1559.maxFeePerGas,
|
|
1206
|
+
},
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
else {
|
|
1210
|
+
txFee = { fee: gasPrice.toString() };
|
|
1211
|
+
}
|
|
1212
|
+
txBuilder.fee({
|
|
1213
|
+
...txFee,
|
|
1214
|
+
gasLimit: gasLimit.toString(),
|
|
1215
|
+
});
|
|
1216
|
+
const transferBuilder = txBuilder.transfer();
|
|
1217
|
+
const network = this.getNetwork();
|
|
1218
|
+
const token = (_b = (0, lib_1.getToken)(params.tokenContractAddress, network, (_a = this.staticsCoin) === null || _a === void 0 ? void 0 : _a.family)) === null || _b === void 0 ? void 0 : _b.name;
|
|
1219
|
+
transferBuilder
|
|
1220
|
+
.coin(token)
|
|
1221
|
+
.amount(txAmount)
|
|
1222
|
+
.contractSequenceId(sequenceId)
|
|
1223
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
1224
|
+
.to(params.recoveryDestination)
|
|
1225
|
+
.coin(token);
|
|
1226
|
+
if (params.walletPassphrase) {
|
|
1227
|
+
txBuilder.transfer().key(userSigningKey);
|
|
1228
|
+
}
|
|
1229
|
+
// If the intended chain is arbitrum or optimism, we need to use wallet version 4
|
|
1230
|
+
// since these contracts construct operationHash differently
|
|
1231
|
+
if (params.intendedChain && ['arbeth', 'opeth'].includes(statics_1.coins.get(params.intendedChain).family)) {
|
|
1232
|
+
txBuilder.walletVersion(4);
|
|
1233
|
+
}
|
|
1234
|
+
const tx = await txBuilder.build();
|
|
1235
|
+
const response = {
|
|
1236
|
+
txHex: tx.toBroadcastFormat(),
|
|
1237
|
+
userKey,
|
|
1238
|
+
coin: token,
|
|
1239
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1240
|
+
gasLimit,
|
|
1241
|
+
recipients: txInfo.recipients,
|
|
1242
|
+
walletContractAddress: tx.toJson().to,
|
|
1243
|
+
amount: txAmount.toString(),
|
|
1244
|
+
backupKeyNonce: bitgoFeeAddressNonce,
|
|
1245
|
+
eip1559: params.eip1559,
|
|
1246
|
+
};
|
|
1247
|
+
lodash_1.default.extend(response, txInfo);
|
|
1248
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
1249
|
+
if (params.walletPassphrase) {
|
|
1250
|
+
const halfSignedTxn = {
|
|
1251
|
+
halfSigned: {
|
|
1252
|
+
txHex: tx.toBroadcastFormat(),
|
|
1253
|
+
recipients: txInfo.recipients,
|
|
1254
|
+
expireTime: txInfo.expireTime,
|
|
1255
|
+
},
|
|
1256
|
+
};
|
|
1257
|
+
lodash_1.default.extend(response, halfSignedTxn);
|
|
1258
|
+
const feesUsed = {
|
|
1259
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1260
|
+
gasLimit: exports.optionalDeps.ethUtil.bufferToInt(gasLimit).toFixed(),
|
|
1261
|
+
};
|
|
1262
|
+
response['feesUsed'] = feesUsed;
|
|
1263
|
+
}
|
|
1264
|
+
return response;
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Validate evm based cross chain recovery params
|
|
1268
|
+
* @param params {RecoverOptions}
|
|
1269
|
+
* @returns {void}
|
|
1270
|
+
*/
|
|
1271
|
+
validateEvmBasedRecoveryParams(params) {
|
|
1272
|
+
if (lodash_1.default.isUndefined(params.bitgoFeeAddress) || !this.isValidAddress(params.bitgoFeeAddress)) {
|
|
1273
|
+
throw new Error('invalid bitgoFeeAddress');
|
|
1274
|
+
}
|
|
1275
|
+
if (lodash_1.default.isUndefined(params.walletContractAddress) || !this.isValidAddress(params.walletContractAddress)) {
|
|
1276
|
+
throw new Error('invalid walletContractAddress');
|
|
1277
|
+
}
|
|
1278
|
+
if (lodash_1.default.isUndefined(params.recoveryDestination) || !this.isValidAddress(params.recoveryDestination)) {
|
|
1279
|
+
throw new Error('invalid recoveryDestination');
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Return types, values, and total amount in wei to send in a batch transaction, using the method signature
|
|
1284
|
+
* `distributeBatch(address[], uint256[])`
|
|
1285
|
+
* @param {Recipient[]} recipients - transaction recipients
|
|
1286
|
+
* @returns {GetBatchExecutionInfoRT} information needed to execute the batch transaction
|
|
1287
|
+
*/
|
|
1288
|
+
getBatchExecutionInfo(recipients) {
|
|
1289
|
+
const addresses = [];
|
|
1290
|
+
const amounts = [];
|
|
1291
|
+
let sum = new bignumber_js_1.BigNumber('0');
|
|
1292
|
+
lodash_1.default.forEach(recipients, ({ address, amount }) => {
|
|
1293
|
+
addresses.push(address);
|
|
1294
|
+
amounts.push(amount);
|
|
1295
|
+
sum = sum.plus(amount);
|
|
1296
|
+
});
|
|
1297
|
+
return {
|
|
1298
|
+
values: [addresses, amounts],
|
|
1299
|
+
totalAmount: sum.toFixed(),
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Build arguments to call the send method on the wallet contract
|
|
1304
|
+
* @param txInfo
|
|
1305
|
+
*/
|
|
1306
|
+
getSendMethodArgs(txInfo) {
|
|
1307
|
+
// Method signature is
|
|
1308
|
+
// sendMultiSig(address toAddress, uint value, bytes data, uint expireTime, uint sequenceId, bytes signature)
|
|
1309
|
+
return [
|
|
1310
|
+
{
|
|
1311
|
+
name: 'toAddress',
|
|
1312
|
+
type: 'address',
|
|
1313
|
+
value: txInfo.recipient.address,
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
name: 'value',
|
|
1317
|
+
type: 'uint',
|
|
1318
|
+
value: txInfo.recipient.amount,
|
|
1319
|
+
},
|
|
1320
|
+
{
|
|
1321
|
+
name: 'data',
|
|
1322
|
+
type: 'bytes',
|
|
1323
|
+
value: exports.optionalDeps.ethUtil.toBuffer(exports.optionalDeps.ethUtil.addHexPrefix(txInfo.recipient.data || '')),
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
name: 'expireTime',
|
|
1327
|
+
type: 'uint',
|
|
1328
|
+
value: txInfo.expireTime,
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
name: 'sequenceId',
|
|
1332
|
+
type: 'uint',
|
|
1333
|
+
value: txInfo.contractSequenceId,
|
|
1334
|
+
},
|
|
1335
|
+
{
|
|
1336
|
+
name: 'signature',
|
|
1337
|
+
type: 'bytes',
|
|
1338
|
+
value: exports.optionalDeps.ethUtil.toBuffer(exports.optionalDeps.ethUtil.addHexPrefix(txInfo.signature)),
|
|
1339
|
+
},
|
|
1340
|
+
];
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Recovers a tx with TSS key shares
|
|
1344
|
+
* same expected arguments as recover method, but with TSS key shares
|
|
1345
|
+
*/
|
|
1346
|
+
async recoverTSS(params) {
|
|
1347
|
+
this.validateRecoveryParams(params);
|
|
1348
|
+
// Clean up whitespace from entered values
|
|
1349
|
+
const userPublicOrPrivateKeyShare = params.userKey.replace(/\s/g, '');
|
|
1350
|
+
const backupPrivateOrPublicKeyShare = params.backupKey.replace(/\s/g, '');
|
|
1351
|
+
const gasLimit = new exports.optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit));
|
|
1352
|
+
const gasPrice = params.eip1559
|
|
1353
|
+
? new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas)
|
|
1354
|
+
: new exports.optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice));
|
|
1355
|
+
if ((0, sdk_core_1.getIsUnsignedSweep)(params)) {
|
|
1356
|
+
const backupKeyPair = new lib_1.KeyPair({ pub: backupPrivateOrPublicKeyShare });
|
|
1357
|
+
const baseAddress = backupKeyPair.getAddress();
|
|
1358
|
+
const { txInfo, tx, nonce } = await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params);
|
|
1359
|
+
return this.formatForOfflineVaultTSS(txInfo, tx, userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, gasPrice, gasLimit, nonce, params.eip1559, params.replayProtectionOptions);
|
|
1360
|
+
}
|
|
1361
|
+
else {
|
|
1362
|
+
const isGG18SigningMaterial = this.isGG18SigningMaterial(userPublicOrPrivateKeyShare, params.walletPassphrase);
|
|
1363
|
+
let signature;
|
|
1364
|
+
let unsignedTx;
|
|
1365
|
+
if (isGG18SigningMaterial) {
|
|
1366
|
+
const [userKeyCombined, backupKeyCombined] = this.getKeyCombinedFromTssKeyShares(userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, params.walletPassphrase);
|
|
1367
|
+
const backupKeyPair = new lib_1.KeyPair({ pub: backupKeyCombined.xShare.y });
|
|
1368
|
+
const baseAddress = backupKeyPair.getAddress();
|
|
1369
|
+
unsignedTx = (await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params)).tx;
|
|
1370
|
+
signature = await this.signRecoveryTSS(userKeyCombined, backupKeyCombined, unsignedTx.getMessageToSign(false).toString('hex'));
|
|
1371
|
+
}
|
|
1372
|
+
else {
|
|
1373
|
+
const { userKeyShare, backupKeyShare, commonKeyChain, baseAddress } = await this.getMpcV2RecoveryKeyShares(userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, params.walletPassphrase);
|
|
1374
|
+
unsignedTx = (await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params)).tx;
|
|
1375
|
+
signature = await AbstractEthLikeNewCoins.signRecoveryMpcV2(unsignedTx, userKeyShare, backupKeyShare, commonKeyChain);
|
|
1376
|
+
}
|
|
1377
|
+
const ethCommmon = AbstractEthLikeNewCoins.getEthLikeCommon(params.eip1559, params.replayProtectionOptions);
|
|
1378
|
+
const signedTx = this.getSignedTxFromSignature(ethCommmon, unsignedTx, signature);
|
|
1379
|
+
return {
|
|
1380
|
+
id: (0, ethereumjs_util_1.addHexPrefix)(signedTx.hash().toString('hex')),
|
|
1381
|
+
tx: (0, ethereumjs_util_1.addHexPrefix)(signedTx.serialize().toString('hex')),
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
static async signRecoveryMpcV2(tx, userKeyShare, backupKeyShare, commonKeyChain) {
|
|
1386
|
+
const messageHash = tx.getMessageToSign(true);
|
|
1387
|
+
const userDsg = new sdk_lib_mpc_1.DklsDsg.Dsg(userKeyShare, 0, 'm', messageHash);
|
|
1388
|
+
const backupDsg = new sdk_lib_mpc_1.DklsDsg.Dsg(backupKeyShare, 1, 'm', messageHash);
|
|
1389
|
+
const signatureString = sdk_lib_mpc_1.DklsUtils.verifyAndConvertDklsSignature(messageHash, (await sdk_lib_mpc_1.DklsUtils.executeTillRound(5, userDsg, backupDsg)), commonKeyChain, 'm', undefined, false);
|
|
1390
|
+
const sigParts = signatureString.split(':');
|
|
1391
|
+
return {
|
|
1392
|
+
recid: parseInt(sigParts[0], 10),
|
|
1393
|
+
r: sigParts[1],
|
|
1394
|
+
s: sigParts[2],
|
|
1395
|
+
y: sigParts[3],
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
async getMpcV2RecoveryKeyShares(userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, walletPassphrase) {
|
|
1399
|
+
const userCompressedPrv = Buffer.from(this.bitgo.decrypt({
|
|
1400
|
+
input: userPublicOrPrivateKeyShare,
|
|
1401
|
+
password: walletPassphrase,
|
|
1402
|
+
}), 'base64');
|
|
1403
|
+
const bakcupCompressedPrv = Buffer.from(this.bitgo.decrypt({
|
|
1404
|
+
input: backupPrivateOrPublicKeyShare,
|
|
1405
|
+
password: walletPassphrase,
|
|
1406
|
+
}), 'base64');
|
|
1407
|
+
const userPrvJSON = sdk_lib_mpc_1.DklsTypes.getDecodedReducedKeyShare(userCompressedPrv);
|
|
1408
|
+
const backupPrvJSON = sdk_lib_mpc_1.DklsTypes.getDecodedReducedKeyShare(bakcupCompressedPrv);
|
|
1409
|
+
const userKeyRetrofit = {
|
|
1410
|
+
xShare: {
|
|
1411
|
+
x: Buffer.from(userPrvJSON.prv).toString('hex'),
|
|
1412
|
+
y: Buffer.from(userPrvJSON.pub).toString('hex'),
|
|
1413
|
+
chaincode: Buffer.from(userPrvJSON.rootChainCode).toString('hex'),
|
|
1414
|
+
},
|
|
1415
|
+
bigSiList: [Buffer.from(userPrvJSON.bigSList[1]).toString('hex')],
|
|
1416
|
+
xiList: userPrvJSON.xList.slice(0, 2),
|
|
1417
|
+
};
|
|
1418
|
+
const backupKeyRetrofit = {
|
|
1419
|
+
xShare: {
|
|
1420
|
+
x: Buffer.from(backupPrvJSON.prv).toString('hex'),
|
|
1421
|
+
y: Buffer.from(backupPrvJSON.pub).toString('hex'),
|
|
1422
|
+
chaincode: Buffer.from(backupPrvJSON.rootChainCode).toString('hex'),
|
|
1423
|
+
},
|
|
1424
|
+
bigSiList: [Buffer.from(backupPrvJSON.bigSList[0]).toString('hex')],
|
|
1425
|
+
xiList: backupPrvJSON.xList.slice(0, 2),
|
|
1426
|
+
};
|
|
1427
|
+
const [user, backup] = await sdk_lib_mpc_1.DklsUtils.generate2of2KeyShares(userKeyRetrofit, backupKeyRetrofit);
|
|
1428
|
+
const userKeyShare = user.getKeyShare();
|
|
1429
|
+
const backupKeyShare = backup.getKeyShare();
|
|
1430
|
+
const commonKeyChain = sdk_lib_mpc_1.DklsTypes.getCommonKeychain(userKeyShare).slice(0, 66);
|
|
1431
|
+
const backupKeyPair = new lib_1.KeyPair({ pub: commonKeyChain });
|
|
1432
|
+
const baseAddress = backupKeyPair.getAddress();
|
|
1433
|
+
return { userKeyShare, backupKeyShare, commonKeyChain, baseAddress };
|
|
1434
|
+
}
|
|
1435
|
+
isGG18SigningMaterial(keyShare, walletPassphrase) {
|
|
1436
|
+
const prv = this.bitgo.decrypt({
|
|
1437
|
+
input: keyShare,
|
|
1438
|
+
password: walletPassphrase,
|
|
1439
|
+
});
|
|
1440
|
+
try {
|
|
1441
|
+
const signingMaterial = JSON.parse(prv);
|
|
1442
|
+
return (signingMaterial.pShare &&
|
|
1443
|
+
signingMaterial.bitgoNShare &&
|
|
1444
|
+
(signingMaterial.userNShare || signingMaterial.backupNShare));
|
|
1445
|
+
}
|
|
1446
|
+
catch (error) {
|
|
1447
|
+
return false;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
async buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params) {
|
|
1451
|
+
const nonce = await this.getAddressNonce(baseAddress);
|
|
1452
|
+
const txAmount = await this.validateBalanceAndGetTxAmount(baseAddress, gasPrice, gasLimit);
|
|
1453
|
+
const recipients = [
|
|
1454
|
+
{
|
|
1455
|
+
address: params.recoveryDestination,
|
|
1456
|
+
amount: txAmount.toString(10),
|
|
1457
|
+
},
|
|
1458
|
+
];
|
|
1459
|
+
const txInfo = {
|
|
1460
|
+
recipient: recipients[0],
|
|
1461
|
+
expireTime: this.getDefaultExpireTime(),
|
|
1462
|
+
gasLimit: gasLimit.toString(10),
|
|
1463
|
+
};
|
|
1464
|
+
const txParams = {
|
|
1465
|
+
to: params.recoveryDestination,
|
|
1466
|
+
nonce: nonce,
|
|
1467
|
+
value: txAmount,
|
|
1468
|
+
gasPrice: gasPrice,
|
|
1469
|
+
gasLimit: gasLimit,
|
|
1470
|
+
data: Buffer.from('0x'),
|
|
1471
|
+
eip1559: params.eip1559,
|
|
1472
|
+
replayProtectionOptions: params.replayProtectionOptions,
|
|
1473
|
+
};
|
|
1474
|
+
const tx = AbstractEthLikeNewCoins.buildTransaction(txParams);
|
|
1475
|
+
return { txInfo, tx, nonce };
|
|
1476
|
+
}
|
|
1477
|
+
async validateBalanceAndGetTxAmount(baseAddress, gasPrice, gasLimit) {
|
|
1478
|
+
const baseAddressBalance = await this.queryAddressBalance(baseAddress);
|
|
1479
|
+
const totalGasNeeded = gasPrice.mul(gasLimit);
|
|
1480
|
+
const weiToGwei = new bn_js_1.default(10 ** 9);
|
|
1481
|
+
if (baseAddressBalance.lt(totalGasNeeded)) {
|
|
1482
|
+
throw new Error(`Backup key address ${baseAddress} has balance ${baseAddressBalance.div(weiToGwei).toString()} Gwei.` +
|
|
1483
|
+
`This address must have a balance of at least ${totalGasNeeded.div(weiToGwei).toString()}` +
|
|
1484
|
+
` Gwei to perform recoveries. Try sending some ETH to this address then retry.`);
|
|
1485
|
+
}
|
|
1486
|
+
const txAmount = baseAddressBalance.sub(totalGasNeeded);
|
|
1487
|
+
return txAmount;
|
|
1488
|
+
}
|
|
1489
|
+
async recoveryBlockchainExplorerQuery(query) {
|
|
1490
|
+
throw new Error('method not implemented');
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Creates the extra parameters needed to build a hop transaction
|
|
1494
|
+
* @param buildParams The original build parameters
|
|
1495
|
+
* @returns extra parameters object to merge with the original build parameters object and send to the platform
|
|
1496
|
+
*/
|
|
1497
|
+
async createHopTransactionParams(buildParams) {
|
|
1498
|
+
const wallet = buildParams.wallet;
|
|
1499
|
+
const recipients = buildParams.recipients;
|
|
1500
|
+
const walletPassphrase = buildParams.walletPassphrase;
|
|
1501
|
+
const userKeychain = await this.keychains().get({ id: wallet.keyIds()[0] });
|
|
1502
|
+
const userPrv = wallet.getUserPrv({ keychain: userKeychain, walletPassphrase });
|
|
1503
|
+
const userPrvBuffer = utxo_lib_1.bip32.fromBase58(userPrv).privateKey;
|
|
1504
|
+
if (!userPrvBuffer) {
|
|
1505
|
+
throw new Error('invalid userPrv');
|
|
1506
|
+
}
|
|
1507
|
+
if (!recipients || !Array.isArray(recipients)) {
|
|
1508
|
+
throw new Error('expecting array of recipients');
|
|
1509
|
+
}
|
|
1510
|
+
// Right now we only support 1 recipient
|
|
1511
|
+
if (recipients.length !== 1) {
|
|
1512
|
+
throw new Error('must send to exactly 1 recipient');
|
|
1513
|
+
}
|
|
1514
|
+
const recipientAddress = recipients[0].address;
|
|
1515
|
+
const recipientAmount = recipients[0].amount;
|
|
1516
|
+
const feeEstimateParams = {
|
|
1517
|
+
recipient: recipientAddress,
|
|
1518
|
+
amount: recipientAmount,
|
|
1519
|
+
hop: true,
|
|
1520
|
+
};
|
|
1521
|
+
const feeEstimate = await this.feeEstimate(feeEstimateParams);
|
|
1522
|
+
const gasLimit = feeEstimate.gasLimitEstimate;
|
|
1523
|
+
const gasPrice = Math.round(feeEstimate.feeEstimate / gasLimit);
|
|
1524
|
+
const gasPriceMax = gasPrice * 5;
|
|
1525
|
+
// Payment id a random number so its different for every tx
|
|
1526
|
+
const paymentId = Math.floor(Math.random() * 10000000000).toString();
|
|
1527
|
+
const hopDigest = AbstractEthLikeNewCoins.getHopDigest([
|
|
1528
|
+
recipientAddress,
|
|
1529
|
+
recipientAmount,
|
|
1530
|
+
gasPriceMax.toString(),
|
|
1531
|
+
gasLimit.toString(),
|
|
1532
|
+
paymentId,
|
|
1533
|
+
]);
|
|
1534
|
+
const userReqSig = exports.optionalDeps.ethUtil.addHexPrefix(Buffer.from(secp256k1_1.default.ecdsaSign(hopDigest, userPrvBuffer).signature).toString('hex'));
|
|
1535
|
+
return {
|
|
1536
|
+
hopParams: {
|
|
1537
|
+
gasPriceMax,
|
|
1538
|
+
userReqSig,
|
|
1539
|
+
paymentId,
|
|
1540
|
+
},
|
|
1541
|
+
gasLimit,
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Validates that the hop prebuild from the HSM is valid and correct
|
|
1546
|
+
* @param {IWallet} wallet - The wallet that the prebuild is for
|
|
1547
|
+
* @param {HopPrebuild} hopPrebuild - The prebuild to validate
|
|
1548
|
+
* @param {Object} originalParams - The original parameters passed to prebuildTransaction
|
|
1549
|
+
* @param {Recipient[]} originalParams.recipients - The original recipients array
|
|
1550
|
+
* @returns {void}
|
|
1551
|
+
* @throws Error if The prebuild is invalid
|
|
1552
|
+
*/
|
|
1553
|
+
async validateHopPrebuild(wallet, hopPrebuild, originalParams) {
|
|
1554
|
+
const { tx, id, signature } = hopPrebuild;
|
|
1555
|
+
// first, validate the HSM signature
|
|
1556
|
+
const serverXpub = sdk_core_1.common.Environments[this.bitgo.getEnv()].hsmXpub;
|
|
1557
|
+
const serverPubkeyBuffer = utxo_lib_1.bip32.fromBase58(serverXpub).publicKey;
|
|
1558
|
+
const signatureBuffer = Buffer.from(exports.optionalDeps.ethUtil.stripHexPrefix(signature), 'hex');
|
|
1559
|
+
const messageBuffer = Buffer.from(exports.optionalDeps.ethUtil.padToEven(exports.optionalDeps.ethUtil.stripHexPrefix(id)), 'hex');
|
|
1560
|
+
const sig = new Uint8Array(signatureBuffer.slice(1));
|
|
1561
|
+
const isValidSignature = secp256k1_1.default.ecdsaVerify(sig, messageBuffer, serverPubkeyBuffer);
|
|
1562
|
+
if (!isValidSignature) {
|
|
1563
|
+
throw new Error(`Hop txid signature invalid - pub: ${serverXpub}, msg: ${messageBuffer === null || messageBuffer === void 0 ? void 0 : messageBuffer.toString()}, sig: ${signatureBuffer === null || signatureBuffer === void 0 ? void 0 : signatureBuffer.toString()}`);
|
|
1564
|
+
}
|
|
1565
|
+
const builtHopTx = exports.optionalDeps.EthTx.TransactionFactory.fromSerializedData(exports.optionalDeps.ethUtil.toBuffer(tx));
|
|
1566
|
+
// If original params are given, we can check them against the transaction prebuild params
|
|
1567
|
+
if (!lodash_1.default.isNil(originalParams)) {
|
|
1568
|
+
const { recipients } = originalParams;
|
|
1569
|
+
// Then validate that the tx params actually equal the requested params
|
|
1570
|
+
const originalAmount = new bignumber_js_1.BigNumber(recipients[0].amount);
|
|
1571
|
+
const originalDestination = recipients[0].address;
|
|
1572
|
+
const hopAmount = new bignumber_js_1.BigNumber(exports.optionalDeps.ethUtil.bufferToHex(builtHopTx.value));
|
|
1573
|
+
if (!builtHopTx.to) {
|
|
1574
|
+
throw new Error(`Transaction does not have a destination address`);
|
|
1575
|
+
}
|
|
1576
|
+
const hopDestination = builtHopTx.to.toString();
|
|
1577
|
+
if (!hopAmount.eq(originalAmount)) {
|
|
1578
|
+
throw new Error(`Hop amount: ${hopAmount} does not equal original amount: ${originalAmount}`);
|
|
1579
|
+
}
|
|
1580
|
+
if (hopDestination.toLowerCase() !== originalDestination.toLowerCase()) {
|
|
1581
|
+
throw new Error(`Hop destination: ${hopDestination} does not equal original recipient: ${hopDestination}`);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
if (!builtHopTx.verifySignature()) {
|
|
1585
|
+
// We dont want to continue at all in this case, at risk of ETH being stuck on the hop address
|
|
1586
|
+
throw new Error(`Invalid hop transaction signature, txid: ${id}`);
|
|
1587
|
+
}
|
|
1588
|
+
if (exports.optionalDeps.ethUtil.addHexPrefix(builtHopTx.hash().toString('hex')) !== id) {
|
|
1589
|
+
throw new Error(`Signed hop txid does not equal actual txid`);
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Gets the hop digest for the user to sign. This is validated in the HSM to prove that the user requested this tx
|
|
1594
|
+
* @param {string[]} paramsArr - The parameters to hash together for the digest
|
|
1595
|
+
* @returns {Buffer}
|
|
1596
|
+
*/
|
|
1597
|
+
static getHopDigest(paramsArr) {
|
|
1598
|
+
const hash = (0, keccak_1.default)('keccak256');
|
|
1599
|
+
hash.update([AbstractEthLikeNewCoins.hopTransactionSalt, ...paramsArr].join('$'));
|
|
1600
|
+
return hash.digest();
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Modify prebuild before sending it to the server. Add things like hop transaction params
|
|
1604
|
+
* @param {BuildOptions} buildParams - The whitelisted parameters for this prebuild
|
|
1605
|
+
* @param {boolean} buildParams.hop - True if this should prebuild a hop tx, else false
|
|
1606
|
+
* @param {Recipient[]} buildParams.recipients - The recipients array of this transaction
|
|
1607
|
+
* @param {Wallet} buildParams.wallet - The wallet sending this tx
|
|
1608
|
+
* @param {string} buildParams.walletPassphrase - the passphrase for this wallet
|
|
1609
|
+
* @returns {Promise<BuildOptions>}
|
|
1610
|
+
*/
|
|
1611
|
+
async getExtraPrebuildParams(buildParams) {
|
|
1612
|
+
if (!lodash_1.default.isUndefined(buildParams.hop) &&
|
|
1613
|
+
buildParams.hop &&
|
|
1614
|
+
!lodash_1.default.isUndefined(buildParams.wallet) &&
|
|
1615
|
+
!lodash_1.default.isUndefined(buildParams.recipients) &&
|
|
1616
|
+
!lodash_1.default.isUndefined(buildParams.walletPassphrase)) {
|
|
1617
|
+
if (this instanceof ethLikeToken_1.EthLikeToken) {
|
|
1618
|
+
throw new Error(`Hop transactions are not enabled for ERC-20 tokens, nor are they necessary. Please remove the 'hop' parameter and try again.`);
|
|
1619
|
+
}
|
|
1620
|
+
return (await this.createHopTransactionParams({
|
|
1621
|
+
wallet: buildParams.wallet,
|
|
1622
|
+
recipients: buildParams.recipients,
|
|
1623
|
+
walletPassphrase: buildParams.walletPassphrase,
|
|
1624
|
+
}));
|
|
1625
|
+
}
|
|
1626
|
+
return {};
|
|
1627
|
+
}
|
|
1628
|
+
/**
|
|
1629
|
+
* Modify prebuild after receiving it from the server. Add things like nlocktime
|
|
1630
|
+
* @param {TransactionPrebuild} params - The prebuild to modify
|
|
1631
|
+
* @returns {TransactionPrebuild} The modified prebuild
|
|
1632
|
+
*/
|
|
1633
|
+
async postProcessPrebuild(params) {
|
|
1634
|
+
if (!lodash_1.default.isUndefined(params.hopTransaction) && !lodash_1.default.isUndefined(params.wallet) && !lodash_1.default.isUndefined(params.buildParams)) {
|
|
1635
|
+
await this.validateHopPrebuild(params.wallet, params.hopTransaction, params.buildParams);
|
|
1636
|
+
}
|
|
1637
|
+
return params;
|
|
1638
|
+
}
|
|
1639
|
+
/**
|
|
1640
|
+
* Coin-specific things done before signing a transaction, i.e. verification
|
|
1641
|
+
* @param {PresignTransactionOptions} params
|
|
1642
|
+
* @returns {Promise<PresignTransactionOptions>}
|
|
1643
|
+
*/
|
|
1644
|
+
async presignTransaction(params) {
|
|
1645
|
+
if (!lodash_1.default.isUndefined(params.hopTransaction) && !lodash_1.default.isUndefined(params.wallet) && !lodash_1.default.isUndefined(params.buildParams)) {
|
|
1646
|
+
await this.validateHopPrebuild(params.wallet, params.hopTransaction);
|
|
1647
|
+
}
|
|
1648
|
+
return params;
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Fetch fee estimate information from the server
|
|
1652
|
+
* @param {Object} params - The params passed into the function
|
|
1653
|
+
* @param {boolean} [params.hop] - True if we should estimate fee for a hop transaction
|
|
1654
|
+
* @param {string} [params.recipient] - The recipient of the transaction to estimate a send to
|
|
1655
|
+
* @param {string} [params.data] - The ETH tx data to estimate a send for
|
|
1656
|
+
* @returns {Object} The fee info returned from the server
|
|
1657
|
+
*/
|
|
1658
|
+
async feeEstimate(params) {
|
|
1659
|
+
const query = {};
|
|
1660
|
+
if (params && params.hop) {
|
|
1661
|
+
query.hop = params.hop;
|
|
1662
|
+
}
|
|
1663
|
+
if (params && params.recipient) {
|
|
1664
|
+
query.recipient = params.recipient;
|
|
1665
|
+
}
|
|
1666
|
+
if (params && params.data) {
|
|
1667
|
+
query.data = params.data;
|
|
1668
|
+
}
|
|
1669
|
+
if (params && params.amount) {
|
|
1670
|
+
query.amount = params.amount;
|
|
1671
|
+
}
|
|
1672
|
+
return await this.bitgo.get(this.url('/tx/fee')).query(query).result();
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Generate secp256k1 key pair
|
|
1676
|
+
*
|
|
1677
|
+
* @param {Buffer} seed
|
|
1678
|
+
* @returns {KeyPair} object with generated pub and prv
|
|
1679
|
+
*/
|
|
1680
|
+
generateKeyPair(seed) {
|
|
1681
|
+
if (!seed) {
|
|
1682
|
+
// An extended private key has both a normal 256 bit private key and a 256
|
|
1683
|
+
// bit chain code, both of which must be random. 512 bits is therefore the
|
|
1684
|
+
// maximum entropy and gives us maximum security against cracking.
|
|
1685
|
+
seed = (0, crypto_1.randomBytes)(512 / 8);
|
|
1686
|
+
}
|
|
1687
|
+
const extendedKey = utxo_lib_1.bip32.fromSeed(seed);
|
|
1688
|
+
const xpub = extendedKey.neutered().toBase58();
|
|
1689
|
+
return {
|
|
1690
|
+
pub: xpub,
|
|
1691
|
+
prv: extendedKey.toBase58(),
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
async parseTransaction(params) {
|
|
1695
|
+
return {};
|
|
1696
|
+
}
|
|
1697
|
+
/**
|
|
1698
|
+
* Make sure an address is a wallet address and throw an error if it's not.
|
|
1699
|
+
* @param {Object} params
|
|
1700
|
+
* @param {string} params.address - The derived address string on the network
|
|
1701
|
+
* @param {Object} params.coinSpecific - Coin-specific details for the address such as a forwarderVersion
|
|
1702
|
+
* @param {string} params.baseAddress - The base address of the wallet on the network
|
|
1703
|
+
* @throws {InvalidAddressError}
|
|
1704
|
+
* @throws {InvalidAddressVerificationObjectPropertyError}
|
|
1705
|
+
* @throws {UnexpectedAddressError}
|
|
1706
|
+
* @returns {boolean} True iff address is a wallet address
|
|
1707
|
+
*/
|
|
1708
|
+
async isWalletAddress(params) {
|
|
1709
|
+
const ethUtil = exports.optionalDeps.ethUtil;
|
|
1710
|
+
let expectedAddress;
|
|
1711
|
+
let actualAddress;
|
|
1712
|
+
const { address, coinSpecific, baseAddress, impliedForwarderVersion = coinSpecific === null || coinSpecific === void 0 ? void 0 : coinSpecific.forwarderVersion } = params;
|
|
1713
|
+
if (address && !this.isValidAddress(address)) {
|
|
1714
|
+
throw new sdk_core_1.InvalidAddressError(`invalid address: ${address}`);
|
|
1715
|
+
}
|
|
1716
|
+
// base address is required to calculate the salt which is used in calculateForwarderV1Address method
|
|
1717
|
+
if (lodash_1.default.isUndefined(baseAddress) || !this.isValidAddress(baseAddress)) {
|
|
1718
|
+
throw new sdk_core_1.InvalidAddressError('invalid base address');
|
|
1719
|
+
}
|
|
1720
|
+
if (!lodash_1.default.isObject(coinSpecific)) {
|
|
1721
|
+
throw new sdk_core_1.InvalidAddressVerificationObjectPropertyError('address validation failure: coinSpecific field must be an object');
|
|
1722
|
+
}
|
|
1723
|
+
if (impliedForwarderVersion === 0 || impliedForwarderVersion === 3) {
|
|
1724
|
+
return true;
|
|
1725
|
+
}
|
|
1726
|
+
else {
|
|
1727
|
+
const ethNetwork = this.getNetwork();
|
|
1728
|
+
const forwarderFactoryAddress = ethNetwork === null || ethNetwork === void 0 ? void 0 : ethNetwork.forwarderFactoryAddress;
|
|
1729
|
+
const forwarderImplementationAddress = ethNetwork === null || ethNetwork === void 0 ? void 0 : ethNetwork.forwarderImplementationAddress;
|
|
1730
|
+
const initcode = (0, lib_1.getProxyInitcode)(forwarderImplementationAddress);
|
|
1731
|
+
const saltBuffer = ethUtil.setLengthLeft(Buffer.from(ethUtil.padToEven(ethUtil.stripHexPrefix(coinSpecific.salt || '')), 'hex'), 32);
|
|
1732
|
+
// Hash the wallet base address with the given salt, so the address directly relies on the base address
|
|
1733
|
+
const calculationSalt = exports.optionalDeps.ethUtil.bufferToHex(exports.optionalDeps.ethAbi.soliditySHA3(['address', 'bytes32'], [baseAddress, saltBuffer]));
|
|
1734
|
+
expectedAddress = (0, lib_1.calculateForwarderV1Address)(forwarderFactoryAddress, calculationSalt, initcode);
|
|
1735
|
+
actualAddress = address;
|
|
1736
|
+
}
|
|
1737
|
+
if (expectedAddress !== actualAddress) {
|
|
1738
|
+
throw new sdk_core_1.UnexpectedAddressError(`address validation failure: expected ${expectedAddress} but got ${address}`);
|
|
1739
|
+
}
|
|
1740
|
+
return true;
|
|
1741
|
+
}
|
|
1742
|
+
/**
|
|
1743
|
+
*
|
|
1744
|
+
* @param {TransactionPrebuild} txPrebuild
|
|
1745
|
+
* @returns {boolean}
|
|
1746
|
+
*/
|
|
1747
|
+
verifyCoin(txPrebuild) {
|
|
1748
|
+
return txPrebuild.coin === this.getChain();
|
|
1749
|
+
}
|
|
1750
|
+
/**
|
|
1751
|
+
* Verify if a tss transaction is valid
|
|
1752
|
+
*
|
|
1753
|
+
* @param {VerifyEthTransactionOptions} params
|
|
1754
|
+
* @param {TransactionParams} params.txParams - params object passed to send
|
|
1755
|
+
* @param {TransactionPrebuild} params.txPrebuild - prebuild object returned by server
|
|
1756
|
+
* @param {Wallet} params.wallet - Wallet object to obtain keys to verify against
|
|
1757
|
+
* @returns {boolean}
|
|
1758
|
+
*/
|
|
1759
|
+
verifyTssTransaction(params) {
|
|
1760
|
+
var _a;
|
|
1761
|
+
const { txParams, txPrebuild, wallet } = params;
|
|
1762
|
+
if (!(txParams === null || txParams === void 0 ? void 0 : txParams.recipients) &&
|
|
1763
|
+
!(((_a = txParams.prebuildTx) === null || _a === void 0 ? void 0 : _a.consolidateId) ||
|
|
1764
|
+
(txParams.type && ['acceleration', 'fillNonce', 'transferToken'].includes(txParams.type)))) {
|
|
1765
|
+
throw new Error(`missing txParams`);
|
|
1766
|
+
}
|
|
1767
|
+
if (!wallet || !txPrebuild) {
|
|
1768
|
+
throw new Error(`missing params`);
|
|
1769
|
+
}
|
|
1770
|
+
if (txParams.hop && txParams.recipients && txParams.recipients.length > 1) {
|
|
1771
|
+
throw new Error(`tx cannot be both a batch and hop transaction`);
|
|
1772
|
+
}
|
|
1773
|
+
return true;
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Verify that a transaction prebuild complies with the original intention
|
|
1777
|
+
*
|
|
1778
|
+
* @param {VerifyEthTransactionOptions} params
|
|
1779
|
+
* @param {TransactionParams} params.txParams - params object passed to send
|
|
1780
|
+
* @param {TransactionPrebuild} params.txPrebuild - prebuild object returned by server
|
|
1781
|
+
* @param {Wallet} params.wallet - Wallet object to obtain keys to verify against
|
|
1782
|
+
* @returns {boolean}
|
|
1783
|
+
*/
|
|
1784
|
+
async verifyTransaction(params) {
|
|
1785
|
+
const ethNetwork = this.getNetwork();
|
|
1786
|
+
const { txParams, txPrebuild, wallet, walletType } = params;
|
|
1787
|
+
if (walletType === 'tss') {
|
|
1788
|
+
return this.verifyTssTransaction(params);
|
|
1789
|
+
}
|
|
1790
|
+
if (!(txParams === null || txParams === void 0 ? void 0 : txParams.recipients) || !(txPrebuild === null || txPrebuild === void 0 ? void 0 : txPrebuild.recipients) || !wallet) {
|
|
1791
|
+
throw new Error(`missing params`);
|
|
1792
|
+
}
|
|
1793
|
+
if (txParams.hop && txParams.recipients.length > 1) {
|
|
1794
|
+
throw new Error(`tx cannot be both a batch and hop transaction`);
|
|
1795
|
+
}
|
|
1796
|
+
if (txPrebuild.recipients.length !== 1) {
|
|
1797
|
+
throw new Error(`txPrebuild should only have 1 recipient but ${txPrebuild.recipients.length} found`);
|
|
1798
|
+
}
|
|
1799
|
+
if (txParams.hop && txPrebuild.hopTransaction) {
|
|
1800
|
+
// Check recipient amount for hop transaction
|
|
1801
|
+
if (txParams.recipients.length !== 1) {
|
|
1802
|
+
throw new Error(`hop transaction only supports 1 recipient but ${txParams.recipients.length} found`);
|
|
1803
|
+
}
|
|
1804
|
+
// Check tx sends to hop address
|
|
1805
|
+
const decodedHopTx = exports.optionalDeps.EthTx.TransactionFactory.fromSerializedData(exports.optionalDeps.ethUtil.toBuffer(txPrebuild.hopTransaction.tx));
|
|
1806
|
+
const expectedHopAddress = exports.optionalDeps.ethUtil.stripHexPrefix(decodedHopTx.getSenderAddress().toString());
|
|
1807
|
+
const actualHopAddress = exports.optionalDeps.ethUtil.stripHexPrefix(txPrebuild.recipients[0].address);
|
|
1808
|
+
if (expectedHopAddress.toLowerCase() !== actualHopAddress.toLowerCase()) {
|
|
1809
|
+
throw new Error('recipient address of txPrebuild does not match hop address');
|
|
1810
|
+
}
|
|
1811
|
+
// Convert TransactionRecipient array to Recipient array
|
|
1812
|
+
const recipients = txParams.recipients.map((r) => {
|
|
1813
|
+
return {
|
|
1814
|
+
address: r.address,
|
|
1815
|
+
amount: typeof r.amount === 'number' ? r.amount.toString() : r.amount,
|
|
1816
|
+
};
|
|
1817
|
+
});
|
|
1818
|
+
// Check destination address and amount
|
|
1819
|
+
await this.validateHopPrebuild(wallet, txPrebuild.hopTransaction, { recipients });
|
|
1820
|
+
}
|
|
1821
|
+
else if (txParams.recipients.length > 1) {
|
|
1822
|
+
// Check total amount for batch transaction
|
|
1823
|
+
let expectedTotalAmount = new bignumber_js_1.BigNumber(0);
|
|
1824
|
+
for (let i = 0; i < txParams.recipients.length; i++) {
|
|
1825
|
+
expectedTotalAmount = expectedTotalAmount.plus(txParams.recipients[i].amount);
|
|
1826
|
+
}
|
|
1827
|
+
if (!expectedTotalAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
|
|
1828
|
+
throw new Error('batch transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client');
|
|
1829
|
+
}
|
|
1830
|
+
// Check batch transaction is sent to the batcher contract address for the chain
|
|
1831
|
+
const batcherContractAddress = ethNetwork === null || ethNetwork === void 0 ? void 0 : ethNetwork.batcherContractAddress;
|
|
1832
|
+
if (!batcherContractAddress ||
|
|
1833
|
+
batcherContractAddress.toLowerCase() !== txPrebuild.recipients[0].address.toLowerCase()) {
|
|
1834
|
+
throw new Error('recipient address of txPrebuild does not match batcher address');
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
else {
|
|
1838
|
+
// Check recipient address and amount for normal transaction
|
|
1839
|
+
if (txParams.recipients.length !== 1) {
|
|
1840
|
+
throw new Error(`normal transaction only supports 1 recipient but ${txParams.recipients.length} found`);
|
|
1841
|
+
}
|
|
1842
|
+
const expectedAmount = new bignumber_js_1.BigNumber(txParams.recipients[0].amount);
|
|
1843
|
+
if (!expectedAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
|
|
1844
|
+
throw new Error('normal transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client');
|
|
1845
|
+
}
|
|
1846
|
+
if (this.isETHAddress(txParams.recipients[0].address) &&
|
|
1847
|
+
txParams.recipients[0].address !== txPrebuild.recipients[0].address) {
|
|
1848
|
+
throw new Error('destination address in normal txPrebuild does not match that in txParams supplied by client');
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
// Check coin is correct for all transaction types
|
|
1852
|
+
if (!this.verifyCoin(txPrebuild)) {
|
|
1853
|
+
throw new Error(`coin in txPrebuild did not match that in txParams supplied by client`);
|
|
1854
|
+
}
|
|
1855
|
+
return true;
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Check if address is valid eth address
|
|
1859
|
+
* @param address
|
|
1860
|
+
* @returns {boolean}
|
|
1861
|
+
*/
|
|
1862
|
+
isETHAddress(address) {
|
|
1863
|
+
return !!address.match(/0x[a-fA-F0-9]{40}/);
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Transform message to accommodate specific blockchain requirements.
|
|
1867
|
+
* @param {string} message - the message to prepare
|
|
1868
|
+
* @return {string} the prepared message.
|
|
1869
|
+
*/
|
|
1870
|
+
encodeMessage(message) {
|
|
1871
|
+
const prefix = `\u0019Ethereum Signed Message:\n${message.length}`;
|
|
1872
|
+
return prefix.concat(message);
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Transform the Typed data to accomodate the blockchain requirements (EIP-712)
|
|
1876
|
+
* @param {TypedData} typedData - the typed data to prepare
|
|
1877
|
+
* @return {Buffer} a buffer of the result
|
|
1878
|
+
*/
|
|
1879
|
+
encodeTypedData(typedData) {
|
|
1880
|
+
const version = typedData.version;
|
|
1881
|
+
if (version === eth_sig_util_1.SignTypedDataVersion.V1) {
|
|
1882
|
+
throw new Error('SignTypedData v1 is not supported due to security concerns');
|
|
1883
|
+
}
|
|
1884
|
+
const typedDataRaw = JSON.parse(typedData.typedDataRaw);
|
|
1885
|
+
const sanitizedData = eth_sig_util_1.TypedDataUtils.sanitizeData(typedDataRaw);
|
|
1886
|
+
const parts = [Buffer.from('1901', 'hex')];
|
|
1887
|
+
const eip712Domain = 'EIP712Domain';
|
|
1888
|
+
parts.push(eth_sig_util_1.TypedDataUtils.hashStruct(eip712Domain, sanitizedData.domain, sanitizedData.types, version));
|
|
1889
|
+
if (sanitizedData.primaryType !== eip712Domain) {
|
|
1890
|
+
parts.push(eth_sig_util_1.TypedDataUtils.hashStruct(sanitizedData.primaryType, sanitizedData.message, sanitizedData.types, version));
|
|
1891
|
+
}
|
|
1892
|
+
return Buffer.concat(parts);
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Build the data to transfer an ERC-721 or ERC-1155 token to another address
|
|
1896
|
+
* @param params
|
|
1897
|
+
*/
|
|
1898
|
+
buildNftTransferData(params) {
|
|
1899
|
+
const { tokenContractAddress, recipientAddress, fromAddress } = params;
|
|
1900
|
+
switch (params.type) {
|
|
1901
|
+
case 'ERC721': {
|
|
1902
|
+
const tokenId = params.tokenId;
|
|
1903
|
+
const contractData = new lib_1.ERC721TransferBuilder()
|
|
1904
|
+
.tokenContractAddress(tokenContractAddress)
|
|
1905
|
+
.to(recipientAddress)
|
|
1906
|
+
.from(fromAddress)
|
|
1907
|
+
.tokenId(tokenId)
|
|
1908
|
+
.build();
|
|
1909
|
+
return contractData;
|
|
1910
|
+
}
|
|
1911
|
+
case 'ERC1155': {
|
|
1912
|
+
const entries = params.entries;
|
|
1913
|
+
const transferBuilder = new lib_1.ERC1155TransferBuilder()
|
|
1914
|
+
.tokenContractAddress(tokenContractAddress)
|
|
1915
|
+
.to(recipientAddress)
|
|
1916
|
+
.from(fromAddress);
|
|
1917
|
+
for (const entry of entries) {
|
|
1918
|
+
transferBuilder.entry(parseInt(entry.tokenId, 10), entry.amount);
|
|
1919
|
+
}
|
|
1920
|
+
return transferBuilder.build();
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
exports.AbstractEthLikeNewCoins = AbstractEthLikeNewCoins;
|
|
1926
|
+
AbstractEthLikeNewCoins.hopTransactionSalt = 'bitgoHopAddressRequestSalt';
|
|
1927
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUE7O0dBRUc7QUFDSCxtREFpQzhCO0FBQzlCLHlEQUF5SDtBQUN6SCxpREFNNkI7QUFDN0IsbURBQTZDO0FBRzdDLHVDQUErRjtBQUMvRix5REFBNEY7QUFDNUYsK0NBQXlDO0FBQ3pDLGtEQUF1QjtBQUN2QixtQ0FBcUM7QUFDckMsa0RBQTZCO0FBQzdCLHFEQUErRDtBQUMvRCxvREFBNEI7QUFDNUIsb0RBQXVCO0FBQ3ZCLDBEQUFrQztBQUVsQywrREFBNEQ7QUFDNUQsaURBQThDO0FBQzlDLCtCQVVlO0FBK1BmLE1BQU0sS0FBSyxHQUFHLElBQUEsZUFBUSxFQUFDLGtCQUFrQixDQUFDLENBQUM7QUFFOUIsUUFBQSxZQUFZLEdBQUc7SUFDMUIsSUFBSSxNQUFNO1FBQ1IsSUFBSTtZQUNGLE9BQU8sT0FBTyxDQUFDLGdCQUFnQixDQUFDLENBQUM7U0FDbEM7UUFBQyxPQUFPLENBQUMsRUFBRTtZQUNWLEtBQUssQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFDO1lBQ3hDLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDZixNQUFNLElBQUksMENBQStCLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztTQUM3RDtJQUNILENBQUM7SUFFRCxJQUFJLE9BQU87UUFDVCxJQUFJO1lBQ0YsT0FBTyxPQUFPLENBQUMsaUJBQWlCLENBQUMsQ0FBQztTQUNuQztRQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQ1YsS0FBSyxDQUFDLGlDQUFpQyxDQUFDLENBQUM7WUFDekMsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNmLE1BQU0sSUFBSSwwQ0FBK0IsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1NBQzlEO0lBQ0gsQ0FBQztJQUVELElB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Q0Y7SUFDSCxDQUFDOztBQTF1RUgsMERBMnVFQztBQTF1RVEsMENBQWtCLEdBQUcsNEJBQTRCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBwcmV0dGllclxuICovXG5pbXBvcnQge1xuICBBZGRyZXNzQ29pblNwZWNpZmljLFxuICBCaXRHb0Jhc2UsXG4gIEJ1aWxkTmZ0VHJhbnNmZXJEYXRhT3B0aW9ucyxcbiAgY29tbW9uLFxuICBFQ0RTQSxcbiAgRWNkc2EsXG4gIEVDRFNBTWV0aG9kVHlwZXMsXG4gIEV0aGVyZXVtTGlicmFyeVVuYXZhaWxhYmxlRXJyb3IsXG4gIEZlZUVzdGltYXRlT3B0aW9ucyxcbiAgRnVsbHlTaWduZWRUcmFuc2FjdGlvbixcbiAgZ2V0SXNVbnNpZ25lZFN3ZWVwLFxuICBIYWxmU2lnbmVkVHJhbnNhY3Rpb24sXG4gIGhleFRvQmlnSW50LFxuICBJbnZhbGlkQWRkcmVzc0Vycm9yLFxuICBJbnZhbGlkQWRkcmVzc1ZlcmlmaWNhdGlvbk9iamVjdFByb3BlcnR5RXJyb3IsXG4gIElXYWxsZXQsXG4gIEtleVBhaXIsXG4gIFBhcnNlZFRyYW5zYWN0aW9uLFxuICBQYXJzZVRyYW5zYWN0aW9uT3B0aW9ucyxcbiAgUHJlYnVpbGRUcmFuc2FjdGlvblJlc3VsdCxcbiAgUHJlc2lnblRyYW5zYWN0aW9uT3B0aW9ucyBhcyBCYXNlUHJlc2lnblRyYW5zYWN0aW9uT3B0aW9ucyxcbiAgUmVjaXBpZW50LFxuICBTaWduVHJhbnNhY3Rpb25PcHRpb25zIGFzIEJhc2VTaWduVHJhbnNhY3Rpb25PcHRpb25zLFxuICBUcmFuc2FjdGlvblBhcmFtcyxcbiAgVHJhbnNhY3Rpb25QcmVidWlsZCBhcyBCYXNlVHJhbnNhY3Rpb25QcmVidWlsZCxcbiAgVHJhbnNhY3Rpb25SZWNpcGllbnQsXG4gIFR5cGVkRGF0YSxcbiAgVW5leHBlY3RlZEFkZHJlc3NFcnJvcixcbiAgVXRpbCxcbiAgVmVyaWZ5QWRkcmVzc09wdGlvbnMgYXMgQmFzZVZlcmlmeUFkZHJlc3NPcHRpb25zLFxuICBWZXJpZnlUcmFuc2FjdGlvbk9wdGlvbnMsXG4gIFdhbGxldCxcbn0gZnJvbSAnQGJpdGdvLWJldGEvc2RrLWNvcmUnO1xuaW1wb3J0IHsgRGtsc0RzZywgRGtsc1R5cGVzLCBEa2xzVXRpbHMsIEVjZHNhUGFpbGxpZXJQcm9vZiwgRWNkc2FSYW5nZVByb29mLCBFY2RzYVR5cGVzIH0gZnJvbSAnQGJpdGdvLWJldGEvc2RrLWxpYi1tcGMnO1xuaW1wb3J0IHtcbiAgQmFzZUNvaW4gYXMgU3RhdGljc0Jhc2VDb2luLFxuICBDb2luTWFwLFxuICBjb2lucyxcbiAgRXRoZXJldW1OZXR3b3JrIGFzIEV0aExpa2VOZXR3b3JrLFxuICBldGhHYXNDb25maWdzLFxufSBmcm9tICdAYml0Z28tYmV0YS9zdGF0aWNzJztcbmltcG9ydCB7IGJpcDMyIH0gZnJvbSAnQGJpdGdvLWJldGEvdXR4by1saWInO1xuaW1wb3J0IHR5cGUgKiBhcyBFdGhMaWtlQ29tbW9uIGZyb20gJ0BldGhlcmV1bWpzL2NvbW1vbic7XG5pbXBvcnQgdHlwZSAqIGFzIEV0aExpa2VUeExpYiBmcm9tICdAZXRoZXJldW1qcy90eCc7XG5pbXBvcnQgeyBGZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24sIFRyYW5zYWN0aW9uIGFzIExlZ2FjeVRyYW5zYWN0aW9uIH0gZnJvbSAnQGV0aGVyZXVtanMvdHgnO1xuaW1wb3J0IHsgU2lnblR5cGVkRGF0YVZlcnNpb24sIFR5cGVkRGF0YVV0aWxzLCBUeXBlZE1lc3NhZ2UgfSBmcm9tICdAbWV0YW1hc2svZXRoLXNpZy11dGlsJztcbmltcG9ydCB7IEJpZ051bWJlciB9IGZyb20gJ2JpZ251bWJlci5qcyc7XG5pbXBvcnQgQk4gZnJvbSAnYm4uanMnO1xuaW1wb3J0IHsgcmFuZG9tQnl0ZXMgfSBmcm9tICdjcnlwdG8nO1xuaW1wb3J0IGRlYnVnTGliIGZyb20gJ2RlYnVnJztcbmltcG9ydCB7IGFkZEhleFByZWZpeCwgc3RyaXBIZXhQcmVmaXggfSBmcm9tICdldGhlcmV1bWpzLXV0aWwnO1xuaW1wb3J0IEtlY2NhayBmcm9tICdrZWNjYWsnO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCBzZWNwMjU2azEgZnJvbSAnc2VjcDI1NmsxJztcblxuaW1wb3J0IHsgQWJzdHJhY3RFdGhMaWtlQ29pbiB9IGZyb20gJy4vYWJzdHJhY3RFdGhMaWtlQ29pbic7XG5pbXBvcnQgeyBFdGhMaWtlVG9rZW4gfSBmcm9tICcuL2V0aExpa2VUb2tlbic7XG5pbXBvcnQge1xuICBjYWxjdWxhdGVGb3J3YXJkZXJWMUFkZHJlc3MsXG4gIEVSQzExNTVUcmFuc2ZlckJ1aWxkZXIsXG4gIEVSQzcyMVRyYW5zZmVyQnVpbGRlcixcbiAgZ2V0Q29tbW9uLFxuICBnZXRQcm94eUluaXRjb2RlLFxuICBnZXRUb2tlbixcbiAgS2V5UGFpciBhcyBLZXlQYWlyTGliLFxuICBUcmFuc2FjdGlvbkJ1aWxkZXIsXG4gIFRyYW5zZmVyQnVpbGRlcixcbn0gZnJvbSAnLi9saWInO1xuXG4vKipcbiAqIFRoZSBwcmVidWlsdCBob3AgdHJhbnNhY3Rpb24gcmV0dXJuZWQgZnJvbSB0aGUgSFNNXG4gKi9cbmludGVyZmFjZSBIb3BQcmVidWlsZCB7XG4gIHR4OiBzdHJpbmc7XG4gIGlkOiBzdHJpbmc7XG4gIHNpZ25hdHVyZTogc3RyaW5nO1xuICBwYXltZW50SWQ6IHN0cmluZztcbiAgZ2FzUHJpY2U6IG51bWJlcjtcbiAgZ2FzTGltaXQ6IG51bWJlcjtcbiAgYW1vdW50OiBudW1iZXI7XG4gIHJlY2lwaWVudDogc3RyaW5nO1xuICBub25jZTogbnVtYmVyO1xuICB1c2VyUmVxU2lnOiBzdHJpbmc7XG4gIGdhc1ByaWNlTWF4OiBudW1iZXI7XG59XG5cbi8qKlxuICogVGhlIGV4dHJhIHBhcmFtZXRlcnMgdG8gc2VuZCB0byBwbGF0Zm9ybSBidWlsZCByb3V0ZSBmb3IgaG9wIHRyYW5zYWN0aW9uc1xuICovXG5pbnRlcmZhY2UgSG9wUGFyYW1zIHtcbiAgaG9wUGFyYW1zOiB7XG4gICAgZ2FzUHJpY2VNYXg6IG51bWJlcjtcbiAgICB1c2VyUmVxU2lnOiBzdHJpbmc7XG4gICAgcGF5bWVudElkOiBzdHJpbmc7XG4gIH07XG4gIGdhc0xpbWl0OiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRUlQMTU1OSB7XG4gIG1heFByaW9yaXR5RmVlUGVyR2FzOiBudW1iZXI7XG4gIG1heEZlZVBlckdhczogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlcGxheVByb3RlY3Rpb25PcHRpb25zIHtcbiAgY2hhaW46IHN0cmluZyB8IG51bWJlcjtcbiAgaGFyZGZvcms6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBUcmFuc2FjdGlvblByZWJ1aWxkIGV4dGVuZHMgQmFzZVRyYW5zYWN0aW9uUHJlYnVpbGQge1xuICBob3BUcmFuc2FjdGlvbj86IEhvcFByZWJ1aWxkO1xuICBidWlsZFBhcmFtczoge1xuICAgIHJlY2lwaWVudHM6IFJlY2lwaWVudFtdO1xuICB9O1xuICByZWNpcGllbnRzOiBUcmFuc2FjdGlvblJlY2lwaWVudFtdO1xuICBuZXh0Q29udHJhY3RTZXF1ZW5jZUlkOiBudW1iZXI7XG4gIGdhc1ByaWNlOiBudW1iZXI7XG4gIGdhc0xpbWl0OiBudW1iZXI7XG4gIGlzQmF0Y2g6IGJvb2xlYW47XG4gIGNvaW46IHN0cmluZztcbiAgdG9rZW4/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2lnbkZpbmFsT3B0aW9ucyB7XG4gIHR4UHJlYnVpbGQ6IHtcbiAgICBlaXAxNTU5PzogRUlQMTU1OTtcbiAgICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zO1xuICAgIGdhc1ByaWNlPzogc3RyaW5nO1xuICAgIGdhc0xpbWl0Pzogc3RyaW5nO1xuICAgIHJlY2lwaWVudHM/OiBSZWNpcGllbnRbXTtcbiAgICBoYWxmU2lnbmVkPzoge1xuICAgICAgZXhwaXJlVGltZTogbnVtYmVyO1xuICAgICAgY29udHJhY3RTZXF1ZW5jZUlkOiBudW1iZXI7XG4gICAgICBiYWNrdXBLZXlOb25jZT86IG51bWJlcjtcbiAgICAgIHNpZ25hdHVyZTogc3RyaW5nO1xuICAgICAgdHhIZXg/OiBzdHJpbmc7XG4gICAgfTtcbiAgICBuZXh0Q29udHJhY3RTZXF1ZW5jZUlkPzogbnVtYmVyO1xuICAgIGhvcFRyYW5zYWN0aW9uPzogc3RyaW5nO1xuICAgIGJhY2t1cEtleU5vbmNlPzogbnVtYmVyO1xuICAgIGlzQmF0Y2g/OiBib29sZWFuO1xuICAgIHR4SGV4Pzogc3RyaW5nO1xuICAgIGV4cGlyZVRpbWU/OiBudW1iZXI7XG4gIH07XG4gIHNpZ25pbmdLZXlOb25jZT86IG51bWJlcjtcbiAgd2FsbGV0Q29udHJhY3RBZGRyZXNzPzogc3RyaW5nO1xuICBwcnY6IHN0cmluZztcbiAgcmVjaXBpZW50cz86IFJlY2lwaWVudFtdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFNpZ25UcmFuc2FjdGlvbk9wdGlvbnMgZXh0ZW5kcyBCYXNlU2lnblRyYW5zYWN0aW9uT3B0aW9ucywgU2lnbkZpbmFsT3B0aW9ucyB7XG4gIGlzTGFzdFNpZ25hdHVyZT86IGJvb2xlYW47XG4gIGV4cGlyZVRpbWU/OiBudW1iZXI7XG4gIHNlcXVlbmNlSWQ/OiBudW1iZXI7XG4gIGdhc0xpbWl0PzogbnVtYmVyO1xuICBnYXNQcmljZT86IG51bWJlcjtcbiAgY3VzdG9kaWFuVHJhbnNhY3Rpb25JZD86IHN0cmluZztcbn1cblxuZXhwb3J0IHR5cGUgU2lnbmVkVHJhbnNhY3Rpb24gPSBIYWxmU2lnbmVkVHJhbnNhY3Rpb24gfCBGdWxseVNpZ25lZFRyYW5zYWN0aW9uO1xuXG5leHBvcnQgaW50ZXJmYWNlIEZlZXNVc2VkIHtcbiAgZ2FzUHJpY2U6IG51bWJlcjtcbiAgZ2FzTGltaXQ6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFByZWNyZWF0ZUJpdEdvT3B0aW9ucyB7XG4gIGVudGVycHJpc2U/OiBzdHJpbmc7XG4gIG5ld0ZlZUFkZHJlc3M/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgT2ZmbGluZVZhdWx0VHhJbmZvIHtcbiAgbmV4dENvbnRyYWN0U2VxdWVuY2VJZD86IHN0cmluZztcbiAgY29udHJhY3RTZXF1ZW5jZUlkPzogc3RyaW5nO1xuICB0eD86IHN0cmluZztcbiAgdHhIZXg/OiBzdHJpbmc7XG4gIHVzZXJLZXk/OiBzdHJpbmc7XG4gIGJhY2t1cEtleT86IHN0cmluZztcbiAgY29pbjogc3RyaW5nO1xuICBnYXNQcmljZTogbnVtYmVyO1xuICBnYXNMaW1pdDogbnVtYmVyO1xuICByZWNpcGllbnRzOiBSZWNpcGllbnRbXTtcbiAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiBzdHJpbmc7XG4gIGFtb3VudDogc3RyaW5nO1xuICBiYWNrdXBLZXlOb25jZTogbnVtYmVyO1xuICAvLyBGb3IgRXRoIFNwZWNpZmljIENvaW5zXG4gIGVpcDE1NTk/OiBFSVAxNTU5O1xuICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zO1xuICAvLyBGb3IgSG90IFdhbGxldCBFdm1CYXNlZENyb3NzQ2hhaW5SZWNvdmVyeSBTcGVjaWZpY1xuICBoYWxmU2lnbmVkPzogSGFsZlNpZ25lZFRyYW5zYWN0aW9uO1xuICBmZWVzVXNlZD86IEZlZXNVc2VkO1xuICBpc0V2bUJhc2VkQ3Jvc3NDaGFpblJlY292ZXJ5PzogYm9vbGVhbjtcbn1cblxuaW50ZXJmYWNlIFVuZm9ybWF0dGVkVHhJbmZvIHtcbiAgcmVjaXBpZW50OiBSZWNpcGllbnQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVjb3Zlck9wdGlvbnMge1xuICB1c2VyS2V5OiBzdHJpbmc7XG4gIGJhY2t1cEtleTogc3RyaW5nO1xuICB3YWxsZXRQYXNzcGhyYXNlPzogc3RyaW5nO1xuICB3YWxsZXRDb250cmFjdEFkZHJlc3M6IHN0cmluZzsgLy8gdXNlIHRoaXMgYXMgd2FsbGV0QmFzZUFkZHJlc3MgZm9yIFRTU1xuICByZWNvdmVyeURlc3RpbmF0aW9uOiBzdHJpbmc7XG4gIGtyc1Byb3ZpZGVyPzogc3RyaW5nO1xuICBnYXNQcmljZT86IG51bWJlcjtcbiAgZ2FzTGltaXQ/OiBudW1iZXI7XG4gIGVpcDE1NTk/OiBFSVAxNTU5O1xuICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zO1xuICBpc1Rzcz86IGJvb2xlYW47XG4gIGJpdGdvRmVlQWRkcmVzcz86IHN0cmluZztcbiAgYml0Z29EZXN0aW5hdGlvbkFkZHJlc3M/OiBzdHJpbmc7XG4gIHRva2VuQ29udHJhY3RBZGRyZXNzPzogc3RyaW5nO1xuICBpbnRlbmRlZENoYWluPzogc3RyaW5nO1xufVxuXG5leHBvcnQgdHlwZSBHZXRCYXRjaEV4ZWN1dGlvbkluZm9SVCA9IHtcbiAgdmFsdWVzOiBbc3RyaW5nW10sIHN0cmluZ1tdXTtcbiAgdG90YWxBbW91bnQ6IHN0cmluZztcbn07XG5cbmV4cG9ydCBpbnRlcmZhY2UgQnVpbGRUcmFuc2FjdGlvblBhcmFtcyB7XG4gIHRvOiBzdHJpbmc7XG4gIG5vbmNlPzogbnVtYmVyO1xuICB2YWx1ZTogbnVtYmVyO1xuICBkYXRhPzogQnVmZmVyO1xuICBnYXNQcmljZT86IG51bWJlcjtcbiAgZ2FzTGltaXQ/OiBudW1iZXI7XG4gIGVpcDE1NTk/OiBFSVAxNTU5O1xuICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlY292ZXJ5SW5mbyB7XG4gIGlkOiBzdHJpbmc7XG4gIHR4OiBzdHJpbmc7XG4gIGJhY2t1cEtleT86IHN0cmluZztcbiAgY29pbj86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSZWNvdmVyVG9rZW5UcmFuc2FjdGlvbiB7XG4gIGhhbGZTaWduZWQ6IHtcbiAgICByZWNpcGllbnQ6IFJlY2lwaWVudDtcbiAgICBleHBpcmVUaW1lOiBudW1iZXI7XG4gICAgY29udHJhY3RTZXF1ZW5jZUlkOiBudW1iZXI7XG4gICAgb3BlcmF0aW9uSGFzaDogc3RyaW5nO1xuICAgIHNpZ25hdHVyZTogc3RyaW5nO1xuICAgIGdhc0xpbWl0OiBudW1iZXI7XG4gICAgZ2FzUHJpY2U6IG51bWJlcjtcbiAgICB0b2tlbkNvbnRyYWN0QWRkcmVzczogc3RyaW5nO1xuICAgIHdhbGxldElkOiBzdHJpbmc7XG4gIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVjb3ZlclRva2VuT3B0aW9ucyB7XG4gIHRva2VuQ29udHJhY3RBZGRyZXNzOiBzdHJpbmc7XG4gIHdhbGxldDogV2FsbGV0O1xuICByZWNpcGllbnQ6IHN0cmluZztcbiAgYnJvYWRjYXN0PzogYm9vbGVhbjtcbiAgd2FsbGV0UGFzc3BocmFzZT86IHN0cmluZztcbiAgcHJ2Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEdldFNlbmRNZXRob2RBcmdzT3B0aW9ucyB7XG4gIHJlY2lwaWVudDogUmVjaXBpZW50O1xuICBleHBpcmVUaW1lOiBudW1iZXI7XG4gIGNvbnRyYWN0U2VxdWVuY2VJZDogbnVtYmVyO1xuICBzaWduYXR1cmU6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTZW5kTWV0aG9kQXJncyB7XG4gIG5hbWU6IHN0cmluZztcbiAgdHlwZTogc3RyaW5nO1xuICB2YWx1ZTogYW55O1xufVxuXG5pbnRlcmZhY2UgSG9wVHJhbnNhY3Rpb25CdWlsZE9wdGlvbnMge1xuICB3YWxsZXQ6IFdhbGxldDtcbiAgcmVjaXBpZW50czogUmVjaXBpZW50W107XG4gIHdhbGxldFBhc3NwaHJhc2U6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBCdWlsZE9wdGlvbnMge1xuICBob3A/OiBib29sZWFuO1xuICB3YWxsZXQ/OiBXYWxsZXQ7XG4gIHJlY2lwaWVudHM/OiBSZWNpcGllbnRbXTtcbiAgd2FsbGV0UGFzc3BocmFzZT86IHN0cmluZztcbiAgW2luZGV4OiBzdHJpbmddOiB1bmtub3duO1xufVxuXG5pbnRlcmZhY2UgRmVlRXN0aW1hdGUge1xuICBnYXNMaW1pdEVzdGltYXRlOiBudW1iZXI7XG4gIGZlZUVzdGltYXRlOiBudW1iZXI7XG59XG5cbi8vIFRPRE86IFRoaXMgaW50ZXJmYWNlIHdpbGwgbmVlZCB0byBiZSB1cGRhdGVkIGZvciB0aGUgbmV3IGZlZSBtb2RlbCBpbnRyb2R1Y2VkIGluIHRoZSBMb25kb24gSGFyZCBGb3JrXG5pbnRlcmZhY2UgRXRoVHJhbnNhY3Rpb25QYXJhbXMgZXh0ZW5kcyBUcmFuc2FjdGlvblBhcmFtcyB7XG4gIGdhc1ByaWNlPzogbnVtYmVyO1xuICBnYXNMaW1pdD86IG51bWJlcjtcbiAgaG9wUGFyYW1zPzogSG9wUGFyYW1zO1xuICBob3A/OiBib29sZWFuO1xuICBwcmVidWlsZFR4PzogUHJlYnVpbGRUcmFuc2FjdGlvblJlc3VsdDtcbn1cblxuaW50ZXJmYWNlIFZlcmlmeUV0aFRyYW5zYWN0aW9uT3B0aW9ucyBleHRlbmRzIFZlcmlmeVRyYW5zYWN0aW9uT3B0aW9ucyB7XG4gIHR4UHJlYnVpbGQ6IFRyYW5zYWN0aW9uUHJlYnVpbGQ7XG4gIHR4UGFyYW1zOiBFdGhUcmFuc2FjdGlvblBhcmFtcztcbn1cblxuaW50ZXJmYWNlIFByZXNpZ25UcmFuc2FjdGlvbk9wdGlvbnMgZXh0ZW5kcyBUcmFuc2FjdGlvblByZWJ1aWxkLCBCYXNlUHJlc2lnblRyYW5zYWN0aW9uT3B0aW9ucyB7XG4gIHdhbGxldDogV2FsbGV0O1xufVxuXG5pbnRlcmZhY2UgRXRoQWRkcmVzc0NvaW5TcGVjaWZpY3MgZXh0ZW5kcyBBZGRyZXNzQ29pblNwZWNpZmljIHtcbiAgZm9yd2FyZGVyVmVyc2lvbjogbnVtYmVyO1xuICBzYWx0Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFZlcmlmeUV0aEFkZHJlc3NPcHRpb25zIGV4dGVuZHMgQmFzZVZlcmlmeUFkZHJlc3NPcHRpb25zIHtcbiAgYmFzZUFkZHJlc3M6IHN0cmluZztcbiAgY29pblNwZWNpZmljOiBFdGhBZGRyZXNzQ29pblNwZWNpZmljcztcbiAgZm9yd2FyZGVyVmVyc2lvbjogbnVtYmVyO1xufVxuXG5jb25zdCBkZWJ1ZyA9IGRlYnVnTGliKCdiaXRnbzp2MjpldGhsaWtlJyk7XG5cbmV4cG9ydCBjb25zdCBvcHRpb25hbERlcHMgPSB7XG4gIGdldCBldGhBYmkoKSB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiByZXF1aXJlKCdldGhlcmV1bWpzLWFiaScpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGRlYnVnKCd1bmFibGUgdG8gbG9hZCBldGhlcmV1bWpzLWFiaTonKTtcbiAgICAgIGRlYnVnKGUuc3RhY2spO1xuICAgICAgdGhyb3cgbmV3IEV0aGVyZXVtTGlicmFyeVVuYXZhaWxhYmxlRXJyb3IoYGV0aGVyZXVtanMtYWJpYCk7XG4gICAgfVxuICB9LFxuXG4gIGdldCBldGhVdGlsKCkge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gcmVxdWlyZSgnZXRoZXJldW1qcy11dGlsJyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgZGVidWcoJ3VuYWJsZSB0byBsb2FkIGV0aGVyZXVtanMtdXRpbDonKTtcbiAgICAgIGRlYnVnKGUuc3RhY2spO1xuICAgICAgdGhyb3cgbmV3IEV0aGVyZXVtTGlicmFyeVVuYXZhaWxhYmxlRXJyb3IoYGV0aGVyZXVtanMtdXRpbGApO1xuICAgIH1cbiAgfSxcblxuICBnZXQgRXRoVHgoKTogdHlwZW9mIEV0aExpa2VUeExpYiB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiByZXF1aXJlKCdAZXRoZXJldW1qcy90eCcpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGRlYnVnKCd1bmFibGUgdG8gbG9hZCBAZXRoZXJldW1qcy90eCcpO1xuICAgICAgZGVidWcoZS5zdGFjayk7XG4gICAgICB0aHJvdyBuZXcgRXRoZXJldW1MaWJyYXJ5VW5hdmFpbGFibGVFcnJvcihgQGV0aGVyZXVtanMvdHhgKTtcbiAgICB9XG4gIH0sXG5cbiAgZ2V0IEV0aENvbW1vbigpOiB0eXBlb2YgRXRoTGlrZUNvbW1vbiB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiByZXF1aXJlKCdAZXRoZXJldW1qcy9jb21tb24nKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBkZWJ1ZygndW5hYmxlIHRvIGxvYWQgQGV0aGVyZXVtanMvY29tbW9uOicpO1xuICAgICAgZGVidWcoZS5zdGFjayk7XG4gICAgICB0aHJvdyBuZXcgRXRoZXJldW1MaWJyYXJ5VW5hdmFpbGFibGVFcnJvcihgQGV0aGVyZXVtanMvY29tbW9uYCk7XG4gICAgfVxuICB9LFxufTtcblxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEFic3RyYWN0RXRoTGlrZU5ld0NvaW5zIGV4dGVuZHMgQWJzdHJhY3RFdGhMaWtlQ29pbiB7XG4gIHN0YXRpYyBob3BUcmFuc2FjdGlvblNhbHQgPSAnYml0Z29Ib3BBZGRyZXNzUmVxdWVzdFNhbHQnO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgc2VuZE1ldGhvZE5hbWU6ICdzZW5kTXVsdGlTaWcnIHwgJ3NlbmRNdWx0aVNpZ1Rva2VuJztcblxuICByZWFkb25seSBzdGF0aWNzQ29pbj86IFJlYWRvbmx5PFN0YXRpY3NCYXNlQ29pbj47XG5cbiAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKGJpdGdvOiBCaXRHb0Jhc2UsIHN0YXRpY3NDb2luPzogUmVhZG9ubHk8U3RhdGljc0Jhc2VDb2luPikge1xuICAgIHN1cGVyKGJpdGdvLCBzdGF0aWNzQ29pbik7XG5cbiAgICBpZiAoIXN0YXRpY3NDb2luKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3NpbmcgcmVxdWlyZWQgY29uc3RydWN0b3IgcGFyYW1ldGVyIHN0YXRpY3NDb2luJyk7XG4gICAgfVxuXG4gICAgdGhpcy5zdGF0aWNzQ29pbiA9IHN0YXRpY3NDb2luO1xuICAgIHRoaXMuc2VuZE1ldGhvZE5hbWUgPSAnc2VuZE11bHRpU2lnJztcbiAgfVxuXG4gIC8qKlxuICAgKiBNZXRob2QgdG8gcmV0dXJuIHRoZSBjb2luJ3MgbmV0d29yayBvYmplY3RcbiAgICogQHJldHVybnMge0V0aExpa2VOZXR3b3JrIHwgdW5kZWZpbmVkfVxuICAgKi9cbiAgZ2V0TmV0d29yaygpOiBFdGhMaWtlTmV0d29yayB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuc3RhdGljc0NvaW4/Lm5ldHdvcmsgYXMgRXRoTGlrZU5ldHdvcms7XG4gIH1cblxuICAvKipcbiAgICogRXZhbHVhdGVzIHdoZXRoZXIgYW4gYWRkcmVzcyBzdHJpbmcgaXMgdmFsaWQgZm9yIHRoaXMgY29pblxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWRkcmVzc1xuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBhZGRyZXNzIGlzIHRoZSB2YWxpZCBldGhsaWtlIGFkZGVyc3NcbiAgICovXG4gIGlzVmFsaWRBZGRyZXNzKGFkZHJlc3M6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBvcHRpb25hbERlcHMuZXRoVXRpbC5pc1ZhbGlkQWRkcmVzcyhvcHRpb25hbERlcHMuZXRoVXRpbC5hZGRIZXhQcmVmaXgoYWRkcmVzcykpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZsYWcgZm9yIHNlbmRpbmcgZGF0YSBhbG9uZyB3aXRoIHRyYW5zYWN0aW9uc1xuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBva2F5IHRvIHNlbmQgdHggZGF0YSAoRVRIKSwgZmFsc2Ugb3RoZXJ3aXNlXG4gICAqL1xuICB0cmFuc2FjdGlvbkRhdGFBbGxvd2VkKCkge1xuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIERlZmF1bHQgZXhwaXJlIHRpbWUgZm9yIGEgY29udHJhY3QgY2FsbCAoMSB3ZWVrKVxuICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaW1lIGluIHNlY29uZHNcbiAgICovXG4gIGdldERlZmF1bHRFeHBpcmVUaW1lKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIE1hdGguZmxvb3IobmV3IERhdGUoKS5nZXRUaW1lKCkgLyAxMDAwKSArIDYwICogNjAgKiAyNCAqIDc7XG4gIH1cblxuICAvKipcbiAgICogTWV0aG9kIHRvIGdldCB0aGUgY3VzdG9tIGNoYWluIGNvbW1vbiBvYmplY3QgYmFzZWQgb24gcGFyYW1zIGZyb20gcmVjb3ZlcnlcbiAgICogQHBhcmFtIHtudW1iZXJ9IGNoYWluSWQgLSB0aGUgY2hhaW4gaWQgb2YgdGhlIGN1c3RvbSBjaGFpblxuICAgKiBAcmV0dXJucyB7RXRoTGlrZUNvbW1vbi5kZWZhdWx0fVxuICAgKi9cbiAgc3RhdGljIGdldEN1c3RvbUNoYWluQ29tbW9uKGNoYWluSWQ6IG51bWJlcik6IEV0aExpa2VDb21tb24uZGVmYXVsdCB7XG4gICAgY29uc3QgY29pbk5hbWUgPSBDb2luTWFwLmNvaW5OYW1lRnJvbUNoYWluSWQoY2hhaW5JZCk7XG4gICAgY29uc3QgY29pbiA9IGNvaW5zLmdldChjb2luTmFtZSk7XG4gICAgY29uc3QgZXRoTGlrZUNvbW1vbiA9IGdldENvbW1vbihjb2luLm5ldHdvcmsgYXMgRXRoTGlrZU5ldHdvcmspO1xuICAgIHJldHVybiBldGhMaWtlQ29tbW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgY29ycmVjdCBFdGggQ29tbW9uIG9iamVjdCBiYXNlZCBvbiBwYXJhbXMgZnJvbSBlaXRoZXIgcmVjb3Zlcnkgb3IgdHggYnVpbGRpbmdcbiAgICogQHBhcmFtIHtFSVAxNTU5fSBlaXAxNTU5IC0gY29uZmlncyB0aGF0IHNwZWNpZnkgd2hldGhlciB3ZSBzaG91bGQgY29uc3RydWN0IGFuIGVpcDE1NTkgdHhcbiAgICogQHBhcmFtIHtSZXBsYXlQcm90ZWN0aW9uT3B0aW9uc30gcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnMgLSBjaGVjayBpZiBjaGFpbiBpZCBzdXBwb3J0cyByZXBsYXkgcHJvdGVjdGlvblxuICAgKiBAcmV0dXJucyB7RXRoTGlrZUNvbW1vbi5kZWZhdWx0fVxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgZ2V0RXRoTGlrZUNvbW1vbihcbiAgICBlaXAxNTU5PzogRUlQMTU1OSxcbiAgICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zXG4gICk6IEV0aExpa2VDb21tb24uZGVmYXVsdCB7XG4gICAgLy8gaWYgZWlwMTU1OSBwYXJhbXMgYXJlIHNwZWNpZmllZCwgZGVmYXVsdCB0byBsb25kb24gaGFyZGZvcmssIG90aGVyd2lzZSxcbiAgICAvLyBkZWZhdWx0IHRvIHRhbmdlcmluZSB3aGlzdGxlIHRvIGF2b2lkIHJlcGxheSBwcm90ZWN0aW9uIGlzc3Vlc1xuICAgIGNvbnN0IGRlZmF1bHRIYXJkZm9yayA9ICEhZWlwMTU1OSA/ICdsb25kb24nIDogb3B0aW9uYWxEZXBzLkV0aENvbW1vbi5IYXJkZm9yay5UYW5nZXJpbmVXaGlzdGxlO1xuICAgIGNvbnN0IGV0aExpa2VDb21tb24gPSBBYnN0cmFjdEV0aExpa2VOZXdDb2lucy5nZXRDdXN0b21DaGFpbkNvbW1vbihyZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz8uY2hhaW4gYXMgbnVtYmVyKTtcbiAgICBldGhMaWtlQ29tbW9uLnNldEhhcmRmb3JrKHJlcGxheVByb3RlY3Rpb25PcHRpb25zPy5oYXJkZm9yayA/PyBkZWZhdWx0SGFyZGZvcmspO1xuICAgIHJldHVybiBldGhMaWtlQ29tbW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIE1ldGhvZCB0byBidWlsZCB0aGUgdHggb2JqZWN0XG4gICAqIEBwYXJhbSB7QnVpbGRUcmFuc2FjdGlvblBhcmFtc30gcGFyYW1zIC0gcGFyYW1zIHRvIGJ1aWxkIHRyYW5zYWN0aW9uXG4gICAqIEByZXR1cm5zIHtFdGhMaWtlVHhMaWIuRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uIHwgRXRoTGlrZVR4TGliLlRyYW5zYWN0aW9ufVxuICAgKi9cbiAgc3RhdGljIGJ1aWxkVHJhbnNhY3Rpb24oXG4gICAgcGFyYW1zOiBCdWlsZFRyYW5zYWN0aW9uUGFyYW1zXG4gICk6IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24gfCBFdGhMaWtlVHhMaWIuVHJhbnNhY3Rpb24ge1xuICAgIC8vIGlmIGVpcDE1NTkgcGFyYW1zIGFyZSBzcGVjaWZpZWQsIGRlZmF1bHQgdG8gbG9uZG9uIGhhcmRmb3JrLCBvdGhlcndpc2UsXG4gICAgLy8gZGVmYXVsdCB0byB0YW5nZXJpbmUgd2hpc3RsZSB0byBhdm9pZCByZXBsYXkgcHJvdGVjdGlvbiBpc3N1ZXNcbiAgICBjb25zdCBldGhMaWtlQ29tbW9uID0gQWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuZ2V0RXRoTGlrZUNvbW1vbihwYXJhbXMuZWlwMTU1OSwgcGFyYW1zLnJlcGxheVByb3RlY3Rpb25PcHRpb25zKTtcbiAgICBjb25zdCBiYXNlUGFyYW1zID0ge1xuICAgICAgdG86IHBhcmFtcy50byxcbiAgICAgIG5vbmNlOiBwYXJhbXMubm9uY2UsXG4gICAgICB2YWx1ZTogcGFyYW1zLnZhbHVlLFxuICAgICAgZGF0YTogcGFyYW1zLmRhdGEsXG4gICAgICBnYXNMaW1pdDogbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHBhcmFtcy5nYXNMaW1pdCksXG4gICAgfTtcblxuICAgIGNvbnN0IHVuc2lnbmVkRXRoVHggPSAhIXBhcmFtcy5laXAxNTU5XG4gICAgICA/IG9wdGlvbmFsRGVwcy5FdGhUeC5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24uZnJvbVR4RGF0YShcbiAgICAgICAgICB7XG4gICAgICAgICAgICAuLi5iYXNlUGFyYW1zLFxuICAgICAgICAgICAgbWF4RmVlUGVyR2FzOiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocGFyYW1zLmVpcDE1NTkubWF4RmVlUGVyR2FzKSxcbiAgICAgICAgICAgIG1heFByaW9yaXR5RmVlUGVyR2FzOiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocGFyYW1zLmVpcDE1NTkubWF4UHJpb3JpdHlGZWVQZXJHYXMpLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgeyBjb21tb246IGV0aExpa2VDb21tb24gfVxuICAgICAgICApXG4gICAgICA6IG9wdGlvbmFsRGVwcy5FdGhUeC5UcmFuc2FjdGlvbi5mcm9tVHhEYXRhKFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIC4uLmJhc2VQYXJhbXMsXG4gICAgICAgICAgICBnYXNQcmljZTogbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHBhcmFtcy5nYXNQcmljZSksXG4gICAgICAgICAgfSxcbiAgICAgICAgICB7IGNvbW1vbjogZXRoTGlrZUNvbW1vbiB9XG4gICAgICAgICk7XG5cbiAgICByZXR1cm4gdW5zaWduZWRFdGhUeDtcbiAgfVxuXG4gIC8qKlxuICAgKiBRdWVyeSBleHBsb3JlciBmb3IgdGhlIGJhbGFuY2Ugb2YgYW4gYWRkcmVzc1xuICAgKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzcyAtIHRoZSBFVEhMaWtlIGFkZHJlc3NcbiAgICogQHJldHVybnMge0JpZ051bWJlcn0gYWRkcmVzcyBiYWxhbmNlXG4gICAqL1xuICBhc3luYyBxdWVyeUFkZHJlc3NCYWxhbmNlKGFkZHJlc3M6IHN0cmluZyk6IFByb21pc2U8YW55PiB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5yZWNvdmVyeUJsb2NrY2hhaW5FeHBsb3JlclF1ZXJ5KHtcbiAgICAgIG1vZHVsZTogJ2FjY291bnQnLFxuICAgICAgYWN0aW9uOiAnYmFsYW5jZScsXG4gICAgICBhZGRyZXNzOiBhZGRyZXNzLFxuICAgIH0pO1xuICAgIC8vIHRocm93IGlmIHRoZSByZXN1bHQgZG9lcyBub3QgZXhpc3Qgb3IgdGhlIHJlc3VsdCBpcyBub3QgYSB2YWxpZCBudW1iZXJcbiAgICBpZiAoIXJlc3VsdCB8fCAhcmVzdWx0LnJlc3VsdCB8fCBpc05hTihyZXN1bHQucmVzdWx0KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBDb3VsZCBub3Qgb2J0YWluIGFkZHJlc3MgYmFsYW5jZSBmb3IgJHthZGRyZXNzfSBmcm9tIHRoZSBleHBsb3JlciwgZ290OiAke3Jlc3VsdC5yZXN1bHR9YCk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocmVzdWx0LnJlc3VsdCwgMTApO1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7UmVjaXBpZW50W119IHJlY2lwaWVudHMgLSB0aGUgcmVjaXBpZW50cyBvZiB0aGUgdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtudW1iZXJ9IGV4cGlyZVRpbWUgLSB0aGUgZXhwaXJlIHRpbWUgb2YgdGhlIHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBjb250cmFjdFNlcXVlbmNlSWQgLSB0aGUgY29udHJhY3Qgc2VxdWVuY2UgaWQgb2YgdGhlIHRyYW5zYWN0aW9uXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICAqL1xuICBnZXRPcGVyYXRpb25TaGEzRm9yRXhlY3V0ZUFuZENvbmZpcm0oXG4gICAgcmVjaXBpZW50czogUmVjaXBpZW50W10sXG4gICAgZXhwaXJlVGltZTogbnVtYmVyLFxuICAgIGNvbnRyYWN0U2VxdWVuY2VJZDogbnVtYmVyXG4gICk6IHN0cmluZyB7XG4gICAgaWYgKCFyZWNpcGllbnRzIHx8ICFBcnJheS5pc0FycmF5KHJlY2lwaWVudHMpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2V4cGVjdGluZyBhcnJheSBvZiByZWNpcGllbnRzJyk7XG4gICAgfVxuXG4gICAgLy8gUmlnaHQgbm93IHdlIG9ubHkgc3VwcG9ydCAxIHJlY2lwaWVudFxuICAgIGlmIChyZWNpcGllbnRzLmxlbmd0aCAhPT0gMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtdXN0IHNlbmQgdG8gZXhhY3RseSAxIHJlY2lwaWVudCcpO1xuICAgIH1cblxuICAgIGlmICghXy5pc051bWJlcihleHBpcmVUaW1lKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdleHBpcmVUaW1lIG11c3QgYmUgbnVtYmVyIG9mIHNlY29uZHMgc2luY2UgZXBvY2gnKTtcbiAgICB9XG5cbiAgICBpZiAoIV8uaXNOdW1iZXIoY29udHJhY3RTZXF1ZW5jZUlkKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdjb250cmFjdFNlcXVlbmNlSWQgbXVzdCBiZSBudW1iZXInKTtcbiAgICB9XG5cbiAgICAvLyBDaGVjayBpbnB1dHNcbiAgICByZWNpcGllbnRzLmZvckVhY2goZnVuY3Rpb24gKHJlY2lwaWVudCkge1xuICAgICAgaWYgKFxuICAgICAgICAhXy5pc1N0cmluZyhyZWNpcGllbnQuYWRkcmVzcykgfHxcbiAgICAgICAgIW9wdGlvbmFsRGVwcy5ldGhVdGlsLmlzVmFsaWRBZGRyZXNzKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmFkZEhleFByZWZpeChyZWNpcGllbnQuYWRkcmVzcykpXG4gICAgICApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGFkZHJlc3M6ICcgKyByZWNpcGllbnQuYWRkcmVzcyk7XG4gICAgICB9XG5cbiAgICAgIGxldCBhbW91bnQ6IEJpZ051bWJlcjtcbiAgICAgIHRyeSB7XG4gICAgICAgIGFtb3VudCA9IG5ldyBCaWdOdW1iZXIocmVjaXBpZW50LmFtb3VudCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBhbW91bnQgZm9yOiAnICsgcmVjaXBpZW50LmFkZHJlc3MgKyAnIC0gc2hvdWxkIGJlIG51bWVyaWMnKTtcbiAgICAgIH1cblxuICAgICAgcmVjaXBpZW50LmFtb3VudCA9IGFtb3VudC50b0ZpeGVkKDApO1xuXG4gICAgICBpZiAocmVjaXBpZW50LmRhdGEgJiYgIV8uaXNTdHJpbmcocmVjaXBpZW50LmRhdGEpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignRGF0YSBmb3IgcmVjaXBpZW50ICcgKyByZWNpcGllbnQuYWRkcmVzcyArICcgLSBzaG91bGQgYmUgb2YgdHlwZSBoZXggc3RyaW5nJyk7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBjb25zdCByZWNpcGllbnQgPSByZWNpcGllbnRzWzBdO1xuICAgIHJldHVybiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0hleChcbiAgICAgIG9wdGlvbmFsRGVwcy5ldGhBYmkuc29saWRpdHlTSEEzKC4uLnRoaXMuZ2V0T3BlcmF0aW9uKHJlY2lwaWVudCwgZXhwaXJlVGltZSwgY29udHJhY3RTZXF1ZW5jZUlkKSlcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0cmFuc2ZlciBvcGVyYXRpb24gZm9yIGNvaW5cbiAgICogQHBhcmFtIHtSZWNpcGllbnR9IHJlY2lwaWVudCAtIHJlY2lwaWVudCBpbmZvXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBleHBpcmVUaW1lIC0gZXhwaXJ5IHRpbWVcbiAgICogQHBhcmFtIHtudW1iZXJ9IGNvbnRyYWN0U2VxdWVuY2VJZCAtIHNlcXVlbmNlIGlkXG4gICAqIEByZXR1cm5zIHtBcnJheX0gb3BlcmF0aW9uIGFycmF5XG4gICAqL1xuICBnZXRPcGVyYXRpb24ocmVjaXBpZW50OiBSZWNpcGllbnQsIGV4cGlyZVRpbWU6IG51bWJlciwgY29udHJhY3RTZXF1ZW5jZUlkOiBudW1iZXIpOiAoc3RyaW5nIHwgQnVmZmVyKVtdW10ge1xuICAgIGNvbnN0IG5ldHdvcmsgPSB0aGlzLmdldE5ldHdvcmsoKSBhcyBFdGhMaWtlTmV0d29yaztcbiAgICByZXR1cm4gW1xuICAgICAgWydzdHJpbmcnLCAnYWRkcmVzcycsICd1aW50JywgJ2J5dGVzJywgJ3VpbnQnLCAndWludCddLFxuICAgICAgW1xuICAgICAgICBuZXR3b3JrLm5hdGl2ZUNvaW5PcGVyYXRpb25IYXNoUHJlZml4LFxuICAgICAgICBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ob3B0aW9uYWxEZXBzLmV0aFV0aWwuc3RyaXBIZXhQcmVmaXgocmVjaXBpZW50LmFkZHJlc3MpLCAxNiksXG4gICAgICAgIHJlY2lwaWVudC5hbW91bnQsXG4gICAgICAgIEJ1ZmZlci5mcm9tKG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KG9wdGlvbmFsRGVwcy5ldGhVdGlsLnBhZFRvRXZlbihyZWNpcGllbnQuZGF0YSB8fCAnJykpLCAnaGV4JyksXG4gICAgICAgIGV4cGlyZVRpbWUsXG4gICAgICAgIGNvbnRyYWN0U2VxdWVuY2VJZCxcbiAgICAgIF0sXG4gICAgXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBRdWVyaWVzIHRoZSBjb250cmFjdCAodmlhIGV4cGxvcmVyIEFQSSkgZm9yIHRoZSBuZXh0IHNlcXVlbmNlIElEXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhZGRyZXNzIC0gYWRkcmVzcyBvZiB0aGUgY29udHJhY3RcbiAgICogQHJldHVybnMge1Byb21pc2U8TnVtYmVyPn0gc2VxdWVuY2UgSURcbiAgICovXG4gIGFzeW5jIHF1ZXJ5U2VxdWVuY2VJZChhZGRyZXNzOiBzdHJpbmcpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIC8vIEdldCBzZXF1ZW5jZSBJRCB1c2luZyBjb250cmFjdCBjYWxsXG4gICAgY29uc3Qgc2VxdWVuY2VJZE1ldGhvZFNpZ25hdHVyZSA9IG9wdGlvbmFsRGVwcy5ldGhBYmkubWV0aG9kSUQoJ2dldE5leHRTZXF1ZW5jZUlkJywgW10pO1xuICAgIGNvbnN0IHNlcXVlbmNlSWRBcmdzID0gb3B0aW9uYWxEZXBzLmV0aEFiaS5yYXdFbmNvZGUoW10sIFtdKTtcbiAgICBjb25zdCBzZXF1ZW5jZUlkRGF0YSA9IEJ1ZmZlci5jb25jYXQoW3NlcXVlbmNlSWRNZXRob2RTaWduYXR1cmUsIHNlcXVlbmNlSWRBcmdzXSkudG9TdHJpbmcoJ2hleCcpO1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMucmVjb3ZlcnlCbG9ja2NoYWluRXhwbG9yZXJRdWVyeSh7XG4gICAgICBtb2R1bGU6ICdwcm94eScsXG4gICAgICBhY3Rpb246ICdldGhfY2FsbCcsXG4gICAgICB0bzogYWRkcmVzcyxcbiAgICAgIGRhdGE6IHNlcXVlbmNlSWREYXRhLFxuICAgICAgdGFnOiAnbGF0ZXN0JyxcbiAgICB9KTtcbiAgICBpZiAoIXJlc3VsdCB8fCAhcmVzdWx0LnJlc3VsdCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3Qgb2J0YWluIHNlcXVlbmNlIElEIGZyb20gZXhwbG9yZXIsIGdvdDogJyArIHJlc3VsdC5yZXN1bHQpO1xuICAgIH1cbiAgICBjb25zdCBzZXF1ZW5jZUlkSGV4ID0gcmVzdWx0LnJlc3VsdDtcbiAgICByZXR1cm4gbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHNlcXVlbmNlSWRIZXguc2xpY2UoMiksIDE2KS50b051bWJlcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlY292ZXIgYW4gdW5zdXBwb3J0ZWQgdG9rZW4gZnJvbSBhIEJpdEdvIG11bHRpc2lnIHdhbGxldFxuICAgKiBUaGlzIGJ1aWxkcyBhIGhhbGYtc2lnbmVkIHRyYW5zYWN0aW9uLCBmb3Igd2hpY2ggdGhlcmUgd2lsbCBiZSBhbiBhZG1pbiByb3V0ZSB0byBjby1zaWduIGFuZCBicm9hZGNhc3QuIE9wdGlvbmFsbHlcbiAgICogdGhlIHVzZXIgY2FuIHNldCBwYXJhbXMuYnJvYWRjYXN0ID0gdHJ1ZSBhbmQgdGhlIGhhbGYtc2lnbmVkIHR4IHdpbGwgYmUgc2VudCB0byBCaXRHbyBmb3IgY29zaWduaW5nIGFuZCBicm9hZGNhc3RpbmdcbiAgICogQHBhcmFtIHtSZWNvdmVyVG9rZW5PcHRpb25zfSBwYXJhbXNcbiAgICogQHBhcmFtIHtXYWxsZXR9IHBhcmFtcy53YWxsZXQgLSB0aGUgd2FsbGV0IHRvIHJlY292ZXIgdGhlIHRva2VuIGZyb21cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcyAtIHRoZSBjb250cmFjdCBhZGRyZXNzIG9mIHRoZSB1bnN1cHBvcnRlZCB0b2tlblxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLnJlY2lwaWVudCAtIHRoZSBkZXN0aW5hdGlvbiBhZGRyZXNzIHJlY292ZXJlZCB0b2tlbnMgc2hvdWxkIGJlIHNlbnQgdG9cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlIC0gdGhlIHdhbGxldCBwYXNzcGhyYXNlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMucHJ2IC0gdGhlIHhwcnZcbiAgICogQHBhcmFtIHtib29sZWFufSBwYXJhbXMuYnJvYWRjYXN0IC0gaWYgdHJ1ZSwgd2Ugd2lsbCBhdXRvbWF0aWNhbGx5IHN1Ym1pdCB0aGUgaGFsZi1zaWduZWQgdHggdG8gQml0R28gZm9yIGNvc2lnbmluZyBhbmQgYnJvYWRjYXN0aW5nXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFJlY292ZXJUb2tlblRyYW5zYWN0aW9uPn1cbiAgICovXG4gIGFzeW5jIHJlY292ZXJUb2tlbihwYXJhbXM6IFJlY292ZXJUb2tlbk9wdGlvbnMpOiBQcm9taXNlPFJlY292ZXJUb2tlblRyYW5zYWN0aW9uPiB7XG4gICAgY29uc3QgbmV0d29yayA9IHRoaXMuZ2V0TmV0d29yaygpIGFzIEV0aExpa2VOZXR3b3JrO1xuICAgIGlmICghXy5pc09iamVjdChwYXJhbXMpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYHJlY292ZXJUb2tlbiBtdXN0IGJlIHBhc3NlZCBhIHBhcmFtcyBvYmplY3QuIEdvdCAke3BhcmFtc30gKHR5cGUgJHt0eXBlb2YgcGFyYW1zfSlgKTtcbiAgICB9XG5cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3MpIHx8ICFfLmlzU3RyaW5nKHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYHRva2VuQ29udHJhY3RBZGRyZXNzIG11c3QgYmUgYSBzdHJpbmcsIGdvdCAke1xuICAgICAgICAgIHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzc1xuICAgICAgICB9ICh0eXBlICR7dHlwZW9mIHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzc30pYFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuaXNWYWxpZEFkZHJlc3MocGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd0b2tlbkNvbnRyYWN0QWRkcmVzcyBub3QgYSB2YWxpZCBhZGRyZXNzJyk7XG4gICAgfVxuXG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLndhbGxldCkgfHwgIShwYXJhbXMud2FsbGV0IGluc3RhbmNlb2YgV2FsbGV0KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGB3YWxsZXQgbXVzdCBiZSBhIHdhbGxldCBpbnN0YW5jZSwgZ290ICR7cGFyYW1zLndhbGxldH0gKHR5cGUgJHt0eXBlb2YgcGFyYW1zLndhbGxldH0pYCk7XG4gICAgfVxuXG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLnJlY2lwaWVudCkgfHwgIV8uaXNTdHJpbmcocGFyYW1zLnJlY2lwaWVudCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgcmVjaXBpZW50IG11c3QgYmUgYSBzdHJpbmcsIGdvdCAke3BhcmFtcy5yZWNpcGllbnR9ICh0eXBlICR7dHlwZW9mIHBhcmFtcy5yZWNpcGllbnR9KWApO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5pc1ZhbGlkQWRkcmVzcyhwYXJhbXMucmVjaXBpZW50KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdyZWNpcGllbnQgbm90IGEgdmFsaWQgYWRkcmVzcycpO1xuICAgIH1cblxuICAgIGlmICghb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9IZXggfHwgIW9wdGlvbmFsRGVwcy5ldGhBYmkuc29saWRpdHlTSEEzKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2V0aGVyZXVtIG5vdCBmdWxseSBzdXBwb3J0ZWQgaW4gdGhpcyBlbnZpcm9ubWVudCcpO1xuICAgIH1cblxuICAgIC8vIEdldCB0b2tlbiBiYWxhbmNlIGZyb20gZXh0ZXJuYWwgQVBJXG4gICAgY29uc3QgY29pblNwZWNpZmljID0gcGFyYW1zLndhbGxldC5jb2luU3BlY2lmaWMoKTtcbiAgICBpZiAoIWNvaW5TcGVjaWZpYyB8fCAhXy5pc1N0cmluZyhjb2luU3BlY2lmaWMuYmFzZUFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3NpbmcgcmVxdWlyZWQgY29pbiBzcGVjaWZpYyBwcm9wZXJ0eSBiYXNlQWRkcmVzcycpO1xuICAgIH1cbiAgICBjb25zdCByZWNvdmVyeUFtb3VudCA9IGF3YWl0IHRoaXMucXVlcnlBZGRyZXNzVG9rZW5CYWxhbmNlKHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcywgY29pblNwZWNpZmljLmJhc2VBZGRyZXNzKTtcblxuICAgIGlmIChwYXJhbXMuYnJvYWRjYXN0KSB7XG4gICAgICAvLyBXZSdyZSBnb2luZyB0byBjcmVhdGUgYSBub3JtYWwgRVRIIHRyYW5zYWN0aW9uIHRoYXQgc2VuZHMgYW4gYW1vdW50IG9mIDAgRVRIIHRvIHRoZVxuICAgICAgLy8gdG9rZW5Db250cmFjdEFkZHJlc3MgYW5kIGVuY29kZSB0aGUgdW5zdXBwb3J0ZWQtdG9rZW4tc2VuZCBkYXRhIGluIHRoZSBkYXRhIGZpZWxkXG4gICAgICAvLyAjdHJpY2tzeVxuICAgICAgY29uc3Qgc2VuZE1ldGhvZEFyZ3MgPSBbXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiAnX3RvJyxcbiAgICAgICAgICB0eXBlOiAnYWRkcmVzcycsXG4gICAgICAgICAgdmFsdWU6IHBhcmFtcy5yZWNpcGllbnQsXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiAnX3ZhbHVlJyxcbiAgICAgICAgICB0eXBlOiAndWludDI1NicsXG4gICAgICAgICAgdmFsdWU6IHJlY292ZXJ5QW1vdW50LnRvU3RyaW5nKDEwKSxcbiAgICAgICAgfSxcbiAgICAgIF07XG4gICAgICBjb25zdCBtZXRob2RTaWduYXR1cmUgPSBvcHRpb25hbERlcHMuZXRoQWJpLm1ldGhvZElEKCd0cmFuc2ZlcicsIF8ubWFwKHNlbmRNZXRob2RBcmdzLCAndHlwZScpKTtcbiAgICAgIGNvbnN0IGVuY29kZWRBcmdzID0gb3B0aW9uYWxEZXBzLmV0aEFiaS5yYXdFbmNvZGUoXy5tYXAoc2VuZE1ldGhvZEFyZ3MsICd0eXBlJyksIF8ubWFwKHNlbmRNZXRob2RBcmdzLCAndmFsdWUnKSk7XG4gICAgICBjb25zdCBzZW5kRGF0YSA9IEJ1ZmZlci5jb25jYXQoW21ldGhvZFNpZ25hdHVyZSwgZW5jb2RlZEFyZ3NdKTtcblxuICAgICAgY29uc3QgYnJvYWRjYXN0UGFyYW1zOiBhbnkgPSB7XG4gICAgICAgIGFkZHJlc3M6IHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcyxcbiAgICAgICAgYW1vdW50OiAnMCcsXG4gICAgICAgIGRhdGE6IHNlbmREYXRhLnRvU3RyaW5nKCdoZXgnKSxcbiAgICAgIH07XG5cbiAgICAgIGlmIChwYXJhbXMud2FsbGV0UGFzc3BocmFzZSkge1xuICAgICAgICBicm9hZGNhc3RQYXJhbXMud2FsbGV0UGFzc3BocmFzZSA9IHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlO1xuICAgICAgfSBlbHNlIGlmIChwYXJhbXMucHJ2KSB7XG4gICAgICAgIGJyb2FkY2FzdFBhcmFtcy5wcnYgPSBwYXJhbXMucHJ2O1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gYXdhaXQgcGFyYW1zLndhbGxldC5zZW5kKGJyb2FkY2FzdFBhcmFtcyk7XG4gICAgfVxuXG4gICAgY29uc3QgcmVjaXBpZW50ID0ge1xuICAgICAgYWRkcmVzczogcGFyYW1zLnJlY2lwaWVudCxcbiAgICAgIGFtb3VudDogcmVjb3ZlcnlBbW91bnQudG9TdHJpbmcoMTApLFxuICAgIH07XG5cbiAgICAvLyBUaGlzIHNpZ25hdHVyZSB3aWxsIGJlIHZhbGlkIGZvciBvbmUgd2Vla1xuICAgIGNvbnN0IGV4cGlyZVRpbWUgPSBNYXRoLmZsb29yKG5ldyBEYXRlKCkuZ2V0VGltZSgpIC8gMTAwMCkgKyA2MCAqIDYwICogMjQgKiA3O1xuXG4gICAgLy8gR2V0IHNlcXVlbmNlIElELiBXZSBkbyB0aGlzIGJ5IGJ1aWxkaW5nIGEgJ2Zha2UnIGV0aCB0cmFuc2FjdGlvbiwgc28gdGhlIHBsYXRmb3JtIHdpbGwgaW5jcmVtZW50IGFuZCByZXR1cm4gdXMgdGhlIG5ldyBzZXF1ZW5jZSBpZFxuICAgIC8vIFRoaXMgX2RvZXNfIHJlcXVpcmUgdGhlIHVzZXIgdG8gaGF2ZSBhIG5vbi16ZXJvIHdhbGxldCBiYWxhbmNlXG4gICAgY29uc3QgeyBuZXh0Q29udHJhY3RTZXF1ZW5jZUlkLCBnYXNQcmljZSwgZ2FzTGltaXQgfSA9IChhd2FpdCBwYXJhbXMud2FsbGV0LnByZWJ1aWxkVHJhbnNhY3Rpb24oe1xuICAgICAgcmVjaXBpZW50czogW1xuICAgICAgICB7XG4gICAgICAgICAgYWRkcmVzczogcGFyYW1zLnJlY2lwaWVudCxcbiAgICAgICAgICBhbW91bnQ6ICcxJyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSkpIGFzIGFueTtcblxuICAgIC8vIHRoZXNlIHJlY292ZXJpZXMgbmVlZCB0byBiZSBwcm9jZXNzZWQgYnkgc3VwcG9ydCwgYnV0IGlmIHRoZSBjdXN0b21lciBzZW5kcyBhbnkgdHJhbnNhY3Rpb25zIGJlZm9yZSByZWNvdmVyeSBpc1xuICAgIC8vIGNvbXBsZXRlIHRoZSBzZXF1ZW5jZSBJRCB3aWxsIGJlIGludmFsaWQuIGFydGlmaWNpYWxseSBpbmZsYXRlIHRoZSBzZXF1ZW5jZSBJRCB0byBhbGxvdyBtb3JlIHRpbWUgZm9yIHByb2Nlc3NpbmdcbiAgICBjb25zdCBzYWZlU2VxdWVuY2VJZCA9IG5leHRDb250cmFjdFNlcXVlbmNlSWQgKyAxMDAwO1xuXG4gICAgLy8gQnVpbGQgc2VuZERhdGEgZm9yIGV0aGVyZXVtIHR4XG4gICAgY29uc3Qgb3BlcmF0aW9uVHlwZXMgPSBbJ3N0cmluZycsICdhZGRyZXNzJywgJ3VpbnQnLCAnYWRkcmVzcycsICd1aW50JywgJ3VpbnQnXTtcbiAgICBjb25zdCBvcGVyYXRpb25BcmdzID0gW1xuICAgICAgLy8gVG9rZW4gb3BlcmF0aW9uIGhhcyBwcmVmaXggaGFzIGJlZW4gYWRkZWQgaGVyZSBzbyB0aGF0IGV0aGVyIG9wZXJhdGlvbiBoYXNoZXMsIHNpZ25hdHVyZXMgY2Fubm90IGJlIHJlLXVzZWQgZm9yIHRva2VuU2VuZGluZ1xuICAgICAgbmV0d29yay50b2tlbk9wZXJhdGlvbkhhc2hQcmVmaXgsXG4gICAgICBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ob3B0aW9uYWxEZXBzLmV0aFV0aWwuc3RyaXBIZXhQcmVmaXgocmVjaXBpZW50LmFkZHJlc3MpLCAxNiksXG4gICAgICByZWNpcGllbnQuYW1vdW50LFxuICAgICAgbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcyksIDE2KSxcbiAgICAgIGV4cGlyZVRpbWUsXG4gICAgICBzYWZlU2VxdWVuY2VJZCxcbiAgICBdO1xuXG4gICAgY29uc3Qgb3BlcmF0aW9uSGFzaCA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSGV4KFxuICAgICAgb3B0aW9uYWxEZXBzLmV0aEFiaS5zb2xpZGl0eVNIQTMob3BlcmF0aW9uVHlwZXMsIG9wZXJhdGlvbkFyZ3MpXG4gICAgKTtcblxuICAgIGNvbnN0IHVzZXJQcnYgPSBhd2FpdCBwYXJhbXMud2FsbGV0LmdldFBydih7XG4gICAgICBwcnY6IHBhcmFtcy5wcnYsXG4gICAgICB3YWxsZXRQYXNzcGhyYXNlOiBwYXJhbXMud2FsbGV0UGFzc3BocmFzZSxcbiAgICB9KTtcblxuICAgIGNvbnN0IHNpZ25hdHVyZSA9IFV0aWwuZXRoU2lnbk1zZ0hhc2gob3BlcmF0aW9uSGFzaCwgVXRpbC54cHJ2VG9FdGhQcml2YXRlS2V5KHVzZXJQcnYpKTtcblxuICAgIHJldHVybiB7XG4gICAgICBoYWxmU2lnbmVkOiB7XG4gICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LFxuICAgICAgICBleHBpcmVUaW1lOiBleHBpcmVUaW1lLFxuICAgICAgICBjb250cmFjdFNlcXVlbmNlSWQ6IHNhZmVTZXF1ZW5jZUlkLFxuICAgICAgICBvcGVyYXRpb25IYXNoOiBvcGVyYXRpb25IYXNoLFxuICAgICAgICBzaWduYXR1cmU6IHNpZ25hdHVyZSxcbiAgICAgICAgZ2FzTGltaXQ6IGdhc0xpbWl0LFxuICAgICAgICBnYXNQcmljZTogZ2FzUHJpY2UsXG4gICAgICAgIHRva2VuQ29udHJhY3RBZGRyZXNzOiBwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3MsXG4gICAgICAgIHdhbGxldElkOiBwYXJhbXMud2FsbGV0LmlkKCksXG4gICAgICB9LFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogRW5zdXJlIGVpdGhlciBlbnRlcnByaXNlIG9yIG5ld0ZlZUFkZHJlc3MgaXMgcGFzc2VkLCB0byBrbm93IHdoZXRoZXIgdG8gY3JlYXRlIG5ldyBrZXkgb3IgdXNlIGVudGVycHJpc2Uga2V5XG4gICAqIEBwYXJhbSB7UHJlY3JlYXRlQml0R29PcHRpb25zfSBwYXJhbXNcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5lbnRlcnByaXNlIHtTdHJpbmd9IHRoZSBlbnRlcnByaXNlIGlkIHRvIGFzc29jaWF0ZSB3aXRoIHRoaXMga2V5XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMubmV3RmVlQWRkcmVzcyB7Qm9vbGVhbn0gY3JlYXRlIGEgbmV3IGZlZSBhZGRyZXNzIChlbnRlcnByaXNlIG5vdCBuZWVkZWQgaW4gdGhpcyBjYXNlKVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIHByZUNyZWF0ZUJpdEdvKHBhcmFtczogUHJlY3JlYXRlQml0R29PcHRpb25zKTogdm9pZCB7XG4gICAgLy8gV2UgYWx3YXlzIG5lZWQgcGFyYW1zIG9iamVjdCwgc2luY2UgZWl0aGVyIGVudGVycHJpc2Ugb3IgbmV3RmVlQWRkcmVzcyBpcyByZXF1aXJlZFxuICAgIGlmICghXy5pc09iamVjdChwYXJhbXMpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYHByZUNyZWF0ZUJpdEdvIG11c3QgYmUgcGFzc2VkIGEgcGFyYW1zIG9iamVjdC4gR290ICR7cGFyYW1zfSAodHlwZSAke3R5cGVvZiBwYXJhbXN9KWApO1xuICAgIH1cblxuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy5lbnRlcnByaXNlKSAmJiBfLmlzVW5kZWZpbmVkKHBhcmFtcy5uZXdGZWVBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnZXhwZWN0aW5nIGVudGVycHJpc2Ugd2hlbiBhZGRpbmcgQml0R28ga2V5LiBJZiB5b3Ugd2FudCB0byBjcmVhdGUgYSBuZXcgRVRIIGJpdGdvIGtleSwgc2V0IHRoZSBuZXdGZWVBZGRyZXNzIHBhcmFtZXRlciB0byB0cnVlLidcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgd2hldGhlciBrZXkgc2hvdWxkIGJlIGFuIGVudGVycHJpc2Uga2V5IG9yIGEgQml0R28ga2V5IGZvciBhIG5ldyBmZWUgYWRkcmVzc1xuICAgIGlmICghXy5pc1VuZGVmaW5lZChwYXJhbXMuZW50ZXJwcmlzZSkgJiYgIV8uaXNVbmRlZmluZWQocGFyYW1zLm5ld0ZlZUFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEluY29tcGF0aWJsZSBhcmd1bWVudHMgLSBjYW5ub3QgcGFzcyBib3RoIGVudGVycHJpc2UgYW5kIG5ld0ZlZUFkZHJlc3MgcGFyYW1ldGVyLmApO1xuICAgIH1cblxuICAgIGlmICghXy5pc1VuZGVmaW5lZChwYXJhbXMuZW50ZXJwcmlzZSkgJiYgIV8uaXNTdHJpbmcocGFyYW1zLmVudGVycHJpc2UpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYGVudGVycHJpc2Ugc2hvdWxkIGJlIGEgc3RyaW5nIC0gZ290ICR7cGFyYW1zLmVudGVycHJpc2V9ICh0eXBlICR7dHlwZW9mIHBhcmFtcy5lbnRlcnByaXNlfSlgKTtcbiAgICB9XG5cbiAgICBpZiAoIV8uaXNVbmRlZmluZWQocGFyYW1zLm5ld0ZlZUFkZHJlc3MpICYmICFfLmlzQm9vbGVhbihwYXJhbXMubmV3RmVlQWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYG5ld0ZlZUFkZHJlc3Mgc2hvdWxkIGJlIGEgYm9vbGVhbiAtIGdvdCAke3BhcmFtcy5uZXdGZWVBZGRyZXNzfSAodHlwZSAke3R5cGVvZiBwYXJhbXMubmV3RmVlQWRkcmVzc30pYFxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUXVlcmllcyBwdWJsaWMgYmxvY2sgZXhwbG9yZXIgdG8gZ2V0IHRoZSBuZXh0IEVUSExpa2UgY29pbidzIG5vbmNlIHRoYXQgc2hvdWxkIGJlIHVzZWQgZm9yIHRoZSBnaXZlbiBFVEggYWRkcmVzc1xuICAgKiBAcGFyYW0ge3N0cmluZ30gYWRkcmVzc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxudW1iZXI+fVxuICAgKi9cbiAgYXN5bmMgZ2V0QWRkcmVzc05vbmNlKGFkZHJlc3M6IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgLy8gR2V0IG5vbmNlIGZvciBiYWNrdXAga2V5IChzaG91bGQgYmUgMClcbiAgICBsZXQgbm9uY2UgPSAwO1xuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5yZWNvdmVyeUJsb2NrY2hhaW5FeHBsb3JlclF1ZXJ5KHtcbiAgICAgIG1vZHVsZTogJ2FjY291bnQnLFxuICAgICAgYWN0aW9uOiAndHhsaXN0JyxcbiAgICAgIGFkZHJlc3MsXG4gICAgfSk7XG4gICAgaWYgKCFyZXN1bHQgfHwgIUFycmF5LmlzQXJyYXkocmVzdWx0LnJlc3VsdCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVW5hYmxlIHRvIGZpbmQgbmV4dCBub25jZSBmcm9tIEV0aGVyc2NhbiwgZ290OiAnICsgSlNPTi5zdHJpbmdpZnkocmVzdWx0KSk7XG4gICAgfVxuICAgIGNvbnN0IGJhY2t1cEtleVR4TGlzdCA9IHJlc3VsdC5yZXN1bHQ7XG4gICAgaWYgKGJhY2t1cEtleVR4TGlzdC5sZW5ndGggPiAwKSB7XG4gICAgICAvLyBDYWxjdWxhdGUgbGFzdCBub25jZSB1c2VkXG4gICAgICBjb25zdCBvdXRnb2luZ1R4cyA9IGJhY2t1cEtleVR4TGlzdC5maWx0ZXIoKHR4KSA9PiB0eC5mcm9tID09PSBhZGRyZXNzKTtcbiAgICAgIG5vbmNlID0gb3V0Z29pbmdUeHMubGVuZ3RoO1xuICAgIH1cbiAgICByZXR1cm4gbm9uY2U7XG4gIH1cblxuICAvKipcbiAgICogSGVscGVyIGZ1bmN0aW9uIGZvciByZWNvdmVyKClcbiAgICogVGhpcyB0cmFuc2Zvcm1zIHRoZSB1bnNpZ25lZCB0cmFuc2FjdGlvbiBpbmZvcm1hdGlvbiBpbnRvIGEgZm9ybWF0IHRoZSBCaXRHbyBvZmZsaW5lIHZhdWx0IGV4cGVjdHNcbiAgICogQHBhcmFtIHtVbmZvcm1hdHRlZFR4SW5mb30gdHhJbmZvIC0gdHggaW5mb1xuICAgKiBAcGFyYW0ge0V0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb259IGV0aFR4IC0gdGhlIGV0aGVyZXVtanMgdHggb2JqZWN0XG4gICAqIEBwYXJhbSB7c3RyaW5nfSB1c2VyS2V5IC0gdGhlIHVzZXIncyBrZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IGJhY2t1cEtleSAtIHRoZSBiYWNrdXAga2V5XG4gICAqIEBwYXJhbSB7QnVmZmVyfSBnYXNQcmljZSAtIGdhcyBwcmljZSBmb3IgdGhlIHR4XG4gICAqIEBwYXJhbSB7bnVtYmVyfSBnYXNMaW1pdCAtIGdhcyBsaW1pdCBmb3IgdGhlIHR4XG4gICAqIEBwYXJhbSB7RUlQMTU1OX0gZWlwMTU1OSAtIGVpcDE1NTkgcGFyYW1zXG4gICAqIEBwYXJhbSB7UmVwbGF5UHJvdGVjdGlvbk9wdGlvbnN9IHJlcGxheVByb3RlY3Rpb25PcHRpb25zIC0gcmVwbGF5IHByb3RlY3Rpb24gb3B0aW9uc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPZmZsaW5lVmF1bHRUeEluZm8+fVxuICAgKi9cbiAgYXN5bmMgZm9ybWF0Rm9yT2ZmbGluZVZhdWx0KFxuICAgIHR4SW5mbzogVW5mb3JtYXR0ZWRUeEluZm8sXG4gICAgZXRoVHg6IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24sXG4gICAgdXNlcktleTogc3RyaW5nLFxuICAgIGJhY2t1cEtleTogc3RyaW5nLFxuICAgIGdhc1ByaWNlOiBCdWZmZXIsXG4gICAgZ2FzTGltaXQ6IG51bWJlcixcbiAgICBlaXAxNTU5PzogRUlQMTU1OSxcbiAgICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zXG4gICk6IFByb21pc2U8T2ZmbGluZVZhdWx0VHhJbmZvPiB7XG4gICAgaWYgKCFldGhUeC50bykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdFdGggdHggbXVzdCBoYXZlIGEgYHRvYCBhZGRyZXNzJyk7XG4gICAgfVxuICAgIGNvbnN0IGJhY2t1cEhETm9kZSA9IGJpcDMyLmZyb21CYXNlNTgoYmFja3VwS2V5KTtcbiAgICBjb25zdCBiYWNrdXBTaWduaW5nS2V5ID0gYmFja3VwSEROb2RlLnB1YmxpY0tleTtcbiAgICBjb25zdCByZXNwb25zZTogT2ZmbGluZVZhdWx0VHhJbmZvID0ge1xuICAgICAgdHg6IGV0aFR4LnNlcmlhbGl6ZSgpLnRvU3RyaW5nKCdoZXgnKSxcbiAgICAgIHVzZXJLZXksXG4gICAgICBiYWNrdXBLZXksXG4gICAgICBjb2luOiB0aGlzLmdldENoYWluKCksXG4gICAgICBnYXNQcmljZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKSxcbiAgICAgIGdhc0xpbWl0LFxuICAgICAgcmVjaXBpZW50czogW3R4SW5mby5yZWNpcGllbnRdLFxuICAgICAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiBldGhUeC50by50b1N0cmluZygpLFxuICAgICAgYW1vdW50OiB0eEluZm8ucmVjaXBpZW50LmFtb3VudCBhcyBzdHJpbmcsXG4gICAgICBiYWNrdXBLZXlOb25jZTogYXdhaXQgdGhpcy5nZXRBZGRyZXNzTm9uY2UoXG4gICAgICAgIGAweCR7b3B0aW9uYWxEZXBzLmV0aFV0aWwucHVibGljVG9BZGRyZXNzKGJhY2t1cFNpZ25pbmdLZXksIHRydWUpLnRvU3RyaW5nKCdoZXgnKX1gXG4gICAgICApLFxuICAgICAgZWlwMTU1OSxcbiAgICAgIHJlcGxheVByb3RlY3Rpb25PcHRpb25zLFxuICAgIH07XG4gICAgXy5leHRlbmQocmVzcG9uc2UsIHR4SW5mbyk7XG4gICAgcmVzcG9uc2UubmV4dENvbnRyYWN0U2VxdWVuY2VJZCA9IHJlc3BvbnNlLmNvbnRyYWN0U2VxdWVuY2VJZDtcbiAgICByZXR1cm4gcmVzcG9uc2U7XG4gIH1cblxuICAvKipcbiAgICogSGVscGVyIGZ1bmN0aW9uIGZvciByZWNvdmVyKClcbiAgICogVGhpcyB0cmFuc2Zvcm1zIHRoZSB1bnNpZ25lZCB0cmFuc2FjdGlvbiBpbmZvcm1hdGlvbiBpbnRvIGEgZm9ybWF0IHRoZSBCaXRHbyBvZmZsaW5lIHZhdWx0IGV4cGVjdHNcbiAgICogQHBhcmFtIHtVbmZvcm1hdHRlZFR4SW5mb30gdHhJbmZvIC0gdHggaW5mb1xuICAgKiBAcGFyYW0ge0V0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb259IGV0aFR4IC0gdGhlIGV0aGVyZXVtanMgdHggb2JqZWN0XG4gICAqIEBwYXJhbSB7c3RyaW5nfSB1c2VyS2V5IC0gdGhlIHVzZXIncyBrZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IGJhY2t1cEtleSAtIHRoZSBiYWNrdXAga2V5XG4gICAqIEBwYXJhbSB7QnVmZmVyfSBnYXNQcmljZSAtIGdhcyBwcmljZSBmb3IgdGhlIHR4XG4gICAqIEBwYXJhbSB7bnVtYmVyfSBnYXNMaW1pdCAtIGdhcyBsaW1pdCBmb3IgdGhlIHR4XG4gICAqIEBwYXJhbSB7bnVtYmVyfSBiYWNrdXBLZXlOb25jZSAtIHRoZSBub25jZSBvZiB0aGUgYmFja3VwIGtleSBhZGRyZXNzXG4gICAqIEBwYXJhbSB7RUlQMTU1OX0gZWlwMTU1OSAtIGVpcDE1NTkgcGFyYW1zXG4gICAqIEBwYXJhbSB7UmVwbGF5UHJvdGVjdGlvbk9wdGlvbnN9IHJlcGxheVByb3RlY3Rpb25PcHRpb25zIC0gcmVwbGF5IHByb3RlY3Rpb24gb3B0aW9uc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPZmZsaW5lVmF1bHRUeEluZm8+fVxuICAgKi9cbiAgZm9ybWF0Rm9yT2ZmbGluZVZhdWx0VFNTKFxuICAgIHR4SW5mbzogVW5mb3JtYXR0ZWRUeEluZm8sXG4gICAgZXRoVHg6IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24sXG4gICAgdXNlcktleTogc3RyaW5nLFxuICAgIGJhY2t1cEtleTogc3RyaW5nLFxuICAgIGdhc1ByaWNlOiBCdWZmZXIsXG4gICAgZ2FzTGltaXQ6IG51bWJlcixcbiAgICBiYWNrdXBLZXlOb25jZTogbnVtYmVyLFxuICAgIGVpcDE1NTk/OiBFSVAxNTU5LFxuICAgIHJlcGxheVByb3RlY3Rpb25PcHRpb25zPzogUmVwbGF5UHJvdGVjdGlvbk9wdGlvbnNcbiAgKTogT2ZmbGluZVZhdWx0VHhJbmZvIHtcbiAgICBpZiAoIWV0aFR4LnRvKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0V0aCB0eCBtdXN0IGhhdmUgYSBgdG9gIGFkZHJlc3MnKTtcbiAgICB9XG4gICAgY29uc3QgcmVzcG9uc2U6IE9mZmxpbmVWYXVsdFR4SW5mbyA9IHtcbiAgICAgIHR4OiBldGhUeC5zZXJpYWxpemUoKS50b1N0cmluZygnaGV4JyksXG4gICAgICB0eEhleDogZXRoVHguZ2V0TWVzc2FnZVRvU2lnbihmYWxzZSkudG9TdHJpbmcoKSxcbiAgICAgIHVzZXJLZXksXG4gICAgICBiYWNrdXBLZXksXG4gICAgICBjb2luOiB0aGlzLmdldENoYWluKCksXG4gICAgICBnYXNQcmljZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKSxcbiAgICAgIGdhc0xpbWl0LFxuICAgICAgcmVjaXBpZW50czogW3R4SW5mby5yZWNpcGllbnRdLFxuICAgICAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiBldGhUeC50by50b1N0cmluZygpLFxuICAgICAgYW1vdW50OiB0eEluZm8ucmVjaXBpZW50LmFtb3VudCBhcyBzdHJpbmcsXG4gICAgICBiYWNrdXBLZXlOb25jZTogYmFja3VwS2V5Tm9uY2UsXG4gICAgICBlaXAxNTU5LFxuICAgICAgcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnMsXG4gICAgfTtcbiAgICBfLmV4dGVuZChyZXNwb25zZSwgdHhJbmZvKTtcbiAgICByZXR1cm4gcmVzcG9uc2U7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgd2hldGhlciB0aGUgZ2FzIHByaWNlIHBhc3NlZCBpbiBieSB1c2VyIGFyZSB3aXRoaW4gb3VyIG1heCBhbmQgbWluIGJvdW5kc1xuICAgKiBJZiB0aGV5IGFyZSBub3Qgc2V0LCBzZXQgdGhlbSB0byB0aGUgZGVmYXVsdHNcbiAgICogQHBhcmFtIHtudW1iZXJ9IHVzZXJHYXNQcmljZSAtIHVzZXIgZGVmaW5lZCBnYXMgcHJpY2VcbiAgICogQHJldHVybnMge251bWJlcn0gdGhlIGdhcyBwcmljZSB0byB1c2UgZm9yIHRoaXMgdHJhbnNhY3Rpb25cbiAgICovXG4gIHNldEdhc1ByaWNlKHVzZXJHYXNQcmljZT86IG51bWJlcik6IG51bWJlciB7XG4gICAgaWYgKCF1c2VyR2FzUHJpY2UpIHtcbiAgICAgIHJldHVybiBldGhHYXNDb25maWdzLmRlZmF1bHRHYXNQcmljZTtcbiAgICB9XG5cbiAgICBjb25zdCBnYXNQcmljZU1heCA9IGV0aEdhc0NvbmZpZ3MubWF4aW11bUdhc1ByaWNlO1xuICAgIGNvbnN0IGdhc1ByaWNlTWluID0gZXRoR2FzQ29uZmlncy5taW5pbXVtR2FzUHJpY2U7XG4gICAgaWYgKHVzZXJHYXNQcmljZSA8IGdhc1ByaWNlTWluIHx8IHVzZXJHYXNQcmljZSA+IGdhc1ByaWNlTWF4KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEdhcyBwcmljZSBtdXN0IGJlIGJldHdlZW4gJHtnYXNQcmljZU1pbn0gYW5kICR7Z2FzUHJpY2VNYXh9YCk7XG4gICAgfVxuICAgIHJldHVybiB1c2VyR2FzUHJpY2U7XG4gIH1cbiAgLyoqXG4gICAqIENoZWNrIHdoZXRoZXIgZ2FzIGxpbWl0IHBhc3NlZCBpbiBieSB1c2VyIGFyZSB3aXRoaW4gb3VyIG1heCBhbmQgbWluIGJvdW5kc1xuICAgKiBJZiB0aGV5IGFyZSBub3Qgc2V0LCBzZXQgdGhlbSB0byB0aGUgZGVmYXVsdHNcbiAgICogQHBhcmFtIHtudW1iZXJ9IHVzZXJHYXNMaW1pdCB1c2VyIGRlZmluZWQgZ2FzIGxpbWl0XG4gICAqIEByZXR1cm5zIHtudW1iZXJ9IHRoZSBnYXMgbGltaXQgdG8gdXNlIGZvciB0aGlzIHRyYW5zYWN0aW9uXG4gICAqL1xuICBzZXRHYXNMaW1pdCh1c2VyR2FzTGltaXQ/OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGlmICghdXNlckdhc0xpbWl0KSB7XG4gICAgICByZXR1cm4gZXRoR2FzQ29uZmlncy5kZWZhdWx0R2FzTGltaXQ7XG4gICAgfVxuICAgIGNvbnN0IGdhc0xpbWl0TWF4ID0gZXRoR2FzQ29uZmlncy5tYXhpbXVtR2FzTGltaXQ7XG4gICAgY29uc3QgZ2FzTGltaXRNaW4gPSBldGhHYXNDb25maWdzLm1pbmltdW1HYXNMaW1pdDtcbiAgICBpZiAodXNlckdhc0xpbWl0IDwgZ2FzTGltaXRNaW4gfHwgdXNlckdhc0xpbWl0ID4gZ2FzTGltaXRNYXgpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgR2FzIGxpbWl0IG11c3QgYmUgYmV0d2VlbiAke2dhc0xpbWl0TWlufSBhbmQgJHtnYXNMaW1pdE1heH1gKTtcbiAgICB9XG4gICAgcmV0dXJuIHVzZXJHYXNMaW1pdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBIZWxwZXIgZnVuY3Rpb24gZm9yIHNpZ25UcmFuc2FjdGlvbiBmb3IgdGhlIHJhcmUgY2FzZSB0aGF0IFNESyBpcyBkb2luZyB0aGUgc2Vjb25kIHNpZ25hdHVyZVxuICAgKiBOb3RlOiB3ZSBhcmUgZXhwZWN0aW5nIHRoaXMgdG8gYmUgY2FsbGVkIGZyb20gdGhlIG9mZmxpbmUgdmF1bHRcbiAgICogQHBhcmFtIHtTaWduRmluYWxPcHRpb25zLnR4UHJlYnVpbGR9IHBhcmFtcy50eFByZWJ1aWxkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMucHJ2XG4gICAqIEByZXR1cm5zIHt7dHhIZXg6IHN0cmluZ319XG4gICAqL1xuICBhc3luYyBzaWduRmluYWxFdGhMaWtlKHBhcmFtczogU2lnbkZpbmFsT3B0aW9ucyk6IFByb21pc2U8RnVsbHlTaWduZWRUcmFuc2FjdGlvbj4ge1xuICAgIGNvbnN0IHNpZ25pbmdLZXkgPSBuZXcgS2V5UGFpckxpYih7IHBydjogcGFyYW1zLnBydiB9KS5nZXRLZXlzKCkucHJ2O1xuICAgIGlmIChfLmlzVW5kZWZpbmVkKHNpZ25pbmdLZXkpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3NpbmcgcHJpdmF0ZSBrZXknKTtcbiAgICB9XG4gICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIoKTtcbiAgICB0cnkge1xuICAgICAgdHhCdWlsZGVyLmZyb20ocGFyYW1zLnR4UHJlYnVpbGQuaGFsZlNpZ25lZD8udHhIZXgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCBoYWxmLXNpZ25lZCB0cmFuc2FjdGlvbicpO1xuICAgIH1cbiAgICB0eEJ1aWxkZXIuc2lnbih7IGtleTogc2lnbmluZ0tleSB9KTtcbiAgICBjb25zdCB0eCA9IGF3YWl0IHR4QnVpbGRlci5idWlsZCgpO1xuICAgIHJldHVybiB7XG4gICAgICB0eEhleDogdHgudG9Ccm9hZGNhc3RGb3JtYXQoKSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEFzc2VtYmxlIGhhbGYtc2lnbiBwcmVidWlsdCB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0ge1NpZ25UcmFuc2FjdGlvbk9wdGlvbnN9IHBhcmFtc1xuICAgKi9cbiAgYXN5bmMgc2lnblRyYW5zYWN0aW9uKHBhcmFtczogU2lnblRyYW5zYWN0aW9uT3B0aW9ucyk6IFByb21pc2U8U2lnbmVkVHJhbnNhY3Rpb24+IHtcbiAgICAvLyBOb3JtYWxseSB0aGUgU0RLIHByb3ZpZGVzIHRoZSBmaXJzdCBzaWduYXR1cmUgZm9yIGFuIEV0aExpa2UgdHgsIGJ1dCBvY2Nhc2lvbmFsbHkgaXQgcHJvdmlkZXMgdGhlIHNlY29uZCBhbmQgZmluYWwgb25lLlxuICAgIGlmIChwYXJhbXMuaXNMYXN0U2lnbmF0dXJlKSB7XG4gICAgICAvLyBJbiB0aGlzIGNhc2Ugd2hlbiB3ZSdyZSBkb2luZyB0aGUgc2Vjb25kIChmaW5hbCkgc2lnbmF0dXJlLCB0aGUgbG9naWMgaXMgZGlmZmVyZW50LlxuICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuc2lnbkZpbmFsRXRoTGlrZShwYXJhbXMpO1xuICAgIH1cbiAgICBjb25zdCB0eEJ1aWxkZXIgPSB0aGlzLmdldFRyYW5zYWN0aW9uQnVpbGRlcigpO1xuICAgIHR4QnVpbGRlci5mcm9tKHBhcmFtcy50eFByZWJ1aWxkLnR4SGV4KTtcbiAgICB0eEJ1aWxkZXJcbiAgICAgIC50cmFuc2ZlcigpXG4gICAgICAuY29pbih0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZylcbiAgICAgIC5rZXkobmV3IEtleVBhaXJMaWIoeyBwcnY6IHBhcmFtcy5wcnYgfSkuZ2V0S2V5cygpLnBydiEpO1xuICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCk7XG5cbiAgICBjb25zdCByZWNpcGllbnRzID0gdHJhbnNhY3Rpb24ub3V0cHV0cy5tYXAoKG91dHB1dCkgPT4gKHsgYWRkcmVzczogb3V0cHV0LmFkZHJlc3MsIGFtb3VudDogb3V0cHV0LnZhbHVlIH0pKTtcblxuICAgIGNvbnN0IHR4UGFyYW1zID0ge1xuICAgICAgZWlwMTU1OTogcGFyYW1zLnR4UHJlYnVpbGQuZWlwMTU1OSxcbiAgICAgIHR4SGV4OiB0cmFuc2FjdGlvbi50b0Jyb2FkY2FzdEZvcm1hdCgpLFxuICAgICAgcmVjaXBpZW50czogcmVjaXBpZW50cyxcbiAgICAgIGV4cGlyYXRpb246IHBhcmFtcy50eFByZWJ1aWxkLmV4cGlyZVRpbWUsXG4gICAgICBob3BUcmFuc2FjdGlvbjogcGFyYW1zLnR4UHJlYnVpbGQuaG9wVHJhbnNhY3Rpb24sXG4gICAgICBjdXN0b2RpYW5UcmFuc2FjdGlvbklkOiBwYXJhbXMuY3VzdG9kaWFuVHJhbnNhY3Rpb25JZCxcbiAgICAgIGV4cGlyZVRpbWU6IHBhcmFtcy5leHBpcmVUaW1lLFxuICAgICAgY29udHJhY3RTZXF1ZW5jZUlkOiBwYXJhbXMudHhQcmVidWlsZC5uZXh0Q29udHJhY3RTZXF1ZW5jZUlkIGFzIG51bWJlcixcbiAgICAgIHNlcXVlbmNlSWQ6IHBhcmFtcy5zZXF1ZW5jZUlkLFxuICAgIH07XG5cbiAgICByZXR1cm4geyBoYWxmU2lnbmVkOiB0eFBhcmFtcyB9O1xuICB9XG5cbiAgLyoqXG4gICAqIE1ldGhvZCB0byB2YWxpZGF0ZSByZWNvdmVyeSBwYXJhbXNcbiAgICogQHBhcmFtIHtSZWNvdmVyT3B0aW9uc30gcGFyYW1zXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgdmFsaWRhdGVSZWNvdmVyeVBhcmFtcyhwYXJhbXM6IFJlY292ZXJPcHRpb25zKTogdm9pZCB7XG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLnVzZXJLZXkpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3NpbmcgdXNlcktleScpO1xuICAgIH1cblxuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy5iYWNrdXBLZXkpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3NpbmcgYmFja3VwS2V5Jyk7XG4gICAgfVxuXG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLndhbGxldFBhc3NwaHJhc2UpICYmICFwYXJhbXMudXNlcktleS5zdGFydHNXaXRoKCd4cHViJykgJiYgIXBhcmFtcy5pc1Rzcykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtaXNzaW5nIHdhbGxldCBwYXNzcGhyYXNlJyk7XG4gICAgfVxuXG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcykgfHwgIXRoaXMuaXNWYWxpZEFkZHJlc3MocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCB3YWxsZXRDb250cmFjdEFkZHJlc3MnKTtcbiAgICB9XG5cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbikgfHwgIXRoaXMuaXNWYWxpZEFkZHJlc3MocGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24pKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgcmVjb3ZlcnlEZXN0aW5hdGlvbicpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBNZXRob2QgdG8gc2lnbiB0c3MgcmVjb3ZlcnkgdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtFQ0RTQS5LZXlDb21iaW5lZH0gdXNlcktleUNvbWJpbmVkXG4gICAqIEBwYXJhbSB7RUNEU0EuS2V5Q29tYmluZWR9IGJhY2t1cEtleUNvbWJpbmVkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0eEhleFxuICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9uc1xuICAgKiBAcGFyYW0ge0VjZHNhVHlwZXMuU2VyaWFsaXplZE50aWxkZX0gb3B0aW9ucy5yYW5nZVByb29mQ2hhbGxlbmdlXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPEVDRFNBTWV0aG9kVHlwZXMuU2lnbmF0dXJlPn1cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc2lnblJlY292ZXJ5VFNTKFxuICAgIHVzZXJLZXlDb21iaW5lZDogRUNEU0EuS2V5Q29tYmluZWQsXG4gICAgYmFja3VwS2V5Q29tYmluZWQ6IEVDRFNBLktleUNvbWJpbmVkLFxuICAgIHR4SGV4OiBzdHJpbmcsXG4gICAge1xuICAgICAgcmFuZ2VQcm9vZkNoYWxsZW5nZSxcbiAgICB9OiB7XG4gICAgICByYW5nZVByb29mQ2hhbGxlbmdlPzogRWNkc2FUeXBlcy5TZXJpYWxpemVkTnRpbGRlO1xuICAgIH0gPSB7fVxuICApOiBQcm9taXNlPEVDRFNBTWV0aG9kVHlwZXMuU2lnbmF0dXJlPiB7XG4gICAgaWYgKCF1c2VyS2V5Q29tYmluZWQgfHwgIWJhY2t1cEtleUNvbWJpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ01pc3Npbmcga2V5IGNvbWJpbmVkIHNoYXJlcyBmb3IgdXNlciBvciBiYWNrdXAnKTtcbiAgICB9XG5cbiAgICBjb25zdCBNUEMgPSBuZXcgRWNkc2EoKTtcbiAgICBjb25zdCBzaWduZXJPbmVJbmRleCA9IHVzZXJLZXlDb21iaW5lZC54U2hhcmUuaTtcbiAgICBjb25zdCBzaWduZXJUd29JbmRleCA9IGJhY2t1cEtleUNvbWJpbmVkLnhTaGFyZS5pO1xuXG4gICAgcmFuZ2VQcm9vZkNoYWxsZW5nZSA9XG4gICAgICByYW5nZVByb29mQ2hhbGxlbmdlID8/IEVjZHNhVHlwZXMuc2VyaWFsaXplTnRpbGRlV2l0aFByb29mcyhhd2FpdCBFY2RzYVJhbmdlUHJvb2YuZ2VuZXJhdGVOdGlsZGUoKSk7XG5cbiAgICBjb25zdCB1c2VyVG9CYWNrdXBQYWlsbGllckNoYWxsZW5nZSA9IGF3YWl0IEVjZHNhUGFpbGxpZXJQcm9vZi5nZW5lcmF0ZVAoXG4gICAgICBoZXhUb0JpZ0ludCh1c2VyS2V5Q29tYmluZWQueVNoYXJlc1tzaWduZXJUd29JbmRleF0ubilcbiAgICApO1xuICAgIGNvbnN0IGJhY2t1cFRvVXNlclBhaWxsaWVyQ2hhbGxlbmdlID0gYXdhaXQgRWNkc2FQYWlsbGllclByb29mLmdlbmVyYXRlUChcbiAgICAgIGhleFRvQmlnSW50KGJhY2t1cEtleUNvbWJpbmVkLnlTaGFyZXNbc2lnbmVyT25lSW5kZXhdLm4pXG4gICAgKTtcblxuICAgIGNvbnN0IHVzZXJYU2hhcmUgPSBNUEMuYXBwZW5kQ2hhbGxlbmdlKFxuICAgICAgdXNlcktleUNvbWJpbmVkLnhTaGFyZSxcbiAgICAgIHJhbmdlUHJvb2ZDaGFsbGVuZ2UsXG4gICAgICBFY2RzYVR5cGVzLnNlcmlhbGl6ZVBhaWxsaWVyQ2hhbGxlbmdlKHsgcDogdXNlclRvQmFja3VwUGFpbGxpZXJDaGFsbGVuZ2UgfSlcbiAgICApO1xuICAgIGNvbnN0IHVzZXJZU2hhcmUgPSBNUEMuYXBwZW5kQ2hhbGxlbmdlKFxuICAgICAgdXNlcktleUNvbWJpbmVkLnlTaGFyZXNbc2lnbmVyVHdvSW5kZXhdLFxuICAgICAgcmFuZ2VQcm9vZkNoYWxsZW5nZSxcbiAgICAgIEVjZHNhVHlwZXMuc2VyaWFsaXplUGFpbGxpZXJDaGFsbGVuZ2UoeyBwOiBiYWNrdXBUb1VzZXJQYWlsbGllckNoYWxsZW5nZSB9KVxuICAgICk7XG4gICAgY29uc3QgYmFja3VwWFNoYXJlID0gTVBDLmFwcGVuZENoYWxsZW5nZShcbiAgICAgIGJhY2t1cEtleUNvbWJpbmVkLnhTaGFyZSxcbiAgICAgIHJhbmdlUHJvb2ZDaGFsbGVuZ2UsXG4gICAgICBFY2RzYVR5cGVzLnNlcmlhbGl6ZVBhaWxsaWVyQ2hhbGxlbmdlKHsgcDogYmFja3VwVG9Vc2VyUGFpbGxpZXJDaGFsbGVuZ2UgfSlcbiAgICApO1xuICAgIGNvbnN0IGJhY2t1cFlTaGFyZSA9IE1QQy5hcHBlbmRDaGFsbGVuZ2UoXG4gICAgICBiYWNrdXBLZXlDb21iaW5lZC55U2hhcmVzW3NpZ25lck9uZUluZGV4XSxcbiAgICAgIHJhbmdlUHJvb2ZDaGFsbGVuZ2UsXG4gICAgICBFY2RzYVR5cGVzLnNlcmlhbGl6ZVBhaWxsaWVyQ2hhbGxlbmdlKHsgcDogdXNlclRvQmFja3VwUGFpbGxpZXJDaGFsbGVuZ2UgfSlcbiAgICApO1xuXG4gICAgY29uc3Qgc2lnblNoYXJlczogRUNEU0EuU2lnblNoYXJlUlQgPSBhd2FpdCBNUEMuc2lnblNoYXJlKHVzZXJYU2hhcmUsIHVzZXJZU2hhcmUpO1xuXG4gICAgY29uc3Qgc2lnbkNvbnZlcnRTMjEgPSBhd2FpdCBNUEMuc2lnbkNvbnZlcnRTdGVwMSh7XG4gICAgICB4U2hhcmU6IGJhY2t1cFhTaGFyZSxcbiAgICAgIHlTaGFyZTogYmFja3VwWVNoYXJlLCAvLyBZU2hhcmUgY29ycmVzcG9uZGluZyB0byB0aGUgb3RoZXIgcGFydGljaXBhbnQgc2lnbmVyT25lXG4gICAgICBrU2hhcmU6IHNpZ25TaGFyZXMua1NoYXJlLFxuICAgIH0pO1xuICAgIGNvbnN0IHNpZ25Db252ZXJ0UzEyID0gYXdhaXQgTVBDLnNpZ25Db252ZXJ0U3RlcDIoe1xuICAgICAgYVNoYXJlOiBzaWduQ29udmVydFMyMS5hU2hhcmUsXG4gICAgICB3U2hhcmU6IHNpZ25TaGFyZXMud1NoYXJlLFxuICAgIH0pO1xuICAgIGNvbnN0IHNpZ25Db252ZXJ0UzIxXzIgPSBhd2FpdCBNUEMuc2lnbkNvbnZlcnRTdGVwMyh7XG4gICAgICBtdVNoYXJlOiBzaWduQ29udmVydFMxMi5tdVNoYXJlLFxuICAgICAgYlNoYXJlOiBzaWduQ29udmVydFMyMS5iU2hhcmUsXG4gICAgfSk7XG5cbiAgICBjb25zdCBbc2lnbkNvbWJpbmVPbmUsIHNpZ25Db21iaW5lVHdvXSA9IFtcbiAgICAgIE1QQy5zaWduQ29tYmluZSh7XG4gICAgICAgIGdTaGFyZTogc2lnbkNvbnZlcnRTMTIuZ1NoYXJlLFxuICAgICAgICBzaWduSW5kZXg6IHtcbiAgICAgICAgICBpOiBzaWduQ29udmVydFMxMi5tdVNoYXJlLmksXG4gICAgICAgICAgajogc2lnbkNvbnZlcnRTMTIubXVTaGFyZS5qLFxuICAgICAgICB9LFxuICAgICAgfSksXG4gICAgICBNUEMuc2lnbkNvbWJpbmUoe1xuICAgICAgICBnU2hhcmU6IHNpZ25Db252ZXJ0UzIxXzIuZ1NoYXJlLFxuICAgICAgICBzaWduSW5kZXg6IHtcbiAgICAgICAgICBpOiBzaWduQ29udmVydFMyMV8yLnNpZ25JbmRleC5pLFxuICAgICAgICAgIGo6IHNpZ25Db252ZXJ0UzIxXzIuc2lnbkluZGV4LmosXG4gICAgICAgIH0sXG4gICAgICB9KSxcbiAgICBdO1xuXG4gICAgY29uc3QgTUVTU0FHRSA9IEJ1ZmZlci5mcm9tKHR4SGV4LCAnaGV4Jyk7XG5cbiAgICBjb25zdCBbc2lnbkEsIHNpZ25CXSA9IFtcbiAgICAgIE1QQy5zaWduKE1FU1NBR0UsIHNpZ25Db21iaW5lT25lLm9TaGFyZSwgc2lnbkNvbWJpbmVUd28uZFNoYXJlLCBLZWNjYWsoJ2tlY2NhazI1NicpKSxcbiAgICAgIE1QQy5zaWduKE1FU1NBR0UsIHNpZ25Db21iaW5lVHdvLm9TaGFyZSwgc2lnbkNvbWJpbmVPbmUuZFNoYXJlLCBLZWNjYWsoJ2tlY2NhazI1NicpKSxcbiAgICBdO1xuXG4gICAgcmV0dXJuIE1QQy5jb25zdHJ1Y3RTaWduYXR1cmUoW3NpZ25BLCBzaWduQl0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEhlbHBlciB3aGljaCBjb21iaW5lcyBrZXkgc2hhcmVzIG9mIHVzZXIgYW5kIGJhY2t1cFxuICAgKiBAcGFyYW0ge3N0cmluZ30gdXNlclB1YmxpY09yUHJpdmF0ZUtleVNoYXJlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBiYWNrdXBQcml2YXRlT3JQdWJsaWNLZXlTaGFyZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gd2FsbGV0UGFzc3BocmFzZVxuICAgKiBAcmV0dXJucyB7W0VDRFNBTWV0aG9kVHlwZXMuS2V5Q29tYmluZWQsIEVDRFNBTWV0aG9kVHlwZXMuS2V5Q29tYmluZWRdfVxuICAgKi9cbiAgcHJpdmF0ZSBnZXRLZXlDb21iaW5lZEZyb21Uc3NLZXlTaGFyZXMoXG4gICAgdXNlclB1YmxpY09yUHJpdmF0ZUtleVNoYXJlOiBzdHJpbmcsXG4gICAgYmFja3VwUHJpdmF0ZU9yUHVibGljS2V5U2hhcmU6IHN0cmluZyxcbiAgICB3YWxsZXRQYXNzcGhyYXNlPzogc3RyaW5nXG4gICk6IFtFQ0RTQU1ldGhvZFR5cGVzLktleUNvbWJpbmVkLCBFQ0RTQU1ldGhvZFR5cGVzLktleUNvbWJpbmVkXSB7XG4gICAgbGV0IGJhY2t1cFBydjtcbiAgICBsZXQgdXNlclBydjtcbiAgICB0cnkge1xuICAgICAgYmFja3VwUHJ2ID0gdGhpcy5iaXRnby5kZWNyeXB0KHtcbiAgICAgICAgaW5wdXQ6IGJhY2t1cFByaXZhdGVPclB1YmxpY0tleVNoYXJlLFxuICAgICAgICBwYXNzd29yZDogd2FsbGV0UGFzc3BocmFzZSxcbiAgICAgIH0pO1xuICAgICAgdXNlclBydiA9IHRoaXMuYml0Z28uZGVjcnlwdCh7XG4gICAgICAgIGlucHV0OiB1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUsXG4gICAgICAgIHBhc3N3b3JkOiB3YWxsZXRQYXNzcGhyYXNlLFxuICAgICAgfSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBFcnJvciBkZWNyeXB0aW5nIGJhY2t1cCBrZXljaGFpbjogJHtlLm1lc3NhZ2V9YCk7XG4gICAgfVxuXG4gICAgY29uc3QgdXNlclNpZ25pbmdNYXRlcmlhbCA9IEpTT04ucGFyc2UodXNlclBydikgYXMgRUNEU0FNZXRob2RUeXBlcy5TaWduaW5nTWF0ZXJpYWw7XG4gICAgY29uc3QgYmFja3VwU2lnbmluZ01hdGVyaWFsID0gSlNPTi5wYXJzZShiYWNrdXBQcnYpIGFzIEVDRFNBTWV0aG9kVHlwZXMuU2lnbmluZ01hdGVyaWFsO1xuXG4gICAgaWYgKCF1c2VyU2lnbmluZ01hdGVyaWFsLmJhY2t1cE5TaGFyZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHVzZXIga2V5IC0gbWlzc2luZyBiYWNrdXBOU2hhcmUnKTtcbiAgICB9XG5cbiAgICBpZiAoIWJhY2t1cFNpZ25pbmdNYXRlcmlhbC51c2VyTlNoYXJlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgYmFja3VwIGtleSAtIG1pc3NpbmcgdXNlck5TaGFyZScpO1xuICAgIH1cblxuICAgIGNvbnN0IE1QQyA9IG5ldyBFY2RzYSgpO1xuXG4gICAgY29uc3QgdXNlcktleUNvbWJpbmVkID0gTVBDLmtleUNvbWJpbmUodXNlclNpZ25pbmdNYXRlcmlhbC5wU2hhcmUsIFtcbiAgICAgIHVzZXJTaWduaW5nTWF0ZXJpYWwuYml0Z29OU2hhcmUsXG4gICAgICB1c2VyU2lnbmluZ01hdGVyaWFsLmJhY2t1cE5TaGFyZSxcbiAgICBdKTtcbiAgICBjb25zdCBiYWNrdXBLZXlDb21iaW5lZCA9IE1QQy5rZXlDb21iaW5lKGJhY2t1cFNpZ25pbmdNYXRlcmlhbC5wU2hhcmUsIFtcbiAgICAgIGJhY2t1cFNpZ25pbmdNYXRlcmlhbC5iaXRnb05TaGFyZSxcbiAgICAgIGJhY2t1cFNpZ25pbmdNYXRlcmlhbC51c2VyTlNoYXJlLFxuICAgIF0pO1xuXG4gICAgaWYgKFxuICAgICAgdXNlcktleUNvbWJpbmVkLnhTaGFyZS55ICE9PSBiYWNrdXBLZXlDb21iaW5lZC54U2hhcmUueSB8fFxuICAgICAgdXNlcktleUNvbWJpbmVkLnhTaGFyZS5jaGFpbmNvZGUgIT09IGJhY2t1cEtleUNvbWJpbmVkLnhTaGFyZS5jaGFpbmNvZGVcbiAgICApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ29tbW9uIGtleWNoYWlucyBkbyBub3QgbWF0Y2gnKTtcbiAgICB9XG5cbiAgICByZXR1cm4gW3VzZXJLZXlDb21iaW5lZCwgYmFja3VwS2V5Q29tYmluZWRdO1xuICB9XG5cbiAgLyoqXG4gICAqIEhlbHBlciB3aGljaCBBZGRzIHNpZ25hdHVyZXMgdG8gdHggb2JqZWN0IGFuZCByZS1zZXJpYWxpemVzIHR4XG4gICAqIEBwYXJhbSB7RXRoTGlrZUNvbW1vbi5kZWZhdWx0fSBldGhDb21tb25cbiAgICogQHBhcmFtIHtFdGhMaWtlVHhMaWIuRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uIHwgRXRoTGlrZVR4TGliLlRyYW5zYWN0aW9ufSB0eFxuICAgKiBAcGFyYW0ge0VDRFNBTWV0aG9kVHlwZXMuU2lnbmF0dXJlfSBzaWduYXR1cmVcbiAgICogQHJldHVybnMge0V0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24gfCBFdGhMaWtlVHhMaWIuVHJhbnNhY3Rpb259XG4gICAqL1xuICBwcml2YXRlIGdldFNpZ25lZFR4RnJvbVNpZ25hdHVyZShcbiAgICBldGhDb21tb246IEV0aExpa2VDb21tb24uZGVmYXVsdCxcbiAgICB0eDogRXRoTGlrZVR4TGliLkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbixcbiAgICBzaWduYXR1cmU6IEVDRFNBTWV0aG9kVHlwZXMuU2lnbmF0dXJlXG4gICkge1xuICAgIC8vIGdldCBzaWduZWQgVHggZnJvbSBzaWduYXR1cmVcbiAgICBjb25zdCB0eERhdGEgPSB0eC50b0pTT04oKTtcbiAgICBjb25zdCB5UGFyaXR5ID0gc2lnbmF0dXJlLnJlY2lkO1xuICAgIGNvbnN0IGJhc2VQYXJhbXMgPSB7XG4gICAgICB0bzogdHhEYXRhLnRvLFxuICAgICAgbm9uY2U6IG5ldyBCTihzdHJpcEhleFByZWZpeCh0eERhdGEubm9uY2UhKSwgJ2hleCcpLFxuICAgICAgdmFsdWU6IG5ldyBCTihzdHJpcEhleFByZWZpeCh0eERhdGEudmFsdWUhKSwgJ2hleCcpLFxuICAgICAgZ2FzTGltaXQ6IG5ldyBCTihzdHJpcEhleFByZWZpeCh0eERhdGEuZ2FzTGltaXQhKSwgJ2hleCcpLFxuICAgICAgZGF0YTogdHhEYXRhLmRhdGEsXG4gICAgICByOiBhZGRIZXhQcmVmaXgoc2lnbmF0dXJlLnIpLFxuICAgICAgczogYWRkSGV4UHJlZml4KHNpZ25hdHVyZS5zKSxcbiAgICB9O1xuXG4gICAgbGV0IGZpbmFsVHg7XG4gICAgaWYgKHR4RGF0YS5tYXhGZWVQZXJHYXMgJiYgdHhEYXRhLm1heFByaW9yaXR5RmVlUGVyR2FzKSB7XG4gICAgICBmaW5hbFR4ID0gRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uLmZyb21UeERhdGEoXG4gICAgICAgIHtcbiAgICAgICAgICAuLi5iYXNlUGFyYW1zLFxuICAgICAgICAgIG1heFByaW9yaXR5RmVlUGVyR2FzOiBuZXcgQk4oc3RyaXBIZXhQcmVmaXgodHhEYXRhLm1heFByaW9yaXR5RmVlUGVyR2FzISksICdoZXgnKSxcbiAgICAgICAgICBtYXhGZWVQZXJHYXM6IG5ldyBCTihzdHJpcEhleFByZWZpeCh0eERhdGEubWF4RmVlUGVyR2FzISksICdoZXgnKSxcbiAgICAgICAgICB2OiBuZXcgQk4oeVBhcml0eS50b1N0cmluZygpKSxcbiAgICAgICAgfSxcbiAgICAgICAgeyBjb21tb246IGV0aENvbW1vbiB9XG4gICAgICApO1xuICAgIH0gZWxzZSBpZiAodHhEYXRhLmdhc1ByaWNlKSB7XG4gICAgICBjb25zdCB2ID0gQmlnSW50KDM1KSArIEJpZ0ludCh5UGFyaXR5KSArIEJpZ0ludChldGhDb21tb24uY2hhaW5JZEJOKCkudG9OdW1iZXIoKSkgKiBCaWdJbnQoMik7XG4gICAgICBmaW5hbFR4ID0gTGVnYWN5VHJhbnNhY3Rpb24uZnJvbVR4RGF0YShcbiAgICAgICAge1xuICAgICAgICAgIC4uLmJhc2VQYXJhbXMsXG4gICAgICAgICAgdjogbmV3IEJOKHYudG9TdHJpbmcoKSksXG4gICAgICAgICAgZ2FzUHJpY2U6IG5ldyBCTihzdHJpcEhleFByZWZpeCh0eERhdGEuZ2FzUHJpY2UhLnRvU3RyaW5nKCkpLCAnaGV4JyksXG4gICAgICAgIH0sXG4gICAgICAgIHsgY29tbW9uOiBldGhDb21tb24gfVxuICAgICAgKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZmluYWxUeDtcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgYSBmdW5kcyByZWNvdmVyeSB0cmFuc2FjdGlvbiB3aXRob3V0IEJpdEdvXG4gICAqIEBwYXJhbSBwYXJhbXNcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy51c2VyS2V5IC0gW2VuY3J5cHRlZF0geHBydlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmJhY2t1cEtleSAtIFtlbmNyeXB0ZWRdIHhwcnYgb3IgeHB1YiBpZiB0aGUgeHBydiBpcyBoZWxkIGJ5IGEgS1JTIHByb3ZpZGVyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMud2FsbGV0UGFzc3BocmFzZSAtIHVzZWQgdG8gZGVjcnlwdCB1c2VyS2V5IGFuZCBiYWNrdXBLZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MgLSB0aGUgRVRIIGFkZHJlc3Mgb2YgdGhlIHdhbGxldCBjb250cmFjdFxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmtyc1Byb3ZpZGVyIC0gbmVjZXNzYXJ5IGlmIGJhY2t1cCBrZXkgaXMgaGVsZCBieSBLUlNcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uIC0gdGFyZ2V0IGFkZHJlc3MgdG8gc2VuZCByZWNvdmVyZWQgZnVuZHMgdG9cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iaXRnb0ZlZUFkZHJlc3MgLSB3cm9uZyBjaGFpbiB3YWxsZXQgZmVlIGFkZHJlc3MgZm9yIGV2bSBiYXNlZCBjcm9zcyBjaGFpbiByZWNvdmVyeSB0eG5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcyAtIHRhcmdldCBiaXRnbyBhZGRyZXNzIHdoZXJlIGZlZSB3aWxsIGJlIHNlbnQgZm9yIGV2bSBiYXNlZCBjcm9zcyBjaGFpbiByZWNvdmVyeSB0eG5cbiAgICovXG4gIGFzeW5jIHJlY292ZXIocGFyYW1zOiBSZWNvdmVyT3B0aW9ucyk6IFByb21pc2U8UmVjb3ZlcnlJbmZvIHwgT2ZmbGluZVZhdWx0VHhJbmZvPiB7XG4gICAgaWYgKHBhcmFtcy5pc1Rzcykge1xuICAgICAgcmV0dXJuIHRoaXMucmVjb3ZlclRTUyhwYXJhbXMpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5yZWNvdmVyRXRoTGlrZShwYXJhbXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkcyBhIGZ1bmRzIHJlY292ZXJ5IHRyYW5zYWN0aW9uIHdpdGhvdXQgQml0R28gZm9yIG5vbi1UU1MgdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHBhcmFtc1xuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLnVzZXJLZXkgW2VuY3J5cHRlZF0geHBydiBvciB4cHViXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuYmFja3VwS2V5IFtlbmNyeXB0ZWRdIHhwcnYgb3IgeHB1YiBpZiB0aGUgeHBydiBpcyBoZWxkIGJ5IGEgS1JTIHByb3ZpZGVyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMud2FsbGV0UGFzc3BocmFzZSB1c2VkIHRvIGRlY3J5cHQgdXNlcktleSBhbmQgYmFja3VwS2V5XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzIHRoZSBFdGhMaWtlIGFkZHJlc3Mgb2YgdGhlIHdhbGxldCBjb250cmFjdFxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmtyc1Byb3ZpZGVyIG5lY2Vzc2FyeSBpZiBiYWNrdXAga2V5IGlzIGhlbGQgYnkgS1JTXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbiB0YXJnZXQgYWRkcmVzcyB0byBzZW5kIHJlY292ZXJlZCBmdW5kcyB0b1xuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmJpdGdvRmVlQWRkcmVzcyB3cm9uZyBjaGFpbiB3YWxsZXQgZmVlIGFkZHJlc3MgZm9yIGV2bSBiYXNlZCBjcm9zcyBjaGFpbiByZWNvdmVyeSB0eG5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcyB0YXJnZXQgYml0Z28gYWRkcmVzcyB3aGVyZSBmZWUgd2lsbCBiZSBzZW50IGZvciBldm0gYmFzZWQgY3Jvc3MgY2hhaW4gcmVjb3ZlcnkgdHhuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFJlY292ZXJ5SW5mbyB8IE9mZmxpbmVWYXVsdFR4SW5mbz59XG4gICAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgcmVjb3ZlckV0aExpa2UocGFyYW1zOiBSZWNvdmVyT3B0aW9ucyk6IFByb21pc2U8UmVjb3ZlcnlJbmZvIHwgT2ZmbGluZVZhdWx0VHhJbmZvPiB7XG4gICAgLy8gYml0Z29GZWVBZGRyZXNzIGlzIG9ubHkgZGVmaW5lZCB3aGVuIGl0IGlzIGEgZXZtIGNyb3NzIGNoYWluIHJlY292ZXJ5XG4gICAgLy8gYXMgd2UgdXNlIGZlZSBmcm9tIHRoaXMgd3JvbmcgY2hhaW4gYWRkcmVzcyBmb3IgdGhlIHJlY292ZXJ5IHR4biBvbiB0aGUgY29ycmVjdCBjaGFpbi5cbiAgICBpZiAocGFyYW1zLmJpdGdvRmVlQWRkcmVzcykge1xuICAgICAgcmV0dXJuIHRoaXMucmVjb3ZlckV0aExpa2Vmb3JFdm1CYXNlZFJlY292ZXJ5KHBhcmFtcyk7XG4gICAgfVxuXG4gICAgdGhpcy52YWxpZGF0ZVJlY292ZXJ5UGFyYW1zKHBhcmFtcyk7XG4gICAgY29uc3QgaXNVbnNpZ25lZFN3ZWVwID0gZ2V0SXNVbnNpZ25lZFN3ZWVwKHBhcmFtcyk7XG5cbiAgICAvLyBDbGVhbiB1cCB3aGl0ZXNwYWNlIGZyb20gZW50ZXJlZCB2YWx1ZXNcbiAgICBsZXQgdXNlcktleSA9IHBhcmFtcy51c2VyS2V5LnJlcGxhY2UoL1xccy9nLCAnJyk7XG4gICAgY29uc3QgYmFja3VwS2V5ID0gcGFyYW1zLmJhY2t1cEtleS5yZXBsYWNlKC9cXHMvZywgJycpO1xuICAgIGNvbnN0IGdhc0xpbWl0ID0gbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHRoaXMuc2V0R2FzTGltaXQocGFyYW1zLmdhc0xpbWl0KSk7XG4gICAgY29uc3QgZ2FzUHJpY2UgPSBwYXJhbXMuZWlwMTU1OVxuICAgICAgPyBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocGFyYW1zLmVpcDE1NTkubWF4RmVlUGVyR2FzKVxuICAgICAgOiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4odGhpcy5zZXRHYXNQcmljZShwYXJhbXMuZ2FzUHJpY2UpKTtcblxuICAgIGlmICghdXNlcktleS5zdGFydHNXaXRoKCd4cHViJykgJiYgIXVzZXJLZXkuc3RhcnRzV2l0aCgneHBydicpKSB7XG4gICAgICB0cnkge1xuICAgICAgICB1c2VyS2V5ID0gdGhpcy5iaXRnby5kZWNyeXB0KHtcbiAgICAgICAgICBpbnB1dDogdXNlcktleSxcbiAgICAgICAgICBwYXNzd29yZDogcGFyYW1zLndhbGxldFBhc3NwaHJhc2UsXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEVycm9yIGRlY3J5cHRpbmcgdXNlciBrZXljaGFpbjogJHtlLm1lc3NhZ2V9YCk7XG4gICAgICB9XG4gICAgfVxuICAgIGxldCBiYWNrdXBLZXlBZGRyZXNzO1xuICAgIGxldCBiYWNrdXBTaWduaW5nS2V5O1xuICAgIGlmIChpc1Vuc2lnbmVkU3dlZXApIHtcbiAgICAgIGNvbnN0IGJhY2t1cEhETm9kZSA9IGJpcDMyLmZyb21CYXNlNTgoYmFja3VwS2V5KTtcbiAgICAgIGJhY2t1cFNpZ25pbmdLZXkgPSBiYWNrdXBIRE5vZGUucHVibGljS2V5O1xuICAgICAgYmFja3VwS2V5QWRkcmVzcyA9IGAweCR7b3B0aW9uYWxEZXBzLmV0aFV0aWwucHVibGljVG9BZGRyZXNzKGJhY2t1cFNpZ25pbmdLZXksIHRydWUpLnRvU3RyaW5nKCdoZXgnKX1gO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBEZWNyeXB0IGJhY2t1cCBwcml2YXRlIGtleSBhbmQgZ2V0IGFkZHJlc3NcbiAgICAgIGxldCBiYWNrdXBQcnY7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGJhY2t1cFBydiA9IHRoaXMuYml0Z28uZGVjcnlwdCh7XG4gICAgICAgICAgaW5wdXQ6IGJhY2t1cEtleSxcbiAgICAgICAgICBwYXNzd29yZDogcGFyYW1zLndhbGxldFBhc3NwaHJhc2UsXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEVycm9yIGRlY3J5cHRpbmcgYmFja3VwIGtleWNoYWluOiAke2UubWVzc2FnZX1gKTtcbiAgICAgIH1cblxuICAgICAgY29uc3Qga2V5UGFpciA9IG5ldyBLZXlQYWlyTGliKHsgcHJ2OiBiYWNrdXBQcnYgfSk7XG4gICAgICBiYWNrdXBTaWduaW5nS2V5ID0ga2V5UGFpci5nZXRLZXlzKCkucHJ2O1xuICAgICAgaWYgKCFiYWNrdXBTaWduaW5nS2V5KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignbm8gcHJpdmF0ZSBrZXknKTtcbiAgICAgIH1cbiAgICAgIGJhY2t1cEtleUFkZHJlc3MgPSBrZXlQYWlyLmdldEFkZHJlc3MoKTtcbiAgICB9XG5cbiAgICBjb25zdCBiYWNrdXBLZXlOb25jZSA9IGF3YWl0IHRoaXMuZ2V0QWRkcmVzc05vbmNlKGJhY2t1cEtleUFkZHJlc3MpO1xuICAgIC8vIGdldCBiYWxhbmNlIG9mIGJhY2t1cEtleSB0byBlbnN1cmUgZnVuZHMgYXJlIGF2YWlsYWJsZSB0byBwYXkgZmVlc1xuICAgIGNvbnN0IGJhY2t1cEtleUJhbGFuY2UgPSBhd2FpdCB0aGlzLnF1ZXJ5QWRkcmVzc0JhbGFuY2UoYmFja3VwS2V5QWRkcmVzcyk7XG4gICAgbGV0IHRvdGFsR2FzTmVlZGVkID0gZ2FzUHJpY2UubXVsKGdhc0xpbWl0KTtcblxuICAgIC8vIE9uIG9wdGltaXNtIGNoYWluLCBMMSBmZWVzIGlzIHRvIGJlIHBhaWQgYXMgd2VsbCBhcGFydCBmcm9tIEwyIGZlZXNcbiAgICAvLyBTbyB3ZSBhcmUgYWRkaW5nIHRoZSBhbW91bnQgdGhhdCBjYW4gYmUgdXNlZCB1cCBhcyBsMSBmZWVzXG4gICAgaWYgKHRoaXMuc3RhdGljc0NvaW4/LmZhbWlseSA9PT0gJ29wZXRoJykge1xuICAgICAgdG90YWxHYXNOZWVkZWQgPSB0b3RhbEdhc05lZWRlZC5hZGQobmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKGV0aEdhc0NvbmZpZ3Mub3BldGhHYXNMMUZlZXMpKTtcbiAgICB9XG5cbiAgICBjb25zdCB3ZWlUb0d3ZWkgPSAxMCAqKiA5O1xuICAgIGlmIChiYWNrdXBLZXlCYWxhbmNlLmx0KHRvdGFsR2FzTmVlZGVkKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgQmFja3VwIGtleSBhZGRyZXNzICR7YmFja3VwS2V5QWRkcmVzc30gaGFzIGJhbGFuY2UgJHsoYmFja3VwS2V5QmFsYW5jZSAvIHdlaVRvR3dlaSkudG9TdHJpbmcoKX0gR3dlaS5gICtcbiAgICAgICAgICBgVGhpcyBhZGRyZXNzIG11c3QgaGF2ZSBhIGJhbGFuY2Ugb2YgYXQgbGVhc3QgJHsodG90YWxHYXNOZWVkZWQgLyB3ZWlUb0d3ZWkpLnRvU3RyaW5nKCl9YCArXG4gICAgICAgICAgYCBHd2VpIHRvIHBlcmZvcm0gcmVjb3Zlcmllcy4gVHJ5IHNlbmRpbmcgc29tZSBmdW5kcyB0byB0aGlzIGFkZHJlc3MgdGhlbiByZXRyeS5gXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIGdldCBiYWxhbmNlIG9mIHdhbGxldFxuICAgIGNvbnN0IHR4QW1vdW50ID0gYXdhaXQgdGhpcy5xdWVyeUFkZHJlc3NCYWxhbmNlKHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MpO1xuICAgIGlmIChuZXcgQmlnTnVtYmVyKHR4QW1vdW50KS5pc0xlc3NUaGFuT3JFcXVhbFRvKDApKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1dhbGxldCBkb2VzIG5vdCBoYXZlIGVub3VnaCBmdW5kcyB0byByZWNvdmVyJyk7XG4gICAgfVxuXG4gICAgLy8gYnVpbGQgcmVjaXBpZW50cyBvYmplY3RcbiAgICBjb25zdCByZWNpcGllbnRzID0gW1xuICAgICAge1xuICAgICAgICBhZGRyZXNzOiBwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbixcbiAgICAgICAgYW1vdW50OiB0eEFtb3VudC50b1N0cmluZygxMCksXG4gICAgICB9LFxuICAgIF07XG5cbiAgICAvLyBHZXQgc2VxdWVuY2UgSUQgdXNpbmcgY29udHJhY3QgY2FsbFxuICAgIC8vIHdlIG5lZWQgdG8gd2FpdCBiZXR3ZWVuIG1ha2luZyB0d28gZXhwbG9yZXIgYXBpIGNhbGxzIHRvIGF2b2lkIGdldHRpbmcgYmFubmVkXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMTAwMCkpO1xuICAgIGNvbnN0IHNlcXVlbmNlSWQgPSBhd2FpdCB0aGlzLnF1ZXJ5U2VxdWVuY2VJZChwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzKTtcblxuICAgIGxldCBvcGVyYXRpb25IYXNoLCBzaWduYXR1cmU7XG4gICAgLy8gR2V0IG9wZXJhdGlvbiBoYXNoIGFuZCBzaWduIGl0XG4gICAgaWYgKCFpc1Vuc2lnbmVkU3dlZXApIHtcbiAgICAgIG9wZXJhdGlvbkhhc2ggPSB0aGlzLmdldE9wZXJhdGlvblNoYTNGb3JFeGVjdXRlQW5kQ29uZmlybShyZWNpcGllbnRzLCB0aGlzLmdldERlZmF1bHRFeHBpcmVUaW1lKCksIHNlcXVlbmNlSWQpO1xuICAgICAgc2lnbmF0dXJlID0gVXRpbC5ldGhTaWduTXNnSGFzaChvcGVyYXRpb25IYXNoLCBVdGlsLnhwcnZUb0V0aFByaXZhdGVLZXkodXNlcktleSkpO1xuXG4gICAgICB0cnkge1xuICAgICAgICBVdGlsLmVjUmVjb3ZlckV0aEFkZHJlc3Mob3BlcmF0aW9uSGFzaCwgc2lnbmF0dXJlKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHNpZ25hdHVyZScpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHR4SW5mbyA9IHtcbiAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50c1swXSxcbiAgICAgIGV4cGlyZVRpbWU6IHRoaXMuZ2V0RGVmYXVsdEV4cGlyZVRpbWUoKSxcbiAgICAgIGNvbnRyYWN0U2VxdWVuY2VJZDogc2VxdWVuY2VJZCxcbiAgICAgIG9wZXJhdGlvbkhhc2g6IG9wZXJhdGlvbkhhc2gsXG4gICAgICBzaWduYXR1cmU6IHNpZ25hdHVyZSxcbiAgICAgIGdhc0xpbWl0OiBnYXNMaW1pdC50b1N0cmluZygxMCksXG4gICAgfTtcblxuICAgIGNvbnN0IHR4QnVpbGRlciA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25CdWlsZGVyKCkgYXMgVHJhbnNhY3Rpb25CdWlsZGVyO1xuICAgIHR4QnVpbGRlci5jb3VudGVyKGJhY2t1cEtleU5vbmNlKTtcbiAgICB0eEJ1aWxkZXIuY29udHJhY3QocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyk7XG4gICAgbGV0IHR4RmVlO1xuICAgIGlmIChwYXJhbXMuZWlwMTU1OSkge1xuICAgICAgdHhGZWUgPSB7XG4gICAgICAgIGVpcDE1NTk6IHtcbiAgICAgICAgICBtYXhQcmlvcml0eUZlZVBlckdhczogcGFyYW1zLmVpcDE1NTkubWF4UHJpb3JpdHlGZWVQZXJHYXMsXG4gICAgICAgICAgbWF4RmVlUGVyR2FzOiBwYXJhbXMuZWlwMTU1OS5tYXhGZWVQZXJHYXMsXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgIH0gZWxzZSB7XG4gICAgICB0eEZlZSA9IHsgZmVlOiBnYXNQcmljZS50b1N0cmluZygpIH07XG4gICAgfVxuICAgIHR4QnVpbGRlci5mZWUoe1xuICAgICAgLi4udHhGZWUsXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoKSxcbiAgICB9KTtcbiAgICBjb25zdCB0cmFuc2ZlckJ1aWxkZXIgPSB0eEJ1aWxkZXIudHJhbnNmZXIoKSBhcyBUcmFuc2ZlckJ1aWxkZXI7XG4gICAgdHJhbnNmZXJCdWlsZGVyXG4gICAgICAuY29pbih0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZylcbiAgICAgIC5hbW91bnQocmVjaXBpZW50c1swXS5hbW91bnQpXG4gICAgICAuY29udHJhY3RTZXF1ZW5jZUlkKHNlcXVlbmNlSWQpXG4gICAgICAuZXhwaXJhdGlvblRpbWUodGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpKVxuICAgICAgLnRvKHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uKTtcblxuICAgIGNvbnN0IHR4ID0gYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCk7XG4gICAgaWYgKGlzVW5zaWduZWRTd2VlcCkge1xuICAgICAgY29uc3QgcmVzcG9uc2U6IE9mZmxpbmVWYXVsdFR4SW5mbyA9IHtcbiAgICAgICAgdHhIZXg6IHR4LnRvQnJvYWRjYXN0Rm9ybWF0KCksXG4gICAgICAgIHVzZXJLZXksXG4gICAgICAgIGJhY2t1cEtleSxcbiAgICAgICAgY29pbjogdGhpcy5nZXRDaGFpbigpLFxuICAgICAgICBnYXNQcmljZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKSxcbiAgICAgICAgZ2FzTGltaXQsXG4gICAgICAgIHJlY2lwaWVudHM6IFt0eEluZm8ucmVjaXBpZW50XSxcbiAgICAgICAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiB0eC50b0pzb24oKS50byxcbiAgICAgICAgYW1vdW50OiB0eEluZm8ucmVjaXBpZW50LmFtb3VudCxcbiAgICAgICAgYmFja3VwS2V5Tm9uY2UsXG4gICAgICAgIGVpcDE1NTk6IHBhcmFtcy5laXAxNTU5LFxuICAgICAgfTtcbiAgICAgIF8uZXh0ZW5kKHJlc3BvbnNlLCB0eEluZm8pO1xuICAgICAgcmVzcG9uc2UubmV4dENvbnRyYWN0U2VxdWVuY2VJZCA9IHJlc3BvbnNlLmNvbnRyYWN0U2VxdWVuY2VJZDtcbiAgICAgIHJldHVybiByZXNwb25zZTtcbiAgICB9XG5cbiAgICB0eEJ1aWxkZXJcbiAgICAgIC50cmFuc2ZlcigpXG4gICAgICAuY29pbih0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZylcbiAgICAgIC5rZXkobmV3IEtleVBhaXJMaWIoeyBwcnY6IHVzZXJLZXkgfSkuZ2V0S2V5cygpLnBydiBhcyBzdHJpbmcpO1xuICAgIHR4QnVpbGRlci5zaWduKHsga2V5OiBiYWNrdXBTaWduaW5nS2V5IH0pO1xuXG4gICAgY29uc3Qgc2lnbmVkVHggPSBhd2FpdCB0eEJ1aWxkZXIuYnVpbGQoKTtcblxuICAgIHJldHVybiB7XG4gICAgICBpZDogc2lnbmVkVHgudG9Kc29uKCkuaWQsXG4gICAgICB0eDogc2lnbmVkVHgudG9Ccm9hZGNhc3RGb3JtYXQoKSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkcyBhIHVuc2lnbmVkIChmb3IgY29sZCwgY3VzdG9keSB3YWxsZXQpIG9yXG4gICAqIGhhbGYtc2lnbmVkIChmb3IgaG90IHdhbGxldCkgZXZtIGNyb3NzIGNoYWluIHJlY292ZXJ5IHRyYW5zYWN0aW9uIHdpdGhcbiAgICogc2FtZSBleHBlY3RlZCBhcmd1bWVudHMgYXMgcmVjb3ZlciBtZXRob2QuXG4gICAqIFRoaXMgaGVscHMgcmVjb3ZlciBmdW5kcyBmcm9tIGV2bSBiYXNlZCB3cm9uZyBjaGFpbi5cbiAgICogQHBhcmFtIHtSZWNvdmVyT3B0aW9uc30gcGFyYW1zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFJlY292ZXJ5SW5mbyB8IE9mZmxpbmVWYXVsdFR4SW5mbz59XG4gICAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgcmVjb3ZlckV0aExpa2Vmb3JFdm1CYXNlZFJlY292ZXJ5KFxuICAgIHBhcmFtczogUmVjb3Zlck9wdGlvbnNcbiAgKTogUHJvbWlzZTxSZWNvdmVyeUluZm8gfCBPZmZsaW5lVmF1bHRUeEluZm8+IHtcbiAgICB0aGlzLnZhbGlkYXRlRXZtQmFzZWRSZWNvdmVyeVBhcmFtcyhwYXJhbXMpO1xuXG4gICAgLy8gQ2xlYW4gdXAgd2hpdGVzcGFjZSBmcm9tIGVudGVyZWQgdmFsdWVzXG4gICAgY29uc3QgdXNlcktleSA9IHBhcmFtcy51c2VyS2V5LnJlcGxhY2UoL1xccy9nLCAnJyk7XG4gICAgY29uc3QgYml0Z29GZWVBZGRyZXNzID0gcGFyYW1zLmJpdGdvRmVlQWRkcmVzcz8ucmVwbGFjZSgvXFxzL2csICcnKS50b0xvd2VyQ2FzZSgpIGFzIHN0cmluZztcbiAgICBjb25zdCBiaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcyA9IHBhcmFtcy5iaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcz8ucmVwbGFjZSgvXFxzL2csICcnKS50b0xvd2VyQ2FzZSgpIGFzIHN0cmluZztcbiAgICBjb25zdCByZWNvdmVyeURlc3RpbmF0aW9uID0gcGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24/LnJlcGxhY2UoL1xccy9nLCAnJykudG9Mb3dlckNhc2UoKSBhcyBzdHJpbmc7XG4gICAgY29uc3Qgd2FsbGV0Q29udHJhY3RBZGRyZXNzID0gcGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcz8ucmVwbGFjZSgvXFxzL2csICcnKS50b0xvd2VyQ2FzZSgpIGFzIHN0cmluZztcbiAgICBjb25zdCB0b2tlbkNvbnRyYWN0QWRkcmVzcyA9IHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcz8ucmVwbGFjZSgvXFxzL2csICcnKS50b0xvd2VyQ2FzZSgpIGFzIHN0cmluZztcblxuICAgIGxldCB1c2VyU2lnbmluZ0tleTtcbiAgICBsZXQgdXNlcktleVBydjtcbiAgICBpZiAocGFyYW1zLndhbGxldFBhc3NwaHJhc2UpIHtcbiAgICAgIGlmICghdXNlcktleS5zdGFydHNXaXRoKCd4cHViJykgJiYgIXVzZXJLZXkuc3RhcnRzV2l0aCgneHBydicpKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgdXNlcktleVBydiA9IHRoaXMuYml0Z28uZGVjcnlwdCh7XG4gICAgICAgICAgICBpbnB1dDogdXNlcktleSxcbiAgICAgICAgICAgIHBhc3N3b3JkOiBwYXJhbXMud2FsbGV0UGFzc3BocmFzZSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgZGVjcnlwdGluZyB1c2VyIGtleWNoYWluOiAke2UubWVzc2FnZX1gKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBjb25zdCBrZXlQYWlyID0gbmV3IEtleVBhaXJMaWIoeyBwcnY6IHVzZXJLZXlQcnYgfSk7XG4gICAgICB1c2VyU2lnbmluZ0tleSA9IGtleVBhaXIuZ2V0S2V5cygpLnBydjtcbiAgICAgIGlmICghdXNlclNpZ25pbmdLZXkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdubyBwcml2YXRlIGtleScpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IGdhc0xpbWl0ID0gbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHRoaXMuc2V0R2FzTGltaXQocGFyYW1zLmdhc0xpbWl0KSk7XG4gICAgY29uc3QgZ2FzUHJpY2UgPSBwYXJhbXMuZWlwMTU1OVxuICAgICAgPyBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocGFyYW1zLmVpcDE1NTkubWF4RmVlUGVyR2FzKVxuICAgICAgOiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4odGhpcy5zZXRHYXNQcmljZShwYXJhbXMuZ2FzUHJpY2UpKTtcblxuICAgIGNvbnN0IGJpdGdvRmVlQWRkcmVzc05vbmNlID0gYXdhaXQgdGhpcy5nZXRBZGRyZXNzTm9uY2UoYml0Z29GZWVBZGRyZXNzKTtcblxuICAgIC8vIGdldCBiYWxhbmNlIG9mIGJpdGdvRmVlQWRkcmVzcyB0byBlbnN1cmUgZnVuZHMgYXJlIGF2YWlsYWJsZSB0byBwYXkgZmVlc1xuICAgIGNvbnN0IGJpdGdvRmVlQWRkcmVzc0JhbGFuY2UgPSBhd2FpdCB0aGlzLnF1ZXJ5QWRkcmVzc0JhbGFuY2UoYml0Z29GZWVBZGRyZXNzKTtcbiAgICBjb25zdCB0b3RhbEdhc05lZWRlZCA9IGdhc1ByaWNlLm11bChnYXNMaW1pdCk7XG4gICAgY29uc3Qgd2VpVG9Hd2VpID0gMTAgKiogOTtcbiAgICBpZiAoYml0Z29GZWVBZGRyZXNzQmFsYW5jZS5sdCh0b3RhbEdhc05lZWRlZCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYEZlZSBhZGRyZXNzICR7Yml0Z29GZWVBZGRyZXNzfSBoYXMgYmFsYW5jZSAkeyhiaXRnb0ZlZUFkZHJlc3NCYWxhbmNlIC8gd2VpVG9Hd2VpKS50b1N0cmluZygpfSBHd2VpLmAgK1xuICAgICAgICAgIGBUaGlzIGFkZHJlc3MgbXVzdCBoYXZlIGEgYmFsYW5jZSBvZiBhdCBsZWFzdCAkeyh0b3RhbEdhc05lZWRlZCAvIHdlaVRvR3dlaSkudG9TdHJpbmcoKX1gICtcbiAgICAgICAgICBgIEd3ZWkgdG8gcGVyZm9ybSByZWNvdmVyaWVzLiBUcnkgc2VuZGluZyBzb21lICR7dGhpcy5nZXRDaGFpbigpfSB0byB0aGlzIGFkZHJlc3MgdGhlbiByZXRyeS5gXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmICh0b2tlbkNvbnRyYWN0QWRkcmVzcykge1xuICAgICAgcmV0dXJuIHRoaXMucmVjb3ZlckV0aExpa2VUb2tlbmZvckV2bUJhc2VkUmVjb3ZlcnkoXG4gICAgICAgIHBhcmFtcyxcbiAgICAgICAgYml0Z29GZWVBZGRyZXNzTm9uY2UsXG4gICAgICAgIGdhc0xpbWl0LFxuICAgICAgICBnYXNQcmljZSxcbiAgICAgICAgdXNlcktleSxcbiAgICAgICAgdXNlclNpZ25pbmdLZXlcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gZ2V0IGJhbGFuY2Ugb2Ygd2FsbGV0XG4gICAgY29uc3QgdHhBbW91bnQgPSBhd2FpdCB0aGlzLnF1ZXJ5QWRkcmVzc0JhbGFuY2Uod2FsbGV0Q29udHJhY3RBZGRyZXNzKTtcblxuICAgIGNvbnN0IGJpdGdvRmVlUGVyY2VudGFnZSA9IDA7IC8vIFRPRE86IEJHLTcxOTEyIGNhbiBjaGFuZ2UgdGhlIGZlZSUgaGVyZS5cbiAgICBjb25zdCBiaXRnb0ZlZUFtb3VudCA9IHR4QW1vdW50ICogKGJpdGdvRmVlUGVyY2VudGFnZSAvIDEwMCk7XG5cbiAgICAvLyBidWlsZCByZWNpcGllbnRzIG9iamVjdFxuICAgIGNvbnN0IHJlY2lwaWVudHM6IFJlY2lwaWVudFtdID0gW1xuICAgICAge1xuICAgICAgICBhZGRyZXNzOiByZWNvdmVyeURlc3RpbmF0aW9uLFxuICAgICAgICBhbW91bnQ6IG5ldyBCaWdOdW1iZXIodHhBbW91bnQpLm1pbnVzKGJpdGdvRmVlQW1vdW50KS50b0ZpeGVkKCksXG4gICAgICB9LFxuICAgIF07XG5cbiAgICBpZiAoYml0Z29GZWVQZXJjZW50YWdlID4gMCkge1xuICAgICAgaWYgKF8uaXNVbmRlZmluZWQoYml0Z29EZXN0aW5hdGlvbkFkZHJlc3MpIHx8ICF0aGlzLmlzVmFsaWRBZGRyZXNzKGJpdGdvRGVzdGluYXRpb25BZGRyZXNzKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgYml0Z29EZXN0aW5hdGlvbkFkZHJlc3MnKTtcbiAgICAgIH1cblxuICAgICAgcmVjaXBpZW50cy5wdXNoKHtcbiAgICAgICAgYWRkcmVzczogYml0Z29EZXN0aW5hdGlvbkFkZHJlc3MsXG4gICAgICAgIGFtb3VudDogYml0Z29GZWVBbW91bnQudG9TdHJpbmcoMTApLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gY2FsY3VsYXRlIGJhdGNoIGRhdGFcbiAgICBjb25zdCBCQVRDSF9NRVRIT0RfTkFNRSA9ICdiYXRjaCc7XG4gICAgY29uc3QgQkFUQ0hfTUVUSE9EX1RZUEVTID0gWydhZGRyZXNzW10nLCAndWludDI1NltdJ107XG4gICAgY29uc3QgYmF0Y2hFeGVjdXRpb25JbmZvID0gdGhpcy5nZXRCYXRjaEV4ZWN1dGlvbkluZm8ocmVjaXBpZW50cyk7XG4gICAgY29uc3QgYmF0Y2hEYXRhID0gb3B0aW9uYWxEZXBzLmV0aFV0aWwuYWRkSGV4UHJlZml4KFxuICAgICAgdGhpcy5nZXRNZXRob2RDYWxsRGF0YShCQVRDSF9NRVRIT0RfTkFNRSwgQkFUQ0hfTUVUSE9EX1RZUEVTLCBiYXRjaEV4ZWN1dGlvbkluZm8udmFsdWVzKS50b1N0cmluZygnaGV4JylcbiAgICApO1xuXG4gICAgLy8gR2V0IHNlcXVlbmNlIElEIHVzaW5nIGNvbnRyYWN0IGNhbGxcbiAgICAvLyB3ZSBuZWVkIHRvIHdhaXQgYmV0d2VlbiBtYWtpbmcgdHdvIGV4cGxvcmVyIGFwaSBjYWxscyB0byBhdm9pZCBnZXR0aW5nIGJhbm5lZFxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDEwMDApKTtcbiAgICBjb25zdCBzZXF1ZW5jZUlkID0gYXdhaXQgdGhpcy5xdWVyeVNlcXVlbmNlSWQod2FsbGV0Q29udHJhY3RBZGRyZXNzKTtcblxuICAgIGNvbnN0IHR4SW5mbyA9IHtcbiAgICAgIHJlY2lwaWVudHM6IHJlY2lwaWVudHMsXG4gICAgICBleHBpcmVUaW1lOiB0aGlzLmdldERlZmF1bHRFeHBpcmVUaW1lKCksXG4gICAgICBjb250cmFjdFNlcXVlbmNlSWQ6IHNlcXVlbmNlSWQsXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoMTApLFxuICAgICAgaXNFdm1CYXNlZENyb3NzQ2hhaW5SZWNvdmVyeTogdHJ1ZSxcbiAgICB9O1xuXG4gICAgY29uc3QgbmV0d29yayA9IHRoaXMuZ2V0TmV0d29yaygpO1xuICAgIGNvbnN0IGJhdGNoZXJDb250cmFjdEFkZHJlc3MgPSBuZXR3b3JrPy5iYXRjaGVyQ29udHJhY3RBZGRyZXNzIGFzIHN0cmluZztcblxuICAgIGNvbnN0IHR4QnVpbGRlciA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25CdWlsZGVyKCkgYXMgVHJhbnNhY3Rpb25CdWlsZGVyO1xuICAgIHR4QnVpbGRlci5jb3VudGVyKGJpdGdvRmVlQWRkcmVzc05vbmNlKTtcbiAgICB0eEJ1aWxkZXIuY29udHJhY3Qod2FsbGV0Q29udHJhY3RBZGRyZXNzKTtcbiAgICBsZXQgdHhGZWU7XG4gICAgaWYgKHBhcmFtcy5laXAxNTU5KSB7XG4gICAgICB0eEZlZSA9IHtcbiAgICAgICAgZWlwMTU1OToge1xuICAgICAgICAgIG1heFByaW9yaXR5RmVlUGVyR2FzOiBwYXJhbXMuZWlwMTU1OS5tYXhQcmlvcml0eUZlZVBlckdhcyxcbiAgICAgICAgICBtYXhGZWVQZXJHYXM6IHBhcmFtcy5laXAxNTU5Lm1heEZlZVBlckdhcyxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIHR4RmVlID0geyBmZWU6IGdhc1ByaWNlLnRvU3RyaW5nKCkgfTtcbiAgICB9XG4gICAgdHhCdWlsZGVyLmZlZSh7XG4gICAgICAuLi50eEZlZSxcbiAgICAgIGdhc0xpbWl0OiBnYXNMaW1pdC50b1N0cmluZygpLFxuICAgIH0pO1xuXG4gICAgY29uc3QgdHJhbnNmZXJCdWlsZGVyID0gdHhCdWlsZGVyLnRyYW5zZmVyKCkgYXMgVHJhbnNmZXJCdWlsZGVyO1xuICAgIGlmICghYmF0Y2hlckNvbnRyYWN0QWRkcmVzcykge1xuICAgICAgdHJhbnNmZXJCdWlsZGVyXG4gICAgICAgIC5jb2luKHRoaXMuc3RhdGljc0NvaW4/Lm5hbWUgYXMgc3RyaW5nKVxuICAgICAgICAuYW1vdW50KGJhdGNoRXhlY3V0aW9uSW5mby50b3RhbEFtb3VudClcbiAgICAgICAgLmNvbnRyYWN0U2VxdWVuY2VJZChzZXF1ZW5jZUlkKVxuICAgICAgICAuZXhwaXJhdGlvblRpbWUodGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpKVxuICAgICAgICAudG8ocmVjb3ZlcnlEZXN0aW5hdGlvbik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRyYW5zZmVyQnVpbGRlclxuICAgICAgICAuY29pbih0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZylcbiAgICAgICAgLmFtb3VudChiYXRjaEV4ZWN1dGlvbkluZm8udG90YWxBbW91bnQpXG4gICAgICAgIC5jb250cmFjdFNlcXVlbmNlSWQoc2VxdWVuY2VJZClcbiAgICAgICAgLmV4cGlyYXRpb25UaW1lKHRoaXMuZ2V0RGVmYXVsdEV4cGlyZVRpbWUoKSlcbiAgICAgICAgLnRvKGJhdGNoZXJDb250cmFjdEFkZHJlc3MpXG4gICAgICAgIC5kYXRhKGJhdGNoRGF0YSk7XG4gICAgfVxuXG4gICAgaWYgKHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKSB7XG4gICAgICB0cmFuc2ZlckJ1aWxkZXIua2V5KHVzZXJTaWduaW5nS2V5KTtcbiAgICB9XG5cbiAgICAvLyBJZiB0aGUgaW50ZW5kZWQgY2hhaW4gaXMgYXJiaXRydW0gb3Igb3B0aW1pc20sIHdlIG5lZWQgdG8gdXNlIHdhbGxldCB2ZXJzaW9uIDRcbiAgICAvLyBzaW5jZSB0aGVzZSBjb250cmFjdHMgY29uc3RydWN0IG9wZXJhdGlvbkhhc2ggZGlmZmVyZW50bHlcbiAgICBpZiAocGFyYW1zLmludGVuZGVkQ2hhaW4gJiYgWydhcmJldGgnLCAnb3BldGgnXS5pbmNsdWRlcyhjb2lucy5nZXQocGFyYW1zLmludGVuZGVkQ2hhaW4pLmZhbWlseSkpIHtcbiAgICAgIHR4QnVpbGRlci53YWxsZXRWZXJzaW9uKDQpO1xuICAgIH1cblxuICAgIGNvbnN0IHR4ID0gYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCk7XG5cbiAgICBjb25zdCByZXNwb25zZTogT2ZmbGluZVZhdWx0VHhJbmZvID0ge1xuICAgICAgdHhIZXg6IHR4LnRvQnJvYWRjYXN0Rm9ybWF0KCksXG4gICAgICB1c2VyS2V5LFxuICAgICAgY29pbjogdGhpcy5nZXRDaGFpbigpLFxuICAgICAgZ2FzUHJpY2U6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSW50KGdhc1ByaWNlKS50b0ZpeGVkKCksXG4gICAgICBnYXNMaW1pdCxcbiAgICAgIHJlY2lwaWVudHM6IHR4SW5mby5yZWNpcGllbnRzLFxuICAgICAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiB0eC50b0pzb24oKS50byxcbiAgICAgIGFtb3VudDogYmF0Y2hFeGVjdXRpb25JbmZvLnRvdGFsQW1vdW50LFxuICAgICAgYmFja3VwS2V5Tm9uY2U6IGJpdGdvRmVlQWRkcmVzc05vbmNlLFxuICAgICAgZWlwMTU1OTogcGFyYW1zLmVpcDE1NTksXG4gICAgfTtcbiAgICBfLmV4dGVuZChyZXNwb25zZSwgdHhJbmZvKTtcbiAgICByZXNwb25zZS5uZXh0Q29udHJhY3RTZXF1ZW5jZUlkID0gcmVzcG9uc2UuY29udHJhY3RTZXF1ZW5jZUlkO1xuXG4gICAgaWYgKHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKSB7XG4gICAgICBjb25zdCBoYWxmU2lnbmVkVHhuOiBIYWxmU2lnbmVkVHJhbnNhY3Rpb24gPSB7XG4gICAgICAgIGhhbGZTaWduZWQ6IHtcbiAgICAgICAgICB0eEhleDogdHgudG9Ccm9hZGNhc3RGb3JtYXQoKSxcbiAgICAgICAgICByZWNpcGllbnRzOiB0eEluZm8ucmVjaXBpZW50cyxcbiAgICAgICAgICBleHBpcmVUaW1lOiB0eEluZm8uZXhwaXJlVGltZSxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgICBfLmV4dGVuZChyZXNwb25zZSwgaGFsZlNpZ25lZFR4bik7XG5cbiAgICAgIGNvbnN0IGZlZXNVc2VkOiBGZWVzVXNlZCA9IHtcbiAgICAgICAgZ2FzUHJpY2U6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSW50KGdhc1ByaWNlKS50b0ZpeGVkKCksXG4gICAgICAgIGdhc0xpbWl0OiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0ludChnYXNMaW1pdCkudG9GaXhlZCgpLFxuICAgICAgfTtcbiAgICAgIHJlc3BvbnNlWydmZWVzVXNlZCddID0gZmVlc1VzZWQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3BvbnNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFF1ZXJ5IGV4cGxvcmVyIGZvciB0aGUgYmFsYW5jZSBvZiBhbiBhZGRyZXNzIGZvciBhIHRva2VuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0b2tlbkNvbnRyYWN0QWRkcmVzcyAtIGFkZHJlc3Mgd2hlcmUgdGhlIHRva2VuIHNtYXJ0IGNvbnRyYWN0IGlzIGhvc3RlZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gd2FsbGV0Q29udHJhY3RBZGRyZXNzIC0gYWRkcmVzcyBvZiB0aGUgd2FsbGV0XG4gICAqIEByZXR1cm5zIHtCaWdOdW1iZXJ9IHRva2VuIGJhbGFhbmNlIGluIGJhc2UgdW5pdHNcbiAgICovXG4gIGFzeW5jIHF1ZXJ5QWRkcmVzc1Rva2VuQmFsYW5jZSh0b2tlbkNvbnRyYWN0QWRkcmVzczogc3RyaW5nLCB3YWxsZXRDb250cmFjdEFkZHJlc3M6IHN0cmluZyk6IFByb21pc2U8YW55PiB7XG4gICAgaWYgKCFvcHRpb25hbERlcHMuZXRoVXRpbC5pc1ZhbGlkQWRkcmVzcyh0b2tlbkNvbnRyYWN0QWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignY2Fubm90IGdldCBiYWxhbmNlIGZvciBpbnZhbGlkIHRva2VuIGFkZHJlc3MnKTtcbiAgICB9XG4gICAgaWYgKCFvcHRpb25hbERlcHMuZXRoVXRpbC5pc1ZhbGlkQWRkcmVzcyh3YWxsZXRDb250cmFjdEFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2Nhbm5vdCBnZXQgdG9rZW4gYmFsYW5jZSBmb3IgaW52YWxpZCB3YWxsZXQgYWRkcmVzcycpO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMucmVjb3ZlcnlCbG9ja2NoYWluRXhwbG9yZXJRdWVyeSh7XG4gICAgICBtb2R1bGU6ICdhY2NvdW50JyxcbiAgICAgIGFjdGlvbjogJ3Rva2VuYmFsYW5jZScsXG4gICAgICBjb250cmFjdGFkZHJlc3M6IHRva2VuQ29udHJhY3RBZGRyZXNzLFxuICAgICAgYWRkcmVzczogd2FsbGV0Q29udHJhY3RBZGRyZXNzLFxuICAgICAgdGFnOiAnbGF0ZXN0JyxcbiAgICB9KTtcbiAgICAvLyB0aHJvdyBpZiB0aGUgcmVzdWx0IGRvZXMgbm90IGV4aXN0IG9yIHRoZSByZXN1bHQgaXMgbm90IGEgdmFsaWQgbnVtYmVyXG4gICAgaWYgKCFyZXN1bHQgfHwgIXJlc3VsdC5yZXN1bHQgfHwgaXNOYU4ocmVzdWx0LnJlc3VsdCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYENvdWxkIG5vdCBvYnRhaW4gdG9rZW4gYWRkcmVzcyBiYWxhbmNlIGZvciAke3Rva2VuQ29udHJhY3RBZGRyZXNzfSBmcm9tIEV0aGVyc2NhbiwgZ290OiAke3Jlc3VsdC5yZXN1bHR9YFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihyZXN1bHQucmVzdWx0LCAxMCk7XG4gIH1cblxuICBhc3luYyByZWNvdmVyRXRoTGlrZVRva2VuZm9yRXZtQmFzZWRSZWNvdmVyeShcbiAgICBwYXJhbXM6IFJlY292ZXJPcHRpb25zLFxuICAgIGJpdGdvRmVlQWRkcmVzc05vbmNlOiBudW1iZXIsXG4gICAgZ2FzTGltaXQsXG4gICAgZ2FzUHJpY2UsXG4gICAgdXNlcktleSxcbiAgICB1c2VyU2lnbmluZ0tleVxuICApIHtcbiAgICAvLyBnZXQgdG9rZW4gYmFsYW5jZSBvZiB3YWxsZXRcbiAgICBjb25zdCB0eEFtb3VudCA9IGF3YWl0IHRoaXMucXVlcnlBZGRyZXNzVG9rZW5CYWxhbmNlKFxuICAgICAgcGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzIGFzIHN0cmluZyxcbiAgICAgIHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3NcbiAgICApO1xuXG4gICAgLy8gYnVpbGQgcmVjaXBpZW50cyBvYmplY3RcbiAgICBjb25zdCByZWNpcGllbnRzOiBSZWNpcGllbnRbXSA9IFtcbiAgICAgIHtcbiAgICAgICAgYWRkcmVzczogcGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24sXG4gICAgICAgIGFtb3VudDogbmV3IEJpZ051bWJlcih0eEFtb3VudCkudG9GaXhlZCgpLFxuICAgICAgfSxcbiAgICBdO1xuXG4gICAgLy8gR2V0IHNlcXVlbmNlIElEIHVzaW5nIGNvbnRyYWN0IGNhbGxcbiAgICAvLyB3ZSBuZWVkIHRvIHdhaXQgYmV0d2VlbiBtYWtpbmcgdHdvIGV4cGxvcmVyIGFwaSBjYWxscyB0byBhdm9pZCBnZXR0aW5nIGJhbm5lZFxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDEwMDApKTtcbiAgICBjb25zdCBzZXF1ZW5jZUlkID0gYXdhaXQgdGhpcy5xdWVyeVNlcXVlbmNlSWQocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyk7XG5cbiAgICBjb25zdCB0eEluZm8gPSB7XG4gICAgICByZWNpcGllbnRzOiByZWNpcGllbnRzLFxuICAgICAgZXhwaXJlVGltZTogdGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpLFxuICAgICAgY29udHJhY3RTZXF1ZW5jZUlkOiBzZXF1ZW5jZUlkLFxuICAgICAgZ2FzTGltaXQ6IGdhc0xpbWl0LnRvU3RyaW5nKDEwKSxcbiAgICAgIGlzRXZtQmFzZWRDcm9zc0NoYWluUmVjb3Zlcnk6IHRydWUsXG4gICAgfTtcblxuICAgIGNvbnN0IHR4QnVpbGRlciA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25CdWlsZGVyKCkgYXMgVHJhbnNhY3Rpb25CdWlsZGVyO1xuICAgIHR4QnVpbGRlci5jb3VudGVyKGJpdGdvRmVlQWRkcmVzc05vbmNlKTtcbiAgICB0eEJ1aWxkZXIuY29udHJhY3QocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyBhcyBzdHJpbmcpO1xuICAgIGxldCB0eEZlZTtcbiAgICBpZiAocGFyYW1zLmVpcDE1NTkpIHtcbiAgICAgIHR4RmVlID0ge1xuICAgICAgICBlaXAxNTU5OiB7XG4gICAgICAgICAgbWF4UHJpb3JpdHlGZWVQZXJHYXM6IHBhcmFtcy5laXAxNTU5Lm1heFByaW9yaXR5RmVlUGVyR2FzLFxuICAgICAgICAgIG1heEZlZVBlckdhczogcGFyYW1zLmVpcDE1NTkubWF4RmVlUGVyR2FzLFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgdHhGZWUgPSB7IGZlZTogZ2FzUHJpY2UudG9TdHJpbmcoKSB9O1xuICAgIH1cbiAgICB0eEJ1aWxkZXIuZmVlKHtcbiAgICAgIC4uLnR4RmVlLFxuICAgICAgZ2FzTGltaXQ6IGdhc0xpbWl0LnRvU3RyaW5nKCksXG4gICAgfSk7XG5cbiAgICBjb25zdCB0cmFuc2ZlckJ1aWxkZXIgPSB0eEJ1aWxkZXIudHJhbnNmZXIoKSBhcyBUcmFuc2ZlckJ1aWxkZXI7XG5cbiAgICBjb25zdCBuZXR3b3JrID0gdGhpcy5nZXROZXR3b3JrKCk7XG4gICAgY29uc3QgdG9rZW4gPSBnZXRUb2tlbihcbiAgICAgIHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcyBhcyBzdHJpbmcsXG4gICAgICBuZXR3b3JrIGFzIEV0aExpa2VOZXR3b3JrLFxuICAgICAgdGhpcy5zdGF0aWNzQ29pbj8uZmFtaWx5IGFzIHN0cmluZ1xuICAgICk/Lm5hbWUgYXMgc3RyaW5nO1xuXG4gICAgdHJhbnNmZXJCdWlsZGVyXG4gICAgICAuY29pbih0b2tlbilcbiAgICAgIC5hbW91bnQodHhBbW91bnQpXG4gICAgICAuY29udHJhY3RTZXF1ZW5jZUlkKHNlcXVlbmNlSWQpXG4gICAgICAuZXhwaXJhdGlvblRpbWUodGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpKVxuICAgICAgLnRvKHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uKVxuICAgICAgLmNvaW4odG9rZW4pO1xuXG4gICAgaWYgKHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKSB7XG4gICAgICB0eEJ1aWxkZXIudHJhbnNmZXIoKS5rZXkodXNlclNpZ25pbmdLZXkpO1xuICAgIH1cbiAgICAvLyBJZiB0aGUgaW50ZW5kZWQgY2hhaW4gaXMgYXJiaXRydW0gb3Igb3B0aW1pc20sIHdlIG5lZWQgdG8gdXNlIHdhbGxldCB2ZXJzaW9uIDRcbiAgICAvLyBzaW5jZSB0aGVzZSBjb250cmFjdHMgY29uc3RydWN0IG9wZXJhdGlvbkhhc2ggZGlmZmVyZW50bHlcbiAgICBpZiAocGFyYW1zLmludGVuZGVkQ2hhaW4gJiYgWydhcmJldGgnLCAnb3BldGgnXS5pbmNsdWRlcyhjb2lucy5nZXQocGFyYW1zLmludGVuZGVkQ2hhaW4pLmZhbWlseSkpIHtcbiAgICAgIHR4QnVpbGRlci53YWxsZXRWZXJzaW9uKDQpO1xuICAgIH1cblxuICAgIGNvbnN0IHR4ID0gYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCk7XG5cbiAgICBjb25zdCByZXNwb25zZTogT2ZmbGluZVZhdWx0VHhJbmZvID0ge1xuICAgICAgdHhIZXg6IHR4LnRvQnJvYWRjYXN0Rm9ybWF0KCksXG4gICAgICB1c2VyS2V5LFxuICAgICAgY29pbjogdG9rZW4sXG4gICAgICBnYXNQcmljZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKSxcbiAgICAgIGdhc0xpbWl0LFxuICAgICAgcmVjaXBpZW50czogdHhJbmZvLnJlY2lwaWVudHMsXG4gICAgICB3YWxsZXRDb250cmFjdEFkZHJlc3M6IHR4LnRvSnNvbigpLnRvLFxuICAgICAgYW1vdW50OiB0eEFtb3VudC50b1N0cmluZygpLFxuICAgICAgYmFja3VwS2V5Tm9uY2U6IGJpdGdvRmVlQWRkcmVzc05vbmNlLFxuICAgICAgZWlwMTU1OTogcGFyYW1zLmVpcDE1NTksXG4gICAgfTtcbiAgICBfLmV4dGVuZChyZXNwb25zZSwgdHhJbmZvKTtcbiAgICByZXNwb25zZS5uZXh0Q29udHJhY3RTZXF1ZW5jZUlkID0gcmVzcG9uc2UuY29udHJhY3RTZXF1ZW5jZUlkO1xuXG4gICAgaWYgKHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKSB7XG4gICAgICBjb25zdCBoYWxmU2lnbmVkVHhuOiBIYWxmU2lnbmVkVHJhbnNhY3Rpb24gPSB7XG4gICAgICAgIGhhbGZTaWduZWQ6IHtcbiAgICAgICAgICB0eEhleDogdHgudG9Ccm9hZGNhc3RGb3JtYXQoKSxcbiAgICAgICAgICByZWNpcGllbnRzOiB0eEluZm8ucmVjaXBpZW50cyxcbiAgICAgICAgICBleHBpcmVUaW1lOiB0eEluZm8uZXhwaXJlVGltZSxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgICBfLmV4dGVuZChyZXNwb25zZSwgaGFsZlNpZ25lZFR4bik7XG5cbiAgICAgIGNvbnN0IGZlZXNVc2VkOiBGZWVzVXNlZCA9IHtcbiAgICAgICAgZ2FzUHJpY2U6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSW50KGdhc1ByaWNlKS50b0ZpeGVkKCksXG4gICAgICAgIGdhc0xpbWl0OiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0ludChnYXNMaW1pdCkudG9GaXhlZCgpLFxuICAgICAgfTtcbiAgICAgIHJlc3BvbnNlWydmZWVzVXNlZCddID0gZmVlc1VzZWQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3BvbnNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIGV2bSBiYXNlZCBjcm9zcyBjaGFpbiByZWNvdmVyeSBwYXJhbXNcbiAgICogQHBhcmFtIHBhcmFtcyB7UmVjb3Zlck9wdGlvbnN9XG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgdmFsaWRhdGVFdm1CYXNlZFJlY292ZXJ5UGFyYW1zKHBhcmFtczogUmVjb3Zlck9wdGlvbnMpOiB2b2lkIHtcbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMuYml0Z29GZWVBZGRyZXNzKSB8fCAhdGhpcy5pc1ZhbGlkQWRkcmVzcyhwYXJhbXMuYml0Z29GZWVBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkIGJpdGdvRmVlQWRkcmVzcycpO1xuICAgIH1cblxuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MpIHx8ICF0aGlzLmlzVmFsaWRBZGRyZXNzKHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgd2FsbGV0Q29udHJhY3RBZGRyZXNzJyk7XG4gICAgfVxuXG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24pIHx8ICF0aGlzLmlzVmFsaWRBZGRyZXNzKHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkIHJlY292ZXJ5RGVzdGluYXRpb24nKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIHR5cGVzLCB2YWx1ZXMsIGFuZCB0b3RhbCBhbW91bnQgaW4gd2VpIHRvIHNlbmQgaW4gYSBiYXRjaCB0cmFuc2FjdGlvbiwgdXNpbmcgdGhlIG1ldGhvZCBzaWduYXR1cmVcbiAgICogYGRpc3RyaWJ1dGVCYXRjaChhZGRyZXNzW10sIHVpbnQyNTZbXSlgXG4gICAqIEBwYXJhbSB7UmVjaXBpZW50W119IHJlY2lwaWVudHMgLSB0cmFuc2FjdGlvbiByZWNpcGllbnRzXG4gICAqIEByZXR1cm5zIHtHZXRCYXRjaEV4ZWN1dGlvbkluZm9SVH0gaW5mb3JtYXRpb24gbmVlZGVkIHRvIGV4ZWN1dGUgdGhlIGJhdGNoIHRyYW5zYWN0aW9uXG4gICAqL1xuICBnZXRCYXRjaEV4ZWN1dGlvbkluZm8ocmVjaXBpZW50czogUmVjaXBpZW50W10pOiBHZXRCYXRjaEV4ZWN1dGlvbkluZm9SVCB7XG4gICAgY29uc3QgYWRkcmVzc2VzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGNvbnN0IGFtb3VudHM6IHN0cmluZ1tdID0gW107XG4gICAgbGV0IHN1bSA9IG5ldyBCaWdOdW1iZXIoJzAnKTtcbiAgICBfLmZvckVhY2gocmVjaXBpZW50cywgKHsgYWRkcmVzcywgYW1vdW50IH0pID0+IHtcbiAgICAgIGFkZHJlc3Nlcy5wdXNoKGFkZHJlc3MpO1xuICAgICAgYW1vdW50cy5wdXNoKGFtb3VudCBhcyBzdHJpbmcpO1xuICAgICAgc3VtID0gc3VtLnBsdXMoYW1vdW50KTtcbiAgICB9KTtcblxuICAgIHJldHVybiB7XG4gICAgICB2YWx1ZXM6IFthZGRyZXNzZXMsIGFtb3VudHNdLFxuICAgICAgdG90YWxBbW91bnQ6IHN1bS50b0ZpeGVkKCksXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIGRhdGEgcmVxdWlyZWQgdG8gbWFrZSBhbiBFVEggZnVuY3Rpb24gY2FsbCBkZWZpbmVkIGJ5IHRoZSBnaXZlbiB0eXBlcyBhbmQgdmFsdWVzXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBmdW5jdGlvbk5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgZnVuY3Rpb24gYmVpbmcgY2FsbGVkLCBlLmcuIHRyYW5zZmVyXG4gICAqIEBwYXJhbSB0eXBlcyBUaGUgdHlwZXMgb2YgdGhlIGZ1bmN0aW9uIGNhbGwgaW4gb3JkZXJcbiAgICogQHBhcmFtIHZhbHVlcyBUaGUgdmFsdWVzIG9mIHRoZSBmdW5jdGlvbiBjYWxsIGluIG9yZGVyXG4gICAqIEByZXR1cm4ge0J1ZmZlcn0gVGhlIGNvbWJpbmVkIGRhdGEgZm9yIHRoZSBmdW5jdGlvbiBjYWxsXG4gICAqL1xuICBnZXRNZXRob2RDYWxsRGF0YSA9IChmdW5jdGlvbk5hbWUsIHR5cGVzLCB2YWx1ZXMpID0+IHtcbiAgICByZXR1cm4gQnVmZmVyLmNvbmNhdChbXG4gICAgICAvLyBmdW5jdGlvbiBzaWduYXR1cmVcbiAgICAgIG9wdGlvbmFsRGVwcy5ldGhBYmkubWV0aG9kSUQoZnVuY3Rpb25OYW1lLCB0eXBlcyksXG4gICAgICAvLyBmdW5jdGlvbiBhcmd1bWVudHNcbiAgICAgIG9wdGlvbmFsRGVwcy5ldGhBYmkucmF3RW5jb2RlKHR5cGVzLCB2YWx1ZXMpLFxuICAgIF0pO1xuICB9O1xuXG4gIC8qKlxuICAgKiBCdWlsZCBhcmd1bWVudHMgdG8gY2FsbCB0aGUgc2VuZCBtZXRob2Qgb24gdGhlIHdhbGxldCBjb250cmFjdFxuICAgKiBAcGFyYW0gdHhJbmZvXG4gICAqL1xuICBnZXRTZW5kTWV0aG9kQXJncyh0eEluZm86IEdldFNlbmRNZXRob2RBcmdzT3B0aW9ucyk6IFNlbmRNZXRob2RBcmdzW10ge1xuICAgIC8vIE1ldGhvZCBzaWduYXR1cmUgaXNcbiAgICAvLyBzZW5kTXVsdGlTaWcoYWRkcmVzcyB0b0FkZHJlc3MsIHVpbnQgdmFsdWUsIGJ5dGVzIGRhdGEsIHVpbnQgZXhwaXJlVGltZSwgdWludCBzZXF1ZW5jZUlkLCBieXRlcyBzaWduYXR1cmUpXG4gICAgcmV0dXJuIFtcbiAgICAgIHtcbiAgICAgICAgbmFtZTogJ3RvQWRkcmVzcycsXG4gICAgICAgIHR5cGU6ICdhZGRyZXNzJyxcbiAgICAgICAgdmFsdWU6IHR4SW5mby5yZWNpcGllbnQuYWRkcmVzcyxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICd2YWx1ZScsXG4gICAgICAgIHR5cGU6ICd1aW50JyxcbiAgICAgICAgdmFsdWU6IHR4SW5mby5yZWNpcGllbnQuYW1vdW50LFxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgbmFtZTogJ2RhdGEnLFxuICAgICAgICB0eXBlOiAnYnl0ZXMnLFxuICAgICAgICB2YWx1ZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwudG9CdWZmZXIob3B0aW9uYWxEZXBzLmV0aFV0aWwuYWRkSGV4UHJlZml4KHR4SW5mby5yZWNpcGllbnQuZGF0YSB8fCAnJykpLFxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgbmFtZTogJ2V4cGlyZVRpbWUnLFxuICAgICAgICB0eXBlOiAndWludCcsXG4gICAgICAgIHZhbHVlOiB0eEluZm8uZXhwaXJlVGltZSxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdzZXF1ZW5jZUlkJyxcbiAgICAgICAgdHlwZTogJ3VpbnQnLFxuICAgICAgICB2YWx1ZTogdHhJbmZvLmNvbnRyYWN0U2VxdWVuY2VJZCxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdzaWduYXR1cmUnLFxuICAgICAgICB0eXBlOiAnYnl0ZXMnLFxuICAgICAgICB2YWx1ZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwudG9CdWZmZXIob3B0aW9uYWxEZXBzLmV0aFV0aWwuYWRkSGV4UHJlZml4KHR4SW5mby5zaWduYXR1cmUpKSxcbiAgICAgIH0sXG4gICAgXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvdmVycyBhIHR4IHdpdGggVFNTIGtleSBzaGFyZXNcbiAgICogc2FtZSBleHBlY3RlZCBhcmd1bWVudHMgYXMgcmVjb3ZlciBtZXRob2QsIGJ1dCB3aXRoIFRTUyBrZXkgc2hhcmVzXG4gICAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgcmVjb3ZlclRTUyhwYXJhbXM6IFJlY292ZXJPcHRpb25zKTogUHJvbWlzZTxSZWNvdmVyeUluZm8gfCBPZmZsaW5lVmF1bHRUeEluZm8+IHtcbiAgICB0aGlzLnZhbGlkYXRlUmVjb3ZlcnlQYXJhbXMocGFyYW1zKTtcbiAgICAvLyBDbGVhbiB1cCB3aGl0ZXNwYWNlIGZyb20gZW50ZXJlZCB2YWx1ZXNcbiAgICBjb25zdCB1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUgPSBwYXJhbXMudXNlcktleS5yZXBsYWNlKC9cXHMvZywgJycpO1xuICAgIGNvbnN0IGJhY2t1cFByaXZhdGVPclB1YmxpY0tleVNoYXJlID0gcGFyYW1zLmJhY2t1cEtleS5yZXBsYWNlKC9cXHMvZywgJycpO1xuXG4gICAgY29uc3QgZ2FzTGltaXQgPSBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4odGhpcy5zZXRHYXNMaW1pdChwYXJhbXMuZ2FzTGltaXQpKTtcbiAgICBjb25zdCBnYXNQcmljZSA9IHBhcmFtcy5laXAxNTU5XG4gICAgICA/IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihwYXJhbXMuZWlwMTU1OS5tYXhGZWVQZXJHYXMpXG4gICAgICA6IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTih0aGlzLnNldEdhc1ByaWNlKHBhcmFtcy5nYXNQcmljZSkpO1xuXG4gICAgaWYgKGdldElzVW5zaWduZWRTd2VlcChwYXJhbXMpKSB7XG4gICAgICBjb25zdCBiYWNrdXBLZXlQYWlyID0gbmV3IEtleVBhaXJMaWIoeyBwdWI6IGJhY2t1cFByaXZhdGVPclB1YmxpY0tleVNoYXJlIH0pO1xuICAgICAgY29uc3QgYmFzZUFkZHJlc3MgPSBiYWNrdXBLZXlQYWlyLmdldEFkZHJlc3MoKTtcbiAgICAgIGNvbnN0IHsgdHhJbmZvLCB0eCwgbm9uY2UgfSA9IGF3YWl0IHRoaXMuYnVpbGRUc3NSZWNvdmVyeVR4bihiYXNlQWRkcmVzcywgZ2FzUHJpY2UsIGdhc0xpbWl0LCBwYXJhbXMpO1xuICAgICAgcmV0dXJuIHRoaXMuZm9ybWF0Rm9yT2ZmbGluZVZhdWx0VFNTKFxuICAgICAgICB0eEluZm8sXG4gICAgICAgIHR4LFxuICAgICAgICB1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUsXG4gICAgICAgIGJhY2t1cFByaXZhdGVPclB1YmxpY0tleVNoYXJlLFxuICAgICAgICBnYXNQcmljZSxcbiAgICAgICAgZ2FzTGltaXQsXG4gICAgICAgIG5vbmNlLFxuICAgICAgICBwYXJhbXMuZWlwMTU1OSxcbiAgICAgICAgcGFyYW1zLnJlcGxheVByb3RlY3Rpb25PcHRpb25zXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBpc0dHMThTaWduaW5nTWF0ZXJpYWwgPSB0aGlzLmlzR0cxOFNpZ25pbmdNYXRlcmlhbCh1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUsIHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKTtcbiAgICAgIGxldCBzaWduYXR1cmU6IEVDRFNBTWV0aG9kVHlwZXMuU2lnbmF0dXJlO1xuICAgICAgbGV0IHVuc2lnbmVkVHg6IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb247XG4gICAgICBpZiAoaXNHRzE4U2lnbmluZ01hdGVyaWFsKSB7XG4gICAgICAgIGNvbnN0IFt1c2VyS2V5Q29tYmluZWQsIGJhY2t1cEtleUNvbWJpbmVkXSA9IHRoaXMuZ2V0S2V5Q29tYmluZWRGcm9tVHNzS2V5U2hhcmVzKFxuICAgICAgICAgIHVzZXJQdWJsaWNPclByaXZhdGVLZXlTaGFyZSxcbiAgICAgICAgICBiYWNrdXBQcml2YXRlT3JQdWJsaWNLZXlTaGFyZSxcbiAgICAgICAgICBwYXJhbXMud2FsbGV0UGFzc3BocmFzZVxuICAgICAgICApO1xuICAgICAgICBjb25zdCBiYWNrdXBLZXlQYWlyID0gbmV3IEtleVBhaXJMaWIoeyBwdWI6IGJhY2t1cEtleUNvbWJpbmVkLnhTaGFyZS55IH0pO1xuICAgICAgICBjb25zdCBiYXNlQWRkcmVzcyA9IGJhY2t1cEtleVBhaXIuZ2V0QWRkcmVzcygpO1xuXG4gICAgICAgIHVuc2lnbmVkVHggPSAoYXdhaXQgdGhpcy5idWlsZFRzc1JlY292ZXJ5VHhuKGJhc2VBZGRyZXNzLCBnYXNQcmljZSwgZ2FzTGltaXQsIHBhcmFtcykpLnR4O1xuXG4gICAgICAgIHNpZ25hdHVyZSA9IGF3YWl0IHRoaXMuc2lnblJlY292ZXJ5VFNTKFxuICAgICAgICAgIHVzZXJLZXlDb21iaW5lZCxcbiAgICAgICAgICBiYWNrdXBLZXlDb21iaW5lZCxcbiAgICAgICAgICB1bnNpZ25lZFR4LmdldE1lc3NhZ2VUb1NpZ24oZmFsc2UpLnRvU3RyaW5nKCdoZXgnKVxuICAgICAgICApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgeyB1c2VyS2V5U2hhcmUsIGJhY2t1cEtleVNoYXJlLCBjb21tb25LZXlDaGFpbiwgYmFzZUFkZHJlc3MgfSA9IGF3YWl0IHRoaXMuZ2V0TXBjVjJSZWNvdmVyeUtleVNoYXJlcyhcbiAgICAgICAgICB1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUsXG4gICAgICAgICAgYmFja3VwUHJpdmF0ZU9yUHVibGljS2V5U2hhcmUsXG4gICAgICAgICAgcGFyYW1zLndhbGxldFBhc3NwaHJhc2VcbiAgICAgICAgKTtcblxuICAgICAgICB1bnNpZ25lZFR4ID0gKGF3YWl0IHRoaXMuYnVpbGRUc3NSZWNvdmVyeVR4bihiYXNlQWRkcmVzcywgZ2FzUHJpY2UsIGdhc0xpbWl0LCBwYXJhbXMpKS50eDtcblxuICAgICAgICBzaWduYXR1cmUgPSBhd2FpdCBBYnN0cmFjdEV0aExpa2VOZXdDb2lucy5zaWduUmVjb3ZlcnlNcGNWMihcbiAgICAgICAgICB1bnNpZ25lZFR4LFxuICAgICAgICAgIHVzZXJLZXlTaGFyZSxcbiAgICAgICAgICBiYWNrdXBLZXlTaGFyZSxcbiAgICAgICAgICBjb21tb25LZXlDaGFpblxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBldGhDb21tbW9uID0gQWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuZ2V0RXRoTGlrZUNvbW1vbihwYXJhbXMuZWlwMTU1OSwgcGFyYW1zLnJlcGxheVByb3RlY3Rpb25PcHRpb25zKTtcbiAgICAgIGNvbnN0IHNpZ25lZFR4ID0gdGhpcy5nZXRTaWduZWRUeEZyb21TaWduYXR1cmUoZXRoQ29tbW1vbiwgdW5zaWduZWRUeCwgc2lnbmF0dXJlKTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaWQ6IGFkZEhleFByZWZpeChzaWduZWRUeC5oYXNoKCkudG9TdHJpbmcoJ2hleCcpKSxcbiAgICAgICAgdHg6IGFkZEhleFByZWZpeChzaWduZWRUeC5zZXJpYWxpemUoKS50b1N0cmluZygnaGV4JykpLFxuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBhc3luYyBzaWduUmVjb3ZlcnlNcGNWMihcbiAgICB0eDogRXRoTGlrZVR4TGliLkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbixcbiAgICB1c2VyS2V5U2hhcmU6IEJ1ZmZlcixcbiAgICBiYWNrdXBLZXlTaGFyZTogQnVmZmVyLFxuICAgIGNvbW1vbktleUNoYWluOiBzdHJpbmdcbiAgKSB7XG4gICAgY29uc3QgbWVzc2FnZUhhc2ggPSB0eC5nZXRNZXNzYWdlVG9TaWduKHRydWUpO1xuICAgIGNvbnN0IHVzZXJEc2cgPSBuZXcgRGtsc0RzZy5Ec2codXNlcktleVNoYXJlLCAwLCAnbScsIG1lc3NhZ2VIYXNoKTtcbiAgICBjb25zdCBiYWNrdXBEc2cgPSBuZXcgRGtsc0RzZy5Ec2coYmFja3VwS2V5U2hhcmUsIDEsICdtJywgbWVzc2FnZUhhc2gpO1xuXG4gICAgY29uc3Qgc2lnbmF0dXJlU3RyaW5nID0gRGtsc1V0aWxzLnZlcmlmeUFuZENvbnZlcnREa2xzU2lnbmF0dXJlKFxuICAgICAgbWVzc2FnZUhhc2gsXG4gICAgICAoYXdhaXQgRGtsc1V0aWxzLmV4ZWN1dGVUaWxsUm91bmQoNSwgdXNlckRzZywgYmFja3VwRHNnKSkgYXMgRGtsc1R5cGVzLkRlc2VyaWFsaXplZERrbHNTaWduYXR1cmUsXG4gICAgICBjb21tb25LZXlDaGFpbixcbiAgICAgICdtJyxcbiAgICAgIHVuZGVmaW5lZCxcbiAgICAgIGZhbHNlXG4gICAgKTtcbiAgICBjb25zdCBzaWdQYXJ0cyA9IHNpZ25hdHVyZVN0cmluZy5zcGxpdCgnOicpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHJlY2lkOiBwYXJzZUludChzaWdQYXJ0c1swXSwgMTApLFxuICAgICAgcjogc2lnUGFydHNbMV0sXG4gICAgICBzOiBzaWdQYXJ0c1syXSxcbiAgICAgIHk6IHNpZ1BhcnRzWzNdLFxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGdldE1wY1YyUmVjb3ZlcnlLZXlTaGFyZXMoXG4gICAgdXNlclB1YmxpY09yUHJpdmF0ZUtleVNoYXJlOiBzdHJpbmcsXG4gICAgYmFja3VwUHJpdmF0ZU9yUHVibGljS2V5U2hhcmU6IHN0cmluZyxcbiAgICB3YWxsZXRQYXNzcGhyYXNlPzogc3RyaW5nXG4gICkge1xuICAgIGNvbnN0IHVzZXJDb21wcmVzc2VkUHJ2ID0gQnVmZmVyLmZyb20oXG4gICAgICB0aGlzLmJpdGdvLmRlY3J5cHQoe1xuICAgICAgICBpbnB1dDogdXNlclB1YmxpY09yUHJpdmF0ZUtleVNoYXJlLFxuICAgICAgICBwYXNzd29yZDogd2FsbGV0UGFzc3BocmFzZSxcbiAgICAgIH0pLFxuICAgICAgJ2Jhc2U2NCdcbiAgICApO1xuICAgIGNvbnN0IGJha2N1cENvbXByZXNzZWRQcnYgPSBCdWZmZXIuZnJvbShcbiAgICAgIHRoaXMuYml0Z28uZGVjcnlwdCh7XG4gICAgICAgIGlucHV0OiBiYWNrdXBQcml2YXRlT3JQdWJsaWNLZXlTaGFyZSxcbiAgICAgICAgcGFzc3dvcmQ6IHdhbGxldFBhc3NwaHJhc2UsXG4gICAgICB9KSxcbiAgICAgICdiYXNlNjQnXG4gICAgKTtcblxuICAgIGNvbnN0IHVzZXJQcnZKU09OOiBEa2xzVHlwZXMuUmVkdWNlZEtleVNoYXJlID0gRGtsc1R5cGVzLmdldERlY29kZWRSZWR1Y2VkS2V5U2hhcmUodXNlckNvbXByZXNzZWRQcnYpO1xuICAgIGNvbnN0IGJhY2t1cFBydkpTT046IERrbHNUeXBlcy5SZWR1Y2VkS2V5U2hhcmUgPSBEa2xzVHlwZXMuZ2V0RGVjb2RlZFJlZHVjZWRLZXlTaGFyZShiYWtjdXBDb21wcmVzc2VkUHJ2KTtcbiAgICBjb25zdCB1c2VyS2V5UmV0cm9maXQ6IERrbHNUeXBlcy5SZXRyb2ZpdERhdGEgPSB7XG4gICAgICB4U2hhcmU6IHtcbiAgICAgICAgeDogQnVmZmVyLmZyb20odXNlclBydkpTT04ucHJ2KS50b1N0cmluZygnaGV4JyksXG4gICAgICAgIHk6IEJ1ZmZlci5mcm9tKHVzZXJQcnZKU09OLnB1YikudG9TdHJpbmcoJ2hleCcpLFxuICAgICAgICBjaGFpbmNvZGU6IEJ1ZmZlci5mcm9tKHVzZXJQcnZKU09OLnJvb3RDaGFpbkNvZGUpLnRvU3RyaW5nKCdoZXgnKSxcbiAgICAgIH0sXG4gICAgICBiaWdTaUxpc3Q6IFtCdWZmZXIuZnJvbSh1c2VyUHJ2SlNPTi5iaWdTTGlzdFsxXSkudG9TdHJpbmcoJ2hleCcpXSxcbiAgICAgIHhpTGlzdDogdXNlclBydkpTT04ueExpc3Quc2xpY2UoMCwgMiksXG4gICAgfTtcbiAgICBjb25zdCBiYWNrdXBLZXlSZXRyb2ZpdDogRGtsc1R5cGVzLlJldHJvZml0RGF0YSA9IHtcbiAgICAgIHhTaGFyZToge1xuICAgICAgICB4OiBCdWZmZXIuZnJvbShiYWNrdXBQcnZKU09OLnBydikudG9TdHJpbmcoJ2hleCcpLFxuICAgICAgICB5OiBCdWZmZXIuZnJvbShiYWNrdXBQcnZKU09OLnB1YikudG9TdHJpbmcoJ2hleCcpLFxuICAgICAgICBjaGFpbmNvZGU6IEJ1ZmZlci5mcm9tKGJhY2t1cFBydkpTT04ucm9vdENoYWluQ29kZSkudG9TdHJpbmcoJ2hleCcpLFxuICAgICAgfSxcbiAgICAgIGJpZ1NpTGlzdDogW0J1ZmZlci5mcm9tKGJhY2t1cFBydkpTT04uYmlnU0xpc3RbMF0pLnRvU3RyaW5nKCdoZXgnKV0sXG4gICAgICB4aUxpc3Q6IGJhY2t1cFBydkpTT04ueExpc3Quc2xpY2UoMCwgMiksXG4gICAgfTtcbiAgICBjb25zdCBbdXNlciwgYmFja3VwXSA9IGF3YWl0IERrbHNVdGlscy5nZW5lcmF0ZTJvZjJLZXlTaGFyZXModXNlcktleVJldHJvZml0LCBiYWNrdXBLZXlSZXRyb2ZpdCk7XG4gICAgY29uc3QgdXNlcktleVNoYXJlID0gdXNlci5nZXRLZXlTaGFyZSgpO1xuICAgIGNvbnN0IGJhY2t1cEtleVNoYXJlID0gYmFja3VwLmdldEtleVNoYXJlKCk7XG4gICAgY29uc3QgY29tbW9uS2V5Q2hhaW4gPSBEa2xzVHlwZXMuZ2V0Q29tbW9uS2V5Y2hhaW4odXNlcktleVNoYXJlKS5zbGljZSgwLCA2Nik7XG4gICAgY29uc3QgYmFja3VwS2V5UGFpciA9IG5ldyBLZXlQYWlyTGliKHsgcHViOiBjb21tb25LZXlDaGFpbiB9KTtcbiAgICBjb25zdCBiYXNlQWRkcmVzcyA9IGJhY2t1cEtleVBhaXIuZ2V0QWRkcmVzcygpO1xuICAgIHJldHVybiB7IHVzZXJLZXlTaGFyZSwgYmFja3VwS2V5U2hhcmUsIGNvbW1vbktleUNoYWluLCBiYXNlQWRkcmVzcyB9O1xuICB9XG5cbiAgcHJpdmF0ZSBpc0dHMThTaWduaW5nTWF0ZXJpYWwoa2V5U2hhcmU6IHN0cmluZywgd2FsbGV0UGFzc3BocmFzZTogc3RyaW5nIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gICAgY29uc3QgcHJ2ID0gdGhpcy5iaXRnby5kZWNyeXB0KHtcbiAgICAgIGlucHV0OiBrZXlTaGFyZSxcbiAgICAgIHBhc3N3b3JkOiB3YWxsZXRQYXNzcGhyYXNlLFxuICAgIH0pO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzaWduaW5nTWF0ZXJpYWwgPSBKU09OLnBhcnNlKHBydik7XG4gICAgICByZXR1cm4gKFxuICAgICAgICBzaWduaW5nTWF0ZXJpYWwucFNoYXJlICYmXG4gICAgICAgIHNpZ25pbmdNYXRlcmlhbC5iaXRnb05TaGFyZSAmJlxuICAgICAgICAoc2lnbmluZ01hdGVyaWFsLnVzZXJOU2hhcmUgfHwgc2lnbmluZ01hdGVyaWFsLmJhY2t1cE5TaGFyZSlcbiAgICAgICk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGJ1aWxkVHNzUmVjb3ZlcnlUeG4oYmFzZUFkZHJlc3M6IHN0cmluZywgZ2FzUHJpY2U6IGFueSwgZ2FzTGltaXQ6IGFueSwgcGFyYW1zOiBSZWNvdmVyT3B0aW9ucykge1xuICAgIGNvbnN0IG5vbmNlID0gYXdhaXQgdGhpcy5nZXRBZGRyZXNzTm9uY2UoYmFzZUFkZHJlc3MpO1xuICAgIGNvbnN0IHR4QW1vdW50ID0gYXdhaXQgdGhpcy52YWxpZGF0ZUJhbGFuY2VBbmRHZXRUeEFtb3VudChiYXNlQWRkcmVzcywgZ2FzUHJpY2UsIGdhc0xpbWl0KTtcbiAgICBjb25zdCByZWNpcGllbnRzID0gW1xuICAgICAge1xuICAgICAgICBhZGRyZXNzOiBwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbixcbiAgICAgICAgYW1vdW50OiB0eEFtb3VudC50b1N0cmluZygxMCksXG4gICAgICB9LFxuICAgIF07XG5cbiAgICBjb25zdCB0eEluZm8gPSB7XG4gICAgICByZWNpcGllbnQ6IHJlY2lwaWVudHNbMF0sXG4gICAgICBleHBpcmVUaW1lOiB0aGlzLmdldERlZmF1bHRFeHBpcmVUaW1lKCksXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoMTApLFxuICAgIH07XG5cbiAgICBjb25zdCB0eFBhcmFtcyA9IHtcbiAgICAgIHRvOiBwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbixcbiAgICAgIG5vbmNlOiBub25jZSxcbiAgICAgIHZhbHVlOiB0eEFtb3VudCxcbiAgICAgIGdhc1ByaWNlOiBnYXNQcmljZSxcbiAgICAgIGdhc0xpbWl0OiBnYXNMaW1pdCxcbiAgICAgIGRhdGE6IEJ1ZmZlci5mcm9tKCcweCcpLFxuICAgICAgZWlwMTU1OTogcGFyYW1zLmVpcDE1NTksXG4gICAgICByZXBsYXlQcm90ZWN0aW9uT3B0aW9uczogcGFyYW1zLnJlcGxheVByb3RlY3Rpb25PcHRpb25zLFxuICAgIH07XG5cbiAgICBjb25zdCB0eCA9IEFic3RyYWN0RXRoTGlrZU5ld0NvaW5zLmJ1aWxkVHJhbnNhY3Rpb24odHhQYXJhbXMpO1xuICAgIHJldHVybiB7IHR4SW5mbywgdHgsIG5vbmNlIH07XG4gIH1cblxuICBhc3luYyB2YWxpZGF0ZUJhbGFuY2VBbmRHZXRUeEFtb3VudChiYXNlQWRkcmVzczogc3RyaW5nLCBnYXNQcmljZTogQk4sIGdhc0xpbWl0OiBCTikge1xuICAgIGNvbnN0IGJhc2VBZGRyZXNzQmFsYW5jZSA9IGF3YWl0IHRoaXMucXVlcnlBZGRyZXNzQmFsYW5jZShiYXNlQWRkcmVzcyk7XG5cbiAgICBjb25zdCB0b3RhbEdhc05lZWRlZCA9IGdhc1ByaWNlLm11bChnYXNMaW1pdCk7XG4gICAgY29uc3Qgd2VpVG9Hd2VpID0gbmV3IEJOKDEwICoqIDkpO1xuICAgIGlmIChiYXNlQWRkcmVzc0JhbGFuY2UubHQodG90YWxHYXNOZWVkZWQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBCYWNrdXAga2V5IGFkZHJlc3MgJHtiYXNlQWRkcmVzc30gaGFzIGJhbGFuY2UgJHtiYXNlQWRkcmVzc0JhbGFuY2UuZGl2KHdlaVRvR3dlaSkudG9TdHJpbmcoKX0gR3dlaS5gICtcbiAgICAgICAgICBgVGhpcyBhZGRyZXNzIG11c3QgaGF2ZSBhIGJhbGFuY2Ugb2YgYXQgbGVhc3QgJHt0b3RhbEdhc05lZWRlZC5kaXYod2VpVG9Hd2VpKS50b1N0cmluZygpfWAgK1xuICAgICAgICAgIGAgR3dlaSB0byBwZXJmb3JtIHJlY292ZXJpZXMuIFRyeSBzZW5kaW5nIHNvbWUgRVRIIHRvIHRoaXMgYWRkcmVzcyB0aGVuIHJldHJ5LmBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgY29uc3QgdHhBbW91bnQgPSBiYXNlQWRkcmVzc0JhbGFuY2Uuc3ViKHRvdGFsR2FzTmVlZGVkKTtcbiAgICByZXR1cm4gdHhBbW91bnQ7XG4gIH1cblxuICBhc3luYyByZWNvdmVyeUJsb2NrY2hhaW5FeHBsb3JlclF1ZXJ5KHF1ZXJ5OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTogUHJvbWlzZTxhbnk+IHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ21ldGhvZCBub3QgaW1wbGVtZW50ZWQnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIHRoZSBleHRyYSBwYXJhbWV0ZXJzIG5lZWRlZCB0byBidWlsZCBhIGhvcCB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0gYnVpbGRQYXJhbXMgVGhlIG9yaWdpbmFsIGJ1aWxkIHBhcmFtZXRlcnNcbiAgICogQHJldHVybnMgZXh0cmEgcGFyYW1ldGVycyBvYmplY3QgdG8gbWVyZ2Ugd2l0aCB0aGUgb3JpZ2luYWwgYnVpbGQgcGFyYW1ldGVycyBvYmplY3QgYW5kIHNlbmQgdG8gdGhlIHBsYXRmb3JtXG4gICAqL1xuICBhc3luYyBjcmVhdGVIb3BUcmFuc2FjdGlvblBhcmFtcyhidWlsZFBhcmFtczogSG9wVHJhbnNhY3Rpb25CdWlsZE9wdGlvbnMpOiBQcm9taXNlPEhvcFBhcmFtcz4ge1xuICAgIGNvbnN0IHdhbGxldCA9IGJ1aWxkUGFyYW1zLndhbGxldDtcbiAgICBjb25zdCByZWNpcGllbnRzID0gYnVpbGRQYXJhbXMucmVjaXBpZW50cztcbiAgICBjb25zdCB3YWxsZXRQYXNzcGhyYXNlID0gYnVpbGRQYXJhbXMud2FsbGV0UGFzc3BocmFzZTtcblxuICAgIGNvbnN0IHVzZXJLZXljaGFpbiA9IGF3YWl0IHRoaXMua2V5Y2hhaW5zKCkuZ2V0KHsgaWQ6IHdhbGxldC5rZXlJZHMoKVswXSB9KTtcbiAgICBjb25zdCB1c2VyUHJ2ID0gd2FsbGV0LmdldFVzZXJQcnYoeyBrZXljaGFpbjogdXNlcktleWNoYWluLCB3YWxsZXRQYXNzcGhyYXNlIH0pO1xuICAgIGNvbnN0IHVzZXJQcnZCdWZmZXIgPSBiaXAzMi5mcm9tQmFzZTU4KHVzZXJQcnYpLnByaXZhdGVLZXk7XG4gICAgaWYgKCF1c2VyUHJ2QnVmZmVyKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgdXNlclBydicpO1xuICAgIH1cbiAgICBpZiAoIXJlY2lwaWVudHMgfHwgIUFycmF5LmlzQXJyYXkocmVjaXBpZW50cykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignZXhwZWN0aW5nIGFycmF5IG9mIHJlY2lwaWVudHMnKTtcbiAgICB9XG5cbiAgICAvLyBSaWdodCBub3cgd2Ugb25seSBzdXBwb3J0IDEgcmVjaXBpZW50XG4gICAgaWYgKHJlY2lwaWVudHMubGVuZ3RoICE9PSAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ211c3Qgc2VuZCB0byBleGFjdGx5IDEgcmVjaXBpZW50Jyk7XG4gICAgfVxuICAgIGNvbnN0IHJlY2lwaWVudEFkZHJlc3MgPSByZWNpcGllbnRzWzBdLmFkZHJlc3M7XG4gICAgY29uc3QgcmVjaXBpZW50QW1vdW50ID0gcmVjaXBpZW50c1swXS5hbW91bnQgYXMgc3RyaW5nO1xuICAgIGNvbnN0IGZlZUVzdGltYXRlUGFyYW1zID0ge1xuICAgICAgcmVjaXBpZW50OiByZWNpcGllbnRBZGRyZXNzLFxuICAgICAgYW1vdW50OiByZWNpcGllbnRBbW91bnQsXG4gICAgICBob3A6IHRydWUsXG4gICAgfTtcbiAgICBjb25zdCBmZWVFc3RpbWF0ZTogRmVlRXN0aW1hdGUgPSBhd2FpdCB0aGlzLmZlZUVzdGltYXRlKGZlZUVzdGltYXRlUGFyYW1zKTtcblxuICAgIGNvbnN0IGdhc0xpbWl0ID0gZmVlRXN0aW1hdGUuZ2FzTGltaXRFc3RpbWF0ZTtcbiAgICBjb25zdCBnYXNQcmljZSA9IE1hdGgucm91bmQoZmVlRXN0aW1hdGUuZmVlRXN0aW1hdGUgLyBnYXNMaW1pdCk7XG4gICAgY29uc3QgZ2FzUHJpY2VNYXggPSBnYXNQcmljZSAqIDU7XG4gICAgLy8gUGF5bWVudCBpZCBhIHJhbmRvbSBudW1iZXIgc28gaXRzIGRpZmZlcmVudCBmb3IgZXZlcnkgdHhcbiAgICBjb25zdCBwYXltZW50SWQgPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAxMDAwMDAwMDAwMCkudG9TdHJpbmcoKTtcbiAgICBjb25zdCBob3BEaWdlc3Q6IEJ1ZmZlciA9IEFic3RyYWN0RXRoTGlrZU5ld0NvaW5zLmdldEhvcERpZ2VzdChbXG4gICAgICByZWNpcGllbnRBZGRyZXNzLFxuICAgICAgcmVjaXBpZW50QW1vdW50LFxuICAgICAgZ2FzUHJpY2VNYXgudG9TdHJpbmcoKSxcbiAgICAgIGdhc0xpbWl0LnRvU3RyaW5nKCksXG4gICAgICBwYXltZW50SWQsXG4gICAgXSk7XG5cbiAgICBjb25zdCB1c2VyUmVxU2lnID0gb3B0aW9uYWxEZXBzLmV0aFV0aWwuYWRkSGV4UHJlZml4KFxuICAgICAgQnVmZmVyLmZyb20oc2VjcDI1NmsxLmVjZHNhU2lnbihob3BEaWdlc3QsIHVzZXJQcnZCdWZmZXIpLnNpZ25hdHVyZSkudG9TdHJpbmcoJ2hleCcpXG4gICAgKTtcblxuICAgIHJldHVybiB7XG4gICAgICBob3BQYXJhbXM6IHtcbiAgICAgICAgZ2FzUHJpY2VNYXgsXG4gICAgICAgIHVzZXJSZXFTaWcsXG4gICAgICAgIHBheW1lbnRJZCxcbiAgICAgIH0sXG4gICAgICBnYXNMaW1pdCxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlcyB0aGF0IHRoZSBob3AgcHJlYnVpbGQgZnJvbSB0aGUgSFNNIGlzIHZhbGlkIGFuZCBjb3JyZWN0XG4gICAqIEBwYXJhbSB7SVdhbGxldH0gd2FsbGV0IC0gVGhlIHdhbGxldCB0aGF0IHRoZSBwcmVidWlsZCBpcyBmb3JcbiAgICogQHBhcmFtIHtIb3BQcmVidWlsZH0gaG9wUHJlYnVpbGQgLSBUaGUgcHJlYnVpbGQgdG8gdmFsaWRhdGVcbiAgICogQHBhcmFtIHtPYmplY3R9IG9yaWdpbmFsUGFyYW1zIC0gVGhlIG9yaWdpbmFsIHBhcmFtZXRlcnMgcGFzc2VkIHRvIHByZWJ1aWxkVHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtSZWNpcGllbnRbXX0gb3JpZ2luYWxQYXJhbXMucmVjaXBpZW50cyAtIFRoZSBvcmlnaW5hbCByZWNpcGllbnRzIGFycmF5XG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKiBAdGhyb3dzIEVycm9yIGlmIFRoZSBwcmVidWlsZCBpcyBpbnZhbGlkXG4gICAqL1xuICBhc3luYyB2YWxpZGF0ZUhvcFByZWJ1aWxkKFxuICAgIHdhbGxldDogSVdhbGxldCxcbiAgICBob3BQcmVidWlsZDogSG9wUHJlYnVpbGQsXG4gICAgb3JpZ2luYWxQYXJhbXM/OiB7IHJlY2lwaWVudHM6IFJlY2lwaWVudFtdIH1cbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgeyB0eCwgaWQsIHNpZ25hdHVyZSB9ID0gaG9wUHJlYnVpbGQ7XG5cbiAgICAvLyBmaXJzdCwgdmFsaWRhdGUgdGhlIEhTTSBzaWduYXR1cmVcbiAgICBjb25zdCBzZXJ2ZXJYcHViID0gY29tbW9uLkVudmlyb25tZW50c1t0aGlzLmJpdGdvLmdldEVudigpXS5oc21YcHViO1xuICAgIGNvbnN0IHNlcnZlclB1YmtleUJ1ZmZlcjogQnVmZmVyID0gYmlwMzIuZnJvbUJhc2U1OChzZXJ2ZXJYcHViKS5wdWJsaWNLZXk7XG4gICAgY29uc3Qgc2lnbmF0dXJlQnVmZmVyOiBCdWZmZXIgPSBCdWZmZXIuZnJvbShvcHRpb25hbERlcHMuZXRoVXRpbC5zdHJpcEhleFByZWZpeChzaWduYXR1cmUpLCAnaGV4Jyk7XG4gICAgY29uc3QgbWVzc2FnZUJ1ZmZlcjogQnVmZmVyID0gQnVmZmVyLmZyb20oXG4gICAgICBvcHRpb25hbERlcHMuZXRoVXRpbC5wYWRUb0V2ZW4ob3B0aW9uYWxEZXBzLmV0aFV0aWwuc3RyaXBIZXhQcmVmaXgoaWQpKSxcbiAgICAgICdoZXgnXG4gICAgKTtcblxuICAgIGNvbnN0IHNpZyA9IG5ldyBVaW50OEFycmF5KHNpZ25hdHVyZUJ1ZmZlci5zbGljZSgxKSk7XG4gICAgY29uc3QgaXNWYWxpZFNpZ25hdHVyZTogYm9vbGVhbiA9IHNlY3AyNTZrMS5lY2RzYVZlcmlmeShzaWcsIG1lc3NhZ2VCdWZmZXIsIHNlcnZlclB1YmtleUJ1ZmZlcik7XG4gICAgaWYgKCFpc1ZhbGlkU2lnbmF0dXJlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBIb3AgdHhpZCBzaWduYXR1cmUgaW52YWxpZCAtIHB1YjogJHtzZXJ2ZXJYcHVifSwgbXNnOiAke21lc3NhZ2VCdWZmZXI/LnRvU3RyaW5nKCl9LCBzaWc6ICR7c2lnbmF0dXJlQnVmZmVyPy50b1N0cmluZygpfWBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgY29uc3QgYnVpbHRIb3BUeCA9IG9wdGlvbmFsRGVwcy5FdGhUeC5UcmFuc2FjdGlvbkZhY3RvcnkuZnJvbVNlcmlhbGl6ZWREYXRhKG9wdGlvbmFsRGVwcy5ldGhVdGlsLnRvQnVmZmVyKHR4KSk7XG4gICAgLy8gSWYgb3JpZ2luYWwgcGFyYW1zIGFyZSBnaXZlbiwgd2UgY2FuIGNoZWNrIHRoZW0gYWdhaW5zdCB0aGUgdHJhbnNhY3Rpb24gcHJlYnVpbGQgcGFyYW1zXG4gICAgaWYgKCFfLmlzTmlsKG9yaWdpbmFsUGFyYW1zKSkge1xuICAgICAgY29uc3QgeyByZWNpcGllbnRzIH0gPSBvcmlnaW5hbFBhcmFtcztcblxuICAgICAgLy8gVGhlbiB2YWxpZGF0ZSB0aGF0IHRoZSB0eCBwYXJhbXMgYWN0dWFsbHkgZXF1YWwgdGhlIHJlcXVlc3RlZCBwYXJhbXNcbiAgICAgIGNvbnN0IG9yaWdpbmFsQW1vdW50ID0gbmV3IEJpZ051bWJlcihyZWNpcGllbnRzWzBdLmFtb3VudCk7XG4gICAgICBjb25zdCBvcmlnaW5hbERlc3RpbmF0aW9uOiBzdHJpbmcgPSByZWNpcGllbnRzWzBdLmFkZHJlc3M7XG5cbiAgICAgIGNvbnN0IGhvcEFtb3VudCA9IG5ldyBCaWdOdW1iZXIob3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9IZXgoYnVpbHRIb3BUeC52YWx1ZSkpO1xuICAgICAgaWYgKCFidWlsdEhvcFR4LnRvKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgVHJhbnNhY3Rpb24gZG9lcyBub3QgaGF2ZSBhIGRlc3RpbmF0aW9uIGFkZHJlc3NgKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGhvcERlc3RpbmF0aW9uID0gYnVpbHRIb3BUeC50by50b1N0cmluZygpO1xuICAgICAgaWYgKCFob3BBbW91bnQuZXEob3JpZ2luYWxBbW91bnQpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgSG9wIGFtb3VudDogJHtob3BBbW91bnR9IGRvZXMgbm90IGVxdWFsIG9yaWdpbmFsIGFtb3VudDogJHtvcmlnaW5hbEFtb3VudH1gKTtcbiAgICAgIH1cbiAgICAgIGlmIChob3BEZXN0aW5hdGlvbi50b0xvd2VyQ2FzZSgpICE9PSBvcmlnaW5hbERlc3RpbmF0aW9uLnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBIb3AgZGVzdGluYXRpb246ICR7aG9wRGVzdGluYXRpb259IGRvZXMgbm90IGVxdWFsIG9yaWdpbmFsIHJlY2lwaWVudDogJHtob3BEZXN0aW5hdGlvbn1gKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIWJ1aWx0SG9wVHgudmVyaWZ5U2lnbmF0dXJlKCkpIHtcbiAgICAgIC8vIFdlIGRvbnQgd2FudCB0byBjb250aW51ZSBhdCBhbGwgaW4gdGhpcyBjYXNlLCBhdCByaXNrIG9mIEVUSCBiZWluZyBzdHVjayBvbiB0aGUgaG9wIGFkZHJlc3NcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBob3AgdHJhbnNhY3Rpb24gc2lnbmF0dXJlLCB0eGlkOiAke2lkfWApO1xuICAgIH1cbiAgICBpZiAob3B0aW9uYWxEZXBzLmV0aFV0aWwuYWRkSGV4UHJlZml4KGJ1aWx0SG9wVHguaGFzaCgpLnRvU3RyaW5nKCdoZXgnKSkgIT09IGlkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFNpZ25lZCBob3AgdHhpZCBkb2VzIG5vdCBlcXVhbCBhY3R1YWwgdHhpZGApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXRzIHRoZSBob3AgZGlnZXN0IGZvciB0aGUgdXNlciB0byBzaWduLiBUaGlzIGlzIHZhbGlkYXRlZCBpbiB0aGUgSFNNIHRvIHByb3ZlIHRoYXQgdGhlIHVzZXIgcmVxdWVzdGVkIHRoaXMgdHhcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gcGFyYW1zQXJyIC0gVGhlIHBhcmFtZXRlcnMgdG8gaGFzaCB0b2dldGhlciBmb3IgdGhlIGRpZ2VzdFxuICAgKiBAcmV0dXJucyB7QnVmZmVyfVxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgZ2V0SG9wRGlnZXN0KHBhcmFtc0Fycjogc3RyaW5nW10pOiBCdWZmZXIge1xuICAgIGNvbnN0IGhhc2ggPSBLZWNjYWsoJ2tlY2NhazI1NicpO1xuICAgIGhhc2gudXBkYXRlKFtBYnN0cmFjdEV0aExpa2VOZXdDb2lucy5ob3BUcmFuc2FjdGlvblNhbHQsIC4uLnBhcmFtc0Fycl0uam9pbignJCcpKTtcbiAgICByZXR1cm4gaGFzaC5kaWdlc3QoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNb2RpZnkgcHJlYnVpbGQgYmVmb3JlIHNlbmRpbmcgaXQgdG8gdGhlIHNlcnZlci4gQWRkIHRoaW5ncyBsaWtlIGhvcCB0cmFuc2FjdGlvbiBwYXJhbXNcbiAgICogQHBhcmFtIHtCdWlsZE9wdGlvbnN9IGJ1aWxkUGFyYW1zIC0gVGhlIHdoaXRlbGlzdGVkIHBhcmFtZXRlcnMgZm9yIHRoaXMgcHJlYnVpbGRcbiAgICogQHBhcmFtIHtib29sZWFufSBidWlsZFBhcmFtcy5ob3AgLSBUcnVlIGlmIHRoaXMgc2hvdWxkIHByZWJ1aWxkIGEgaG9wIHR4LCBlbHNlIGZhbHNlXG4gICAqIEBwYXJhbSB7UmVjaXBpZW50W119IGJ1aWxkUGFyYW1zLnJlY2lwaWVudHMgLSBUaGUgcmVjaXBpZW50cyBhcnJheSBvZiB0aGlzIHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSB7V2FsbGV0fSBidWlsZFBhcmFtcy53YWxsZXQgLSBUaGUgd2FsbGV0IHNlbmRpbmcgdGhpcyB0eFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYnVpbGRQYXJhbXMud2FsbGV0UGFzc3BocmFzZSAtIHRoZSBwYXNzcGhyYXNlIGZvciB0aGlzIHdhbGxldFxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxCdWlsZE9wdGlvbnM+fVxuICAgKi9cbiAgYXN5bmMgZ2V0RXh0cmFQcmVidWlsZFBhcmFtcyhidWlsZFBhcmFtczogQnVpbGRPcHRpb25zKTogUHJvbWlzZTxCdWlsZE9wdGlvbnM+IHtcbiAgICBpZiAoXG4gICAgICAhXy5pc1VuZGVmaW5lZChidWlsZFBhcmFtcy5ob3ApICYmXG4gICAgICBidWlsZFBhcmFtcy5ob3AgJiZcbiAgICAgICFfLmlzVW5kZWZpbmVkKGJ1aWxkUGFyYW1zLndhbGxldCkgJiZcbiAgICAgICFfLmlzVW5kZWZpbmVkKGJ1aWxkUGFyYW1zLnJlY2lwaWVudHMpICYmXG4gICAgICAhXy5pc1VuZGVmaW5lZChidWlsZFBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKVxuICAgICkge1xuICAgICAgaWYgKHRoaXMgaW5zdGFuY2VvZiBFdGhMaWtlVG9rZW4pIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBIb3AgdHJhbnNhY3Rpb25zIGFyZSBub3QgZW5hYmxlZCBmb3IgRVJDLTIwIHRva2Vucywgbm9yIGFyZSB0aGV5IG5lY2Vzc2FyeS4gUGxlYXNlIHJlbW92ZSB0aGUgJ2hvcCcgcGFyYW1ldGVyIGFuZCB0cnkgYWdhaW4uYFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIChhd2FpdCB0aGlzLmNyZWF0ZUhvcFRyYW5zYWN0aW9uUGFyYW1zKHtcbiAgICAgICAgd2FsbGV0OiBidWlsZFBhcmFtcy53YWxsZXQsXG4gICAgICAgIHJlY2lwaWVudHM6IGJ1aWxkUGFyYW1zLnJlY2lwaWVudHMsXG4gICAgICAgIHdhbGxldFBhc3NwaHJhc2U6IGJ1aWxkUGFyYW1zLndhbGxldFBhc3NwaHJhc2UsXG4gICAgICB9KSkgYXMgYW55O1xuICAgIH1cbiAgICByZXR1cm4ge307XG4gIH1cblxuICAvKipcbiAgICogTW9kaWZ5IHByZWJ1aWxkIGFmdGVyIHJlY2VpdmluZyBpdCBmcm9tIHRoZSBzZXJ2ZXIuIEFkZCB0aGluZ3MgbGlrZSBubG9ja3RpbWVcbiAgICogQHBhcmFtIHtUcmFuc2FjdGlvblByZWJ1aWxkfSBwYXJhbXMgLSBUaGUgcHJlYnVpbGQgdG8gbW9kaWZ5XG4gICAqIEByZXR1cm5zIHtUcmFuc2FjdGlvblByZWJ1aWxkfSBUaGUgbW9kaWZpZWQgcHJlYnVpbGRcbiAgICovXG4gIGFzeW5jIHBvc3RQcm9jZXNzUHJlYnVpbGQocGFyYW1zOiBUcmFuc2FjdGlvblByZWJ1aWxkKTogUHJvbWlzZTxUcmFuc2FjdGlvblByZWJ1aWxkPiB7XG4gICAgaWYgKCFfLmlzVW5kZWZpbmVkKHBhcmFtcy5ob3BUcmFuc2FjdGlvbikgJiYgIV8uaXNVbmRlZmluZWQocGFyYW1zLndhbGxldCkgJiYgIV8uaXNVbmRlZmluZWQocGFyYW1zLmJ1aWxkUGFyYW1zKSkge1xuICAgICAgYXdhaXQgdGhpcy52YWxpZGF0ZUhvcFByZWJ1aWxkKHBhcmFtcy53YWxsZXQsIHBhcmFtcy5ob3BUcmFuc2FjdGlvbiwgcGFyYW1zLmJ1aWxkUGFyYW1zKTtcbiAgICB9XG4gICAgcmV0dXJuIHBhcmFtcztcbiAgfVxuXG4gIC8qKlxuICAgKiBDb2luLXNwZWNpZmljIHRoaW5ncyBkb25lIGJlZm9yZSBzaWduaW5nIGEgdHJhbnNhY3Rpb24sIGkuZS4gdmVyaWZpY2F0aW9uXG4gICAqIEBwYXJhbSB7UHJlc2lnblRyYW5zYWN0aW9uT3B0aW9uc30gcGFyYW1zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFByZXNpZ25UcmFuc2FjdGlvbk9wdGlvbnM+fVxuICAgKi9cbiAgYXN5bmMgcHJlc2lnblRyYW5zYWN0aW9uKHBhcmFtczogUHJlc2lnblRyYW5zYWN0aW9uT3B0aW9ucyk6IFByb21pc2U8UHJlc2lnblRyYW5zYWN0aW9uT3B0aW9ucz4ge1xuICAgIGlmICghXy5pc1VuZGVmaW5lZChwYXJhbXMuaG9wVHJhbnNhY3Rpb24pICYmICFfLmlzVW5kZWZpbmVkKHBhcmFtcy53YWxsZXQpICYmICFfLmlzVW5kZWZpbmVkKHBhcmFtcy5idWlsZFBhcmFtcykpIHtcbiAgICAgIGF3YWl0IHRoaXMudmFsaWRhdGVIb3BQcmVidWlsZChwYXJhbXMud2FsbGV0LCBwYXJhbXMuaG9wVHJhbnNhY3Rpb24pO1xuICAgIH1cbiAgICByZXR1cm4gcGFyYW1zO1xuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoIGZlZSBlc3RpbWF0ZSBpbmZvcm1hdGlvbiBmcm9tIHRoZSBzZXJ2ZXJcbiAgICogQHBhcmFtIHtPYmplY3R9IHBhcmFtcyAtIFRoZSBwYXJhbXMgcGFzc2VkIGludG8gdGhlIGZ1bmN0aW9uXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW3BhcmFtcy5ob3BdIC0gVHJ1ZSBpZiB3ZSBzaG91bGQgZXN0aW1hdGUgZmVlIGZvciBhIGhvcCB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0ge3N0cmluZ30gW3BhcmFtcy5yZWNpcGllbnRdIC0gVGhlIHJlY2lwaWVudCBvZiB0aGUgdHJhbnNhY3Rpb24gdG8gZXN0aW1hdGUgYSBzZW5kIHRvXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbcGFyYW1zLmRhdGFdIC0gVGhlIEVUSCB0eCBkYXRhIHRvIGVzdGltYXRlIGEgc2VuZCBmb3JcbiAgICogQHJldHVybnMge09iamVjdH0gVGhlIGZlZSBpbmZvIHJldHVybmVkIGZyb20gdGhlIHNlcnZlclxuICAgKi9cbiAgYXN5bmMgZmVlRXN0aW1hdGUocGFyYW1zOiBGZWVFc3RpbWF0ZU9wdGlvbnMpOiBQcm9taXNlPEZlZUVzdGltYXRlPiB7XG4gICAgY29uc3QgcXVlcnk6IEZlZUVzdGltYXRlT3B0aW9ucyA9IHt9O1xuICAgIGlmIChwYXJhbXMgJiYgcGFyYW1zLmhvcCkge1xuICAgICAgcXVlcnkuaG9wID0gcGFyYW1zLmhvcDtcbiAgICB9XG4gICAgaWYgKHBhcmFtcyAmJiBwYXJhbXMucmVjaXBpZW50KSB7XG4gICAgICBxdWVyeS5yZWNpcGllbnQgPSBwYXJhbXMucmVjaXBpZW50O1xuICAgIH1cbiAgICBpZiAocGFyYW1zICYmIHBhcmFtcy5kYXRhKSB7XG4gICAgICBxdWVyeS5kYXRhID0gcGFyYW1zLmRhdGE7XG4gICAgfVxuICAgIGlmIChwYXJhbXMgJiYgcGFyYW1zLmFtb3VudCkge1xuICAgICAgcXVlcnkuYW1vdW50ID0gcGFyYW1zLmFtb3VudDtcbiAgICB9XG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5iaXRnby5nZXQodGhpcy51cmwoJy90eC9mZWUnKSkucXVlcnkocXVlcnkpLnJlc3VsdCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlIHNlY3AyNTZrMSBrZXkgcGFpclxuICAgKlxuICAgKiBAcGFyYW0ge0J1ZmZlcn0gc2VlZFxuICAgKiBAcmV0dXJucyB7S2V5UGFpcn0gb2JqZWN0IHdpdGggZ2VuZXJhdGVkIHB1YiBhbmQgcHJ2XG4gICAqL1xuICBnZW5lcmF0ZUtleVBhaXIoc2VlZDogQnVmZmVyKTogS2V5UGFpciB7XG4gICAgaWYgKCFzZWVkKSB7XG4gICAgICAvLyBBbiBleHRlbmRlZCBwcml2YXRlIGtleSBoYXMgYm90aCBhIG5vcm1hbCAyNTYgYml0IHByaXZhdGUga2V5IGFuZCBhIDI1NlxuICAgICAgLy8gYml0IGNoYWluIGNvZGUsIGJvdGggb2Ygd2hpY2ggbXVzdCBiZSByYW5kb20uIDUxMiBiaXRzIGlzIHRoZXJlZm9yZSB0aGVcbiAgICAgIC8vIG1heGltdW0gZW50cm9weSBhbmQgZ2l2ZXMgdXMgbWF4aW11bSBzZWN1cml0eSBhZ2FpbnN0IGNyYWNraW5nLlxuICAgICAgc2VlZCA9IHJhbmRvbUJ5dGVzKDUxMiAvIDgpO1xuICAgIH1cbiAgICBjb25zdCBleHRlbmRlZEtleSA9IGJpcDMyLmZyb21TZWVkKHNlZWQpO1xuICAgIGNvbnN0IHhwdWIgPSBleHRlbmRlZEtleS5uZXV0ZXJlZCgpLnRvQmFzZTU4KCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHB1YjogeHB1YixcbiAgICAgIHBydjogZXh0ZW5kZWRLZXkudG9CYXNlNTgoKSxcbiAgICB9O1xuICB9XG5cbiAgYXN5bmMgcGFyc2VUcmFuc2FjdGlvbihwYXJhbXM6IFBhcnNlVHJhbnNhY3Rpb25PcHRpb25zKTogUHJvbWlzZTxQYXJzZWRUcmFuc2FjdGlvbj4ge1xuICAgIHJldHVybiB7fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNYWtlIHN1cmUgYW4gYWRkcmVzcyBpcyBhIHdhbGxldCBhZGRyZXNzIGFuZCB0aHJvdyBhbiBlcnJvciBpZiBpdCdzIG5vdC5cbiAgICogQHBhcmFtIHtPYmplY3R9IHBhcmFtc1xuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmFkZHJlc3MgLSBUaGUgZGVyaXZlZCBhZGRyZXNzIHN0cmluZyBvbiB0aGUgbmV0d29ya1xuICAgKiBAcGFyYW0ge09iamVjdH0gcGFyYW1zLmNvaW5TcGVjaWZpYyAtIENvaW4tc3BlY2lmaWMgZGV0YWlscyBmb3IgdGhlIGFkZHJlc3Mgc3VjaCBhcyBhIGZvcndhcmRlclZlcnNpb25cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iYXNlQWRkcmVzcyAtIFRoZSBiYXNlIGFkZHJlc3Mgb2YgdGhlIHdhbGxldCBvbiB0aGUgbmV0d29ya1xuICAgKiBAdGhyb3dzIHtJbnZhbGlkQWRkcmVzc0Vycm9yfVxuICAgKiBAdGhyb3dzIHtJbnZhbGlkQWRkcmVzc1ZlcmlmaWNhdGlvbk9iamVjdFByb3BlcnR5RXJyb3J9XG4gICAqIEB0aHJvd3Mge1VuZXhwZWN0ZWRBZGRyZXNzRXJyb3J9XG4gICAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmZiBhZGRyZXNzIGlzIGEgd2FsbGV0IGFkZHJlc3NcbiAgICovXG4gIGFzeW5jIGlzV2FsbGV0QWRkcmVzcyhwYXJhbXM6IFZlcmlmeUV0aEFkZHJlc3NPcHRpb25zKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3QgZXRoVXRpbCA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsO1xuXG4gICAgbGV0IGV4cGVjdGVkQWRkcmVzcztcbiAgICBsZXQgYWN0dWFsQWRkcmVzcztcblxuICAgIGNvbnN0IHsgYWRkcmVzcywgY29pblNwZWNpZmljLCBiYXNlQWRkcmVzcywgaW1wbGllZEZvcndhcmRlclZlcnNpb24gPSBjb2luU3BlY2lmaWM/LmZvcndhcmRlclZlcnNpb24gfSA9IHBhcmFtcztcblxuICAgIGlmIChhZGRyZXNzICYmICF0aGlzLmlzVmFsaWRBZGRyZXNzKGFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgSW52YWxpZEFkZHJlc3NFcnJvcihgaW52YWxpZCBhZGRyZXNzOiAke2FkZHJlc3N9YCk7XG4gICAgfVxuXG4gICAgLy8gYmFzZSBhZGRyZXNzIGlzIHJlcXVpcmVkIHRvIGNhbGN1bGF0ZSB0aGUgc2FsdCB3aGljaCBpcyB1c2VkIGluIGNhbGN1bGF0ZUZvcndhcmRlclYxQWRkcmVzcyBtZXRob2RcbiAgICBpZiAoXy5pc1VuZGVmaW5lZChiYXNlQWRkcmVzcykgfHwgIXRoaXMuaXNWYWxpZEFkZHJlc3MoYmFzZUFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgSW52YWxpZEFkZHJlc3NFcnJvcignaW52YWxpZCBiYXNlIGFkZHJlc3MnKTtcbiAgICB9XG5cbiAgICBpZiAoIV8uaXNPYmplY3QoY29pblNwZWNpZmljKSkge1xuICAgICAgdGhyb3cgbmV3IEludmFsaWRBZGRyZXNzVmVyaWZpY2F0aW9uT2JqZWN0UHJvcGVydHlFcnJvcihcbiAgICAgICAgJ2FkZHJlc3MgdmFsaWRhdGlvbiBmYWlsdXJlOiBjb2luU3BlY2lmaWMgZmllbGQgbXVzdCBiZSBhbiBvYmplY3QnXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChpbXBsaWVkRm9yd2FyZGVyVmVyc2lvbiA9PT0gMCB8fCBpbXBsaWVkRm9yd2FyZGVyVmVyc2lvbiA9PT0gMykge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGV0aE5ldHdvcmsgPSB0aGlzLmdldE5ldHdvcmsoKTtcbiAgICAgIGNvbnN0IGZvcndhcmRlckZhY3RvcnlBZGRyZXNzID0gZXRoTmV0d29yaz8uZm9yd2FyZGVyRmFjdG9yeUFkZHJlc3MgYXMgc3RyaW5nO1xuICAgICAgY29uc3QgZm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzID0gZXRoTmV0d29yaz8uZm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzIGFzIHN0cmluZztcblxuICAgICAgY29uc3QgaW5pdGNvZGUgPSBnZXRQcm94eUluaXRjb2RlKGZvcndhcmRlckltcGxlbWVudGF0aW9uQWRkcmVzcyk7XG4gICAgICBjb25zdCBzYWx0QnVmZmVyID0gZXRoVXRpbC5zZXRMZW5ndGhMZWZ0KFxuICAgICAgICBCdWZmZXIuZnJvbShldGhVdGlsLnBhZFRvRXZlbihldGhVdGlsLnN0cmlwSGV4UHJlZml4KGNvaW5TcGVjaWZpYy5zYWx0IHx8ICcnKSksICdoZXgnKSxcbiAgICAgICAgMzJcbiAgICAgICk7XG5cbiAgICAgIC8vIEhhc2ggdGhlIHdhbGxldCBiYXNlIGFkZHJlc3Mgd2l0aCB0aGUgZ2l2ZW4gc2FsdCwgc28gdGhlIGFkZHJlc3MgZGlyZWN0bHkgcmVsaWVzIG9uIHRoZSBiYXNlIGFkZHJlc3NcbiAgICAgIGNvbnN0IGNhbGN1bGF0aW9uU2FsdCA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSGV4KFxuICAgICAgICBvcHRpb25hbERlcHMuZXRoQWJpLnNvbGlkaXR5U0hBMyhbJ2FkZHJlc3MnLCAnYnl0ZXMzMiddLCBbYmFzZUFkZHJlc3MsIHNhbHRCdWZmZXJdKVxuICAgICAgKTtcblxuICAgICAgZXhwZWN0ZWRBZGRyZXNzID0gY2FsY3VsYXRlRm9yd2FyZGVyVjFBZGRyZXNzKGZvcndhcmRlckZhY3RvcnlBZGRyZXNzLCBjYWxjdWxhdGlvblNhbHQsIGluaXRjb2RlKTtcbiAgICAgIGFjdHVhbEFkZHJlc3MgPSBhZGRyZXNzO1xuICAgIH1cblxuICAgIGlmIChleHBlY3RlZEFkZHJlc3MgIT09IGFjdHVhbEFkZHJlc3MpIHtcbiAgICAgIHRocm93IG5ldyBVbmV4cGVjdGVkQWRkcmVzc0Vycm9yKGBhZGRyZXNzIHZhbGlkYXRpb24gZmFpbHVyZTogZXhwZWN0ZWQgJHtleHBlY3RlZEFkZHJlc3N9IGJ1dCBnb3QgJHthZGRyZXNzfWApO1xuICAgIH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqXG4gICAqIEBwYXJhbSB7VHJhbnNhY3Rpb25QcmVidWlsZH0gdHhQcmVidWlsZFxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIHZlcmlmeUNvaW4odHhQcmVidWlsZDogVHJhbnNhY3Rpb25QcmVidWlsZCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0eFByZWJ1aWxkLmNvaW4gPT09IHRoaXMuZ2V0Q2hhaW4oKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZnkgaWYgYSB0c3MgdHJhbnNhY3Rpb24gaXMgdmFsaWRcbiAgICpcbiAgICogQHBhcmFtIHtWZXJpZnlFdGhUcmFuc2FjdGlvbk9wdGlvbnN9IHBhcmFtc1xuICAgKiBAcGFyYW0ge1RyYW5zYWN0aW9uUGFyYW1zfSBwYXJhbXMudHhQYXJhbXMgLSBwYXJhbXMgb2JqZWN0IHBhc3NlZCB0byBzZW5kXG4gICAqIEBwYXJhbSB7VHJhbnNhY3Rpb25QcmVidWlsZH0gcGFyYW1zLnR4UHJlYnVpbGQgLSBwcmVidWlsZCBvYmplY3QgcmV0dXJuZWQgYnkgc2VydmVyXG4gICAqIEBwYXJhbSB7V2FsbGV0fSBwYXJhbXMud2FsbGV0IC0gV2FsbGV0IG9iamVjdCB0byBvYnRhaW4ga2V5cyB0byB2ZXJpZnkgYWdhaW5zdFxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIHZlcmlmeVRzc1RyYW5zYWN0aW9uKHBhcmFtczogVmVyaWZ5RXRoVHJhbnNhY3Rpb25PcHRpb25zKTogYm9vbGVhbiB7XG4gICAgY29uc3QgeyB0eFBhcmFtcywgdHhQcmVidWlsZCwgd2FsbGV0IH0gPSBwYXJhbXM7XG4gICAgaWYgKFxuICAgICAgIXR4UGFyYW1zPy5yZWNpcGllbnRzICYmXG4gICAgICAhKFxuICAgICAgICB0eFBhcmFtcy5wcmVidWlsZFR4Py5jb25zb2xpZGF0ZUlkIHx8XG4gICAgICAgICh0eFBhcmFtcy50eXBlICYmIFsnYWNjZWxlcmF0aW9uJywgJ2ZpbGxOb25jZScsICd0cmFuc2ZlclRva2VuJ10uaW5jbHVkZXModHhQYXJhbXMudHlwZSkpXG4gICAgICApXG4gICAgKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYG1pc3NpbmcgdHhQYXJhbXNgKTtcbiAgICB9XG4gICAgaWYgKCF3YWxsZXQgfHwgIXR4UHJlYnVpbGQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgbWlzc2luZyBwYXJhbXNgKTtcbiAgICB9XG4gICAgaWYgKHR4UGFyYW1zLmhvcCAmJiB0eFBhcmFtcy5yZWNpcGllbnRzICYmIHR4UGFyYW1zLnJlY2lwaWVudHMubGVuZ3RoID4gMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGB0eCBjYW5ub3QgYmUgYm90aCBhIGJhdGNoIGFuZCBob3AgdHJhbnNhY3Rpb25gKTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogVmVyaWZ5IHRoYXQgYSB0cmFuc2FjdGlvbiBwcmVidWlsZCBjb21wbGllcyB3aXRoIHRoZSBvcmlnaW5hbCBpbnRlbnRpb25cbiAgICpcbiAgICogQHBhcmFtIHtWZXJpZnlFdGhUcmFuc2FjdGlvbk9wdGlvbnN9IHBhcmFtc1xuICAgKiBAcGFyYW0ge1RyYW5zYWN0aW9uUGFyYW1zfSBwYXJhbXMudHhQYXJhbXMgLSBwYXJhbXMgb2JqZWN0IHBhc3NlZCB0byBzZW5kXG4gICAqIEBwYXJhbSB7VHJhbnNhY3Rpb25QcmVidWlsZH0gcGFyYW1zLnR4UHJlYnVpbGQgLSBwcmVidWlsZCBvYmplY3QgcmV0dXJuZWQgYnkgc2VydmVyXG4gICAqIEBwYXJhbSB7V2FsbGV0fSBwYXJhbXMud2FsbGV0IC0gV2FsbGV0IG9iamVjdCB0byBvYnRhaW4ga2V5cyB0byB2ZXJpZnkgYWdhaW5zdFxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIGFzeW5jIHZlcmlmeVRyYW5zYWN0aW9uKHBhcmFtczogVmVyaWZ5RXRoVHJhbnNhY3Rpb25PcHRpb25zKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3QgZXRoTmV0d29yayA9IHRoaXMuZ2V0TmV0d29yaygpO1xuICAgIGNvbnN0IHsgdHhQYXJhbXMsIHR4UHJlYnVpbGQsIHdhbGxldCwgd2FsbGV0VHlwZSB9ID0gcGFyYW1zO1xuXG4gICAgaWYgKHdhbGxldFR5cGUgPT09ICd0c3MnKSB7XG4gICAgICByZXR1cm4gdGhpcy52ZXJpZnlUc3NUcmFuc2FjdGlvbihwYXJhbXMpO1xuICAgIH1cblxuICAgIGlmICghdHhQYXJhbXM/LnJlY2lwaWVudHMgfHwgIXR4UHJlYnVpbGQ/LnJlY2lwaWVudHMgfHwgIXdhbGxldCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBtaXNzaW5nIHBhcmFtc2ApO1xuICAgIH1cbiAgICBpZiAodHhQYXJhbXMuaG9wICYmIHR4UGFyYW1zLnJlY2lwaWVudHMubGVuZ3RoID4gMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGB0eCBjYW5ub3QgYmUgYm90aCBhIGJhdGNoIGFuZCBob3AgdHJhbnNhY3Rpb25gKTtcbiAgICB9XG4gICAgaWYgKHR4UHJlYnVpbGQucmVjaXBpZW50cy5sZW5ndGggIT09IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgdHhQcmVidWlsZCBzaG91bGQgb25seSBoYXZlIDEgcmVjaXBpZW50IGJ1dCAke3R4UHJlYnVpbGQucmVjaXBpZW50cy5sZW5ndGh9IGZvdW5kYCk7XG4gICAgfVxuICAgIGlmICh0eFBhcmFtcy5ob3AgJiYgdHhQcmVidWlsZC5ob3BUcmFuc2FjdGlvbikge1xuICAgICAgLy8gQ2hlY2sgcmVjaXBpZW50IGFtb3VudCBmb3IgaG9wIHRyYW5zYWN0aW9uXG4gICAgICBpZiAodHhQYXJhbXMucmVjaXBpZW50cy5sZW5ndGggIT09IDEpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBob3AgdHJhbnNhY3Rpb24gb25seSBzdXBwb3J0cyAxIHJlY2lwaWVudCBidXQgJHt0eFBhcmFtcy5yZWNpcGllbnRzLmxlbmd0aH0gZm91bmRgKTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgdHggc2VuZHMgdG8gaG9wIGFkZHJlc3NcbiAgICAgIGNvbnN0IGRlY29kZWRIb3BUeCA9IG9wdGlvbmFsRGVwcy5FdGhUeC5UcmFuc2FjdGlvbkZhY3RvcnkuZnJvbVNlcmlhbGl6ZWREYXRhKFxuICAgICAgICBvcHRpb25hbERlcHMuZXRoVXRpbC50b0J1ZmZlcih0eFByZWJ1aWxkLmhvcFRyYW5zYWN0aW9uLnR4KVxuICAgICAgKTtcbiAgICAgIGNvbnN0IGV4cGVjdGVkSG9wQWRkcmVzcyA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KGRlY29kZWRIb3BUeC5nZXRTZW5kZXJBZGRyZXNzKCkudG9TdHJpbmcoKSk7XG4gICAgICBjb25zdCBhY3R1YWxIb3BBZGRyZXNzID0gb3B0aW9uYWxEZXBzLmV0aFV0aWwuc3RyaXBIZXhQcmVmaXgodHhQcmVidWlsZC5yZWNpcGllbnRzWzBdLmFkZHJlc3MpO1xuICAgICAgaWYgKGV4cGVjdGVkSG9wQWRkcmVzcy50b0xvd2VyQ2FzZSgpICE9PSBhY3R1YWxIb3BBZGRyZXNzLnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdyZWNpcGllbnQgYWRkcmVzcyBvZiB0eFByZWJ1aWxkIGRvZXMgbm90IG1hdGNoIGhvcCBhZGRyZXNzJyk7XG4gICAgICB9XG5cbiAgICAgIC8vIENvbnZlcnQgVHJhbnNhY3Rpb25SZWNpcGllbnQgYXJyYXkgdG8gUmVjaXBpZW50IGFycmF5XG4gICAgICBjb25zdCByZWNpcGllbnRzOiBSZWNpcGllbnRbXSA9IHR4UGFyYW1zLnJlY2lwaWVudHMubWFwKChyKSA9PiB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgYWRkcmVzczogci5hZGRyZXNzLFxuICAgICAgICAgIGFtb3VudDogdHlwZW9mIHIuYW1vdW50ID09PSAnbnVtYmVyJyA/IHIuYW1vdW50LnRvU3RyaW5nKCkgOiByLmFtb3VudCxcbiAgICAgICAgfTtcbiAgICAgIH0pO1xuXG4gICAgICAvLyBDaGVjayBkZXN0aW5hdGlvbiBhZGRyZXNzIGFuZCBhbW91bnRcbiAgICAgIGF3YWl0IHRoaXMudmFsaWRhdGVIb3BQcmVidWlsZCh3YWxsZXQsIHR4UHJlYnVpbGQuaG9wVHJhbnNhY3Rpb24sIHsgcmVjaXBpZW50cyB9KTtcbiAgICB9IGVsc2UgaWYgKHR4UGFyYW1zLnJlY2lwaWVudHMubGVuZ3RoID4gMSkge1xuICAgICAgLy8gQ2hlY2sgdG90YWwgYW1vdW50IGZvciBiYXRjaCB0cmFuc2FjdGlvblxuICAgICAgbGV0IGV4cGVjdGVkVG90YWxBbW91bnQgPSBuZXcgQmlnTnVtYmVyKDApO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0eFBhcmFtcy5yZWNpcGllbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGV4cGVjdGVkVG90YWxBbW91bnQgPSBleHBlY3RlZFRvdGFsQW1vdW50LnBsdXModHhQYXJhbXMucmVjaXBpZW50c1tpXS5hbW91bnQpO1xuICAgICAgfVxuICAgICAgaWYgKCFleHBlY3RlZFRvdGFsQW1vdW50LmlzRXF1YWxUbyh0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYW1vdW50KSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ2JhdGNoIHRyYW5zYWN0aW9uIGFtb3VudCBpbiB0eFByZWJ1aWxkIHJlY2VpdmVkIGZyb20gQml0R28gc2VydmVycyBkb2VzIG5vdCBtYXRjaCB0eFBhcmFtcyBzdXBwbGllZCBieSBjbGllbnQnXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGJhdGNoIHRyYW5zYWN0aW9uIGlzIHNlbnQgdG8gdGhlIGJhdGNoZXIgY29udHJhY3QgYWRkcmVzcyBmb3IgdGhlIGNoYWluXG4gICAgICBjb25zdCBiYXRjaGVyQ29udHJhY3RBZGRyZXNzID0gZXRoTmV0d29yaz8uYmF0Y2hlckNvbnRyYWN0QWRkcmVzcztcbiAgICAgIGlmIChcbiAgICAgICAgIWJhdGNoZXJDb250cmFjdEFkZHJlc3MgfHxcbiAgICAgICAgYmF0Y2hlckNvbnRyYWN0QWRkcmVzcy50b0xvd2VyQ2FzZSgpICE9PSB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYWRkcmVzcy50b0xvd2VyQ2FzZSgpXG4gICAgICApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdyZWNpcGllbnQgYWRkcmVzcyBvZiB0eFByZWJ1aWxkIGRvZXMgbm90IG1hdGNoIGJhdGNoZXIgYWRkcmVzcycpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBDaGVjayByZWNpcGllbnQgYWRkcmVzcyBhbmQgYW1vdW50IGZvciBub3JtYWwgdHJhbnNhY3Rpb25cbiAgICAgIGlmICh0eFBhcmFtcy5yZWNpcGllbnRzLmxlbmd0aCAhPT0gMSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYG5vcm1hbCB0cmFuc2FjdGlvbiBvbmx5IHN1cHBvcnRzIDEgcmVjaXBpZW50IGJ1dCAke3R4UGFyYW1zLnJlY2lwaWVudHMubGVuZ3RofSBmb3VuZGApO1xuICAgICAgfVxuICAgICAgY29uc3QgZXhwZWN0ZWRBbW91bnQgPSBuZXcgQmlnTnVtYmVyKHR4UGFyYW1zLnJlY2lwaWVudHNbMF0uYW1vdW50KTtcbiAgICAgIGlmICghZXhwZWN0ZWRBbW91bnQuaXNFcXVhbFRvKHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hbW91bnQpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnbm9ybWFsIHRyYW5zYWN0aW9uIGFtb3VudCBpbiB0eFByZWJ1aWxkIHJlY2VpdmVkIGZyb20gQml0R28gc2VydmVycyBkb2VzIG5vdCBtYXRjaCB0eFBhcmFtcyBzdXBwbGllZCBieSBjbGllbnQnXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICBpZiAoXG4gICAgICAgIHRoaXMuaXNFVEhBZGRyZXNzKHR4UGFyYW1zLnJlY2lwaWVudHNbMF0uYWRkcmVzcykgJiZcbiAgICAgICAgdHhQYXJhbXMucmVjaXBpZW50c1swXS5hZGRyZXNzICE9PSB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYWRkcmVzc1xuICAgICAgKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignZGVzdGluYXRpb24gYWRkcmVzcyBpbiBub3JtYWwgdHhQcmVidWlsZCBkb2VzIG5vdCBtYXRjaCB0aGF0IGluIHR4UGFyYW1zIHN1cHBsaWVkIGJ5IGNsaWVudCcpO1xuICAgICAgfVxuICAgIH1cbiAgICAvLyBDaGVjayBjb2luIGlzIGNvcnJlY3QgZm9yIGFsbCB0cmFuc2FjdGlvbiB0eXBlc1xuICAgIGlmICghdGhpcy52ZXJpZnlDb2luKHR4UHJlYnVpbGQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYGNvaW4gaW4gdHhQcmVidWlsZCBkaWQgbm90IG1hdGNoIHRoYXQgaW4gdHhQYXJhbXMgc3VwcGxpZWQgYnkgY2xpZW50YCk7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGFkZHJlc3MgaXMgdmFsaWQgZXRoIGFkZHJlc3NcbiAgICogQHBhcmFtIGFkZHJlc3NcbiAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAqL1xuICBwcml2YXRlIGlzRVRIQWRkcmVzcyhhZGRyZXNzOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gISFhZGRyZXNzLm1hdGNoKC8weFthLWZBLUYwLTldezQwfS8pO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYW5zZm9ybSBtZXNzYWdlIHRvIGFjY29tbW9kYXRlIHNwZWNpZmljIGJsb2NrY2hhaW4gcmVxdWlyZW1lbnRzLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIHRoZSBtZXNzYWdlIHRvIHByZXBhcmVcbiAgICogQHJldHVybiB7c3RyaW5nfSB0aGUgcHJlcGFyZWQgbWVzc2FnZS5cbiAgICovXG4gIGVuY29kZU1lc3NhZ2UobWVzc2FnZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBwcmVmaXggPSBgXFx1MDAxOUV0aGVyZXVtIFNpZ25lZCBNZXNzYWdlOlxcbiR7bWVzc2FnZS5sZW5ndGh9YDtcbiAgICByZXR1cm4gcHJlZml4LmNvbmNhdChtZXNzYWdlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUcmFuc2Zvcm0gdGhlIFR5cGVkIGRhdGEgdG8gYWNjb21vZGF0ZSB0aGUgYmxvY2tjaGFpbiByZXF1aXJlbWVudHMgKEVJUC03MTIpXG4gICAqIEBwYXJhbSB7VHlwZWREYXRhfSB0eXBlZERhdGEgLSB0aGUgdHlwZWQgZGF0YSB0byBwcmVwYXJlXG4gICAqIEByZXR1cm4ge0J1ZmZlcn0gYSBidWZmZXIgb2YgdGhlIHJlc3VsdFxuICAgKi9cbiAgZW5jb2RlVHlwZWREYXRhKHR5cGVkRGF0YTogVHlwZWREYXRhKTogQnVmZmVyIHtcbiAgICBjb25zdCB2ZXJzaW9uID0gdHlwZWREYXRhLnZlcnNpb247XG4gICAgaWYgKHZlcnNpb24gPT09IFNpZ25UeXBlZERhdGFWZXJzaW9uLlYxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NpZ25UeXBlZERhdGEgdjEgaXMgbm90IHN1cHBvcnRlZCBkdWUgdG8gc2VjdXJpdHkgY29uY2VybnMnKTtcbiAgICB9XG4gICAgY29uc3QgdHlwZWREYXRhUmF3ID0gSlNPTi5wYXJzZSh0eXBlZERhdGEudHlwZWREYXRhUmF3KTtcbiAgICBjb25zdCBzYW5pdGl6ZWREYXRhID0gVHlwZWREYXRhVXRpbHMuc2FuaXRpemVEYXRhKHR5cGVkRGF0YVJhdyBhcyB1bmtub3duIGFzIFR5cGVkTWVzc2FnZTxhbnk+KTtcbiAgICBjb25zdCBwYXJ0cyA9IFtCdWZmZXIuZnJvbSgnMTkwMScsICdoZXgnKV07XG4gICAgY29uc3QgZWlwNzEyRG9tYWluID0gJ0VJUDcxMkRvbWFpbic7XG4gICAgcGFydHMucHVzaChUeXBlZERhdGFVdGlscy5oYXNoU3RydWN0KGVpcDcxMkRvbWFpbiwgc2FuaXRpemVkRGF0YS5kb21haW4sIHNhbml0aXplZERhdGEudHlwZXMsIHZlcnNpb24pKTtcblxuICAgIGlmIChzYW5pdGl6ZWREYXRhLnByaW1hcnlUeXBlICE9PSBlaXA3MTJEb21haW4pIHtcbiAgICAgIHBhcnRzLnB1c2goXG4gICAgICAgIFR5cGVkRGF0YVV0aWxzLmhhc2hTdHJ1Y3QoXG4gICAgICAgICAgc2FuaXRpemVkRGF0YS5wcmltYXJ5VHlwZSBhcyBzdHJpbmcsXG4gICAgICAgICAgc2FuaXRpemVkRGF0YS5tZXNzYWdlLFxuICAgICAgICAgIHNhbml0aXplZERhdGEudHlwZXMsXG4gICAgICAgICAgdmVyc2lvblxuICAgICAgICApXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gQnVmZmVyLmNvbmNhdChwYXJ0cyk7XG4gIH1cblxuICAvKipcbiAgICogQnVpbGQgdGhlIGRhdGEgdG8gdHJhbnNmZXIgYW4gRVJDLTcyMSBvciBFUkMtMTE1NSB0b2tlbiB0byBhbm90aGVyIGFkZHJlc3NcbiAgICogQHBhcmFtIHBhcmFtc1xuICAgKi9cbiAgYnVpbGROZnRUcmFuc2ZlckRhdGEocGFyYW1zOiBCdWlsZE5mdFRyYW5zZmVyRGF0YU9wdGlvbnMpOiBzdHJpbmcge1xuICAgIGNvbnN0IHsgdG9rZW5Db250cmFjdEFkZHJlc3MsIHJlY2lwaWVudEFkZHJlc3MsIGZyb21BZGRyZXNzIH0gPSBwYXJhbXM7XG4gICAgc3dpdGNoIChwYXJhbXMudHlwZSkge1xuICAgICAgY2FzZSAnRVJDNzIxJzoge1xuICAgICAgICBjb25zdCB0b2tlbklkID0gcGFyYW1zLnRva2VuSWQ7XG4gICAgICAgIGNvbnN0IGNvbnRyYWN0RGF0YSA9IG5ldyBFUkM3MjFUcmFuc2ZlckJ1aWxkZXIoKVxuICAgICAgICAgIC50b2tlbkNvbnRyYWN0QWRkcmVzcyh0b2tlbkNvbnRyYWN0QWRkcmVzcylcbiAgICAgICAgICAudG8ocmVjaXBpZW50QWRkcmVzcylcbiAgICAgICAgICAuZnJvbShmcm9tQWRkcmVzcylcbiAgICAgICAgICAudG9rZW5JZCh0b2tlbklkKVxuICAgICAgICAgIC5idWlsZCgpO1xuICAgICAgICByZXR1cm4gY29udHJhY3REYXRhO1xuICAgICAgfVxuXG4gICAgICBjYXNlICdFUkMxMTU1Jzoge1xuICAgICAgICBjb25zdCBlbnRyaWVzID0gcGFyYW1zLmVudHJpZXM7XG4gICAgICAgIGNvbnN0IHRyYW5zZmVyQnVpbGRlciA9IG5ldyBFUkMxMTU1VHJhbnNmZXJCdWlsZGVyKClcbiAgICAgICAgICAudG9rZW5Db250cmFjdEFkZHJlc3ModG9rZW5Db250cmFjdEFkZHJlc3MpXG4gICAgICAgICAgLnRvKHJlY2lwaWVudEFkZHJlc3MpXG4gICAgICAgICAgLmZyb20oZnJvbUFkZHJlc3MpO1xuXG4gICAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgICAgIHRyYW5zZmVyQnVpbGRlci5lbnRyeShwYXJzZUludChlbnRyeS50b2tlbklkLCAxMCksIGVudHJ5LmFtb3VudCk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdHJhbnNmZXJCdWlsZGVyLmJ1aWxkKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG59XG4iXX0=
|