@bitgo-beta/abstract-eth 1.2.3-alpha.35 → 1.2.3-alpha.351
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 +1692 -0
- package/dist/src/abstractEthLikeCoin.d.ts +18 -9
- package/dist/src/abstractEthLikeCoin.d.ts.map +1 -1
- package/dist/src/abstractEthLikeCoin.js +39 -15
- package/dist/src/abstractEthLikeNewCoins.d.ts +696 -0
- package/dist/src/abstractEthLikeNewCoins.d.ts.map +1 -0
- package/dist/src/abstractEthLikeNewCoins.js +2095 -0
- package/dist/src/ethLikeToken.d.ts +36 -6
- package/dist/src/ethLikeToken.d.ts.map +1 -1
- package/dist/src/ethLikeToken.js +286 -10
- 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 +133 -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 +16 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +57 -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 +65 -0
- package/dist/src/lib/messages/eip191/eip191Message.d.ts +12 -0
- package/dist/src/lib/messages/eip191/eip191Message.d.ts.map +1 -0
- package/dist/src/lib/messages/eip191/eip191Message.js +25 -0
- package/dist/src/lib/messages/eip191/eip191MessageBuilder.d.ts +19 -0
- package/dist/src/lib/messages/eip191/eip191MessageBuilder.d.ts.map +1 -0
- package/dist/src/lib/messages/eip191/eip191MessageBuilder.js +27 -0
- package/dist/src/lib/messages/eip191/index.d.ts +3 -0
- package/dist/src/lib/messages/eip191/index.d.ts.map +1 -0
- package/dist/src/lib/messages/eip191/index.js +19 -0
- package/dist/src/lib/messages/index.d.ts +3 -0
- package/dist/src/lib/messages/index.d.ts.map +1 -0
- package/dist/src/lib/messages/index.js +19 -0
- package/dist/src/lib/messages/messageBuilderFactory.d.ts +7 -0
- package/dist/src/lib/messages/messageBuilderFactory.d.ts.map +1 -0
- package/dist/src/lib/messages/messageBuilderFactory.js +20 -0
- package/dist/src/lib/transaction.d.ts +67 -0
- package/dist/src/lib/transaction.d.ts.map +1 -0
- package/dist/src/lib/transaction.js +142 -0
- package/dist/src/lib/transactionBuilder.d.ts +251 -0
- package/dist/src/lib/transactionBuilder.d.ts.map +1 -0
- package/dist/src/lib/transactionBuilder.js +742 -0
- package/dist/src/lib/transferBuilder.d.ts +76 -0
- package/dist/src/lib/transferBuilder.d.ts.map +1 -0
- package/dist/src/lib/transferBuilder.js +307 -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 +120 -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 +17 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC1155.d.ts.map +1 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC1155.js +96 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC721.d.ts +16 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC721.d.ts.map +1 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC721.js +81 -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 +270 -0
- package/dist/src/lib/utils.d.ts.map +1 -0
- package/dist/src/lib/utils.js +717 -0
- package/dist/src/lib/walletUtil.d.ts +32 -0
- package/dist/src/lib/walletUtil.d.ts.map +1 -0
- package/dist/src/lib/walletUtil.js +35 -0
- package/dist/src/types.d.ts +9 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +3 -0
- package/dist/test/index.d.ts +2 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/index.js +18 -0
- package/dist/test/unit/coin.d.ts +8 -0
- package/dist/test/unit/coin.d.ts.map +1 -0
- package/dist/test/unit/coin.js +577 -0
- package/dist/test/unit/index.d.ts +6 -0
- package/dist/test/unit/index.d.ts.map +1 -0
- package/dist/test/unit/index.js +22 -0
- package/dist/test/unit/messages/eip191/eip191Message.d.ts +2 -0
- package/dist/test/unit/messages/eip191/eip191Message.d.ts.map +1 -0
- package/dist/test/unit/messages/eip191/eip191Message.js +137 -0
- package/dist/test/unit/messages/eip191/eip191MessageBuilder.d.ts +2 -0
- package/dist/test/unit/messages/eip191/eip191MessageBuilder.d.ts.map +1 -0
- package/dist/test/unit/messages/eip191/eip191MessageBuilder.js +95 -0
- package/dist/test/unit/messages/fixtures.d.ts +24 -0
- package/dist/test/unit/messages/fixtures.d.ts.map +1 -0
- package/dist/test/unit/messages/fixtures.js +28 -0
- package/dist/test/unit/messages/index.d.ts +4 -0
- package/dist/test/unit/messages/index.d.ts.map +1 -0
- package/dist/test/unit/messages/index.js +20 -0
- package/dist/test/unit/messages/messageBuilderFactory.d.ts +2 -0
- package/dist/test/unit/messages/messageBuilderFactory.d.ts.map +1 -0
- package/dist/test/unit/messages/messageBuilderFactory.js +44 -0
- package/dist/test/unit/token.d.ts +2 -0
- package/dist/test/unit/token.d.ts.map +1 -0
- package/dist/test/unit/token.js +37 -0
- package/dist/test/unit/transaction.d.ts +3 -0
- package/dist/test/unit/transaction.d.ts.map +1 -0
- package/dist/test/unit/transaction.js +60 -0
- package/dist/test/unit/transactionBuilder/addressInitialization.d.ts +8 -0
- package/dist/test/unit/transactionBuilder/addressInitialization.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/addressInitialization.js +95 -0
- package/dist/test/unit/transactionBuilder/index.d.ts +4 -0
- package/dist/test/unit/transactionBuilder/index.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/index.js +20 -0
- package/dist/test/unit/transactionBuilder/send.d.ts +3 -0
- package/dist/test/unit/transactionBuilder/send.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/send.js +197 -0
- package/dist/test/unit/transactionBuilder/walletInitialization.d.ts +10 -0
- package/dist/test/unit/transactionBuilder/walletInitialization.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/walletInitialization.js +124 -0
- package/dist/test/unit/transferBuilder.d.ts +2 -0
- package/dist/test/unit/transferBuilder.d.ts.map +1 -0
- package/dist/test/unit/transferBuilder.js +76 -0
- package/dist/tsconfig.tsbuildinfo +1 -10143
- package/index.ts +2 -0
- package/package.json +29 -9
|
@@ -0,0 +1,2095 @@
|
|
|
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 secp256k1_1 = require("@bitgo-beta/secp256k1");
|
|
13
|
+
const statics_1 = require("@bitgo-beta/statics");
|
|
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_2 = __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
|
+
return this.staticsCoin?.network;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Evaluates whether an address string is valid for this coin
|
|
104
|
+
* @param {string} address
|
|
105
|
+
* @returns {boolean} True if address is the valid ethlike adderss
|
|
106
|
+
*/
|
|
107
|
+
isValidAddress(address) {
|
|
108
|
+
return exports.optionalDeps.ethUtil.isValidAddress(exports.optionalDeps.ethUtil.addHexPrefix(address));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Flag for sending data along with transactions
|
|
112
|
+
* @returns {boolean} True if okay to send tx data (ETH), false otherwise
|
|
113
|
+
*/
|
|
114
|
+
transactionDataAllowed() {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Default expire time for a contract call (1 week)
|
|
119
|
+
* @returns {number} Time in seconds
|
|
120
|
+
*/
|
|
121
|
+
getDefaultExpireTime() {
|
|
122
|
+
return Math.floor(new Date().getTime() / 1000) + 60 * 60 * 24 * 7;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Method to get the custom chain common object based on params from recovery
|
|
126
|
+
* @param {number} chainId - the chain id of the custom chain
|
|
127
|
+
* @returns {EthLikeCommon.default}
|
|
128
|
+
*/
|
|
129
|
+
static getCustomChainCommon(chainId) {
|
|
130
|
+
const coinName = statics_1.CoinMap.coinNameFromChainId(chainId);
|
|
131
|
+
const coin = statics_1.coins.get(coinName);
|
|
132
|
+
const ethLikeCommon = (0, lib_1.getCommon)(coin.network);
|
|
133
|
+
return ethLikeCommon;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Gets correct Eth Common object based on params from either recovery or tx building
|
|
137
|
+
* @param {EIP1559} eip1559 - configs that specify whether we should construct an eip1559 tx
|
|
138
|
+
* @param {ReplayProtectionOptions} replayProtectionOptions - check if chain id supports replay protection
|
|
139
|
+
* @returns {EthLikeCommon.default}
|
|
140
|
+
*/
|
|
141
|
+
getEthLikeCommon(eip1559, replayProtectionOptions) {
|
|
142
|
+
// if eip1559 params are specified, default to london hardfork, otherwise,
|
|
143
|
+
// default to tangerine whistle to avoid replay protection issues
|
|
144
|
+
const defaultHardfork = !!eip1559 ? 'london' : exports.optionalDeps.EthCommon.Hardfork.Petersburg;
|
|
145
|
+
const ethLikeCommon = AbstractEthLikeNewCoins.getCustomChainCommon(replayProtectionOptions?.chain);
|
|
146
|
+
ethLikeCommon.setHardfork(replayProtectionOptions?.hardfork ?? defaultHardfork);
|
|
147
|
+
return ethLikeCommon;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Method to build the tx object
|
|
151
|
+
* @param {BuildTransactionParams} params - params to build transaction
|
|
152
|
+
* @returns {EthLikeTxLib.FeeMarketEIP1559Transaction | EthLikeTxLib.Transaction}
|
|
153
|
+
*/
|
|
154
|
+
buildTransaction(params) {
|
|
155
|
+
// if eip1559 params are specified, default to london hardfork, otherwise,
|
|
156
|
+
// default to tangerine whistle to avoid replay protection issues
|
|
157
|
+
const ethLikeCommon = this.getEthLikeCommon(params.eip1559, params.replayProtectionOptions);
|
|
158
|
+
const baseParams = {
|
|
159
|
+
to: params.to,
|
|
160
|
+
nonce: params.nonce,
|
|
161
|
+
value: params.value,
|
|
162
|
+
data: params.data,
|
|
163
|
+
gasLimit: new exports.optionalDeps.ethUtil.BN(params.gasLimit),
|
|
164
|
+
};
|
|
165
|
+
const unsignedEthTx = !!params.eip1559
|
|
166
|
+
? exports.optionalDeps.EthTx.FeeMarketEIP1559Transaction.fromTxData({
|
|
167
|
+
...baseParams,
|
|
168
|
+
maxFeePerGas: new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas),
|
|
169
|
+
maxPriorityFeePerGas: new exports.optionalDeps.ethUtil.BN(params.eip1559.maxPriorityFeePerGas),
|
|
170
|
+
}, { common: ethLikeCommon })
|
|
171
|
+
: exports.optionalDeps.EthTx.Transaction.fromTxData({
|
|
172
|
+
...baseParams,
|
|
173
|
+
gasPrice: new exports.optionalDeps.ethUtil.BN(params.gasPrice),
|
|
174
|
+
}, { common: ethLikeCommon });
|
|
175
|
+
return unsignedEthTx;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Query explorer for the balance of an address
|
|
179
|
+
* @param {String} address - the ETHLike address
|
|
180
|
+
* @returns {BigNumber} address balance
|
|
181
|
+
*/
|
|
182
|
+
async queryAddressBalance(address) {
|
|
183
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
184
|
+
chainid: this.getChainId().toString(),
|
|
185
|
+
module: 'account',
|
|
186
|
+
action: 'balance',
|
|
187
|
+
address: address,
|
|
188
|
+
});
|
|
189
|
+
// throw if the result does not exist or the result is not a valid number
|
|
190
|
+
if (!result || !result.result || isNaN(result.result)) {
|
|
191
|
+
throw new Error(`Could not obtain address balance for ${address} from the explorer, got: ${result.result}`);
|
|
192
|
+
}
|
|
193
|
+
return new exports.optionalDeps.ethUtil.BN(result.result, 10);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* @param {Recipient[]} recipients - the recipients of the transaction
|
|
197
|
+
* @param {number} expireTime - the expire time of the transaction
|
|
198
|
+
* @param {number} contractSequenceId - the contract sequence id of the transaction
|
|
199
|
+
* @returns {string}
|
|
200
|
+
*/
|
|
201
|
+
getOperationSha3ForExecuteAndConfirm(recipients, expireTime, contractSequenceId) {
|
|
202
|
+
if (!recipients || !Array.isArray(recipients)) {
|
|
203
|
+
throw new Error('expecting array of recipients');
|
|
204
|
+
}
|
|
205
|
+
// Right now we only support 1 recipient
|
|
206
|
+
if (recipients.length !== 1) {
|
|
207
|
+
throw new Error('must send to exactly 1 recipient');
|
|
208
|
+
}
|
|
209
|
+
if (!lodash_1.default.isNumber(expireTime)) {
|
|
210
|
+
throw new Error('expireTime must be number of seconds since epoch');
|
|
211
|
+
}
|
|
212
|
+
if (!lodash_1.default.isNumber(contractSequenceId)) {
|
|
213
|
+
throw new Error('contractSequenceId must be number');
|
|
214
|
+
}
|
|
215
|
+
// Check inputs
|
|
216
|
+
recipients.forEach(function (recipient) {
|
|
217
|
+
if (!lodash_1.default.isString(recipient.address) ||
|
|
218
|
+
!exports.optionalDeps.ethUtil.isValidAddress(exports.optionalDeps.ethUtil.addHexPrefix(recipient.address))) {
|
|
219
|
+
throw new Error('Invalid address: ' + recipient.address);
|
|
220
|
+
}
|
|
221
|
+
let amount;
|
|
222
|
+
try {
|
|
223
|
+
amount = new bignumber_js_1.BigNumber(recipient.amount);
|
|
224
|
+
}
|
|
225
|
+
catch (e) {
|
|
226
|
+
throw new Error('Invalid amount for: ' + recipient.address + ' - should be numeric');
|
|
227
|
+
}
|
|
228
|
+
recipient.amount = amount.toFixed(0);
|
|
229
|
+
if (recipient.data && !lodash_1.default.isString(recipient.data)) {
|
|
230
|
+
throw new Error('Data for recipient ' + recipient.address + ' - should be of type hex string');
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
const recipient = recipients[0];
|
|
234
|
+
return exports.optionalDeps.ethUtil.bufferToHex(exports.optionalDeps.ethAbi.soliditySHA3(...this.getOperation(recipient, expireTime, contractSequenceId)));
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get transfer operation for coin
|
|
238
|
+
* @param {Recipient} recipient - recipient info
|
|
239
|
+
* @param {number} expireTime - expiry time
|
|
240
|
+
* @param {number} contractSequenceId - sequence id
|
|
241
|
+
* @returns {Array} operation array
|
|
242
|
+
*/
|
|
243
|
+
getOperation(recipient, expireTime, contractSequenceId) {
|
|
244
|
+
const network = this.getNetwork();
|
|
245
|
+
return [
|
|
246
|
+
['string', 'address', 'uint', 'bytes', 'uint', 'uint'],
|
|
247
|
+
[
|
|
248
|
+
network.nativeCoinOperationHashPrefix,
|
|
249
|
+
new exports.optionalDeps.ethUtil.BN(exports.optionalDeps.ethUtil.stripHexPrefix(recipient.address), 16),
|
|
250
|
+
recipient.amount,
|
|
251
|
+
Buffer.from(exports.optionalDeps.ethUtil.stripHexPrefix(exports.optionalDeps.ethUtil.padToEven(recipient.data || '')), 'hex'),
|
|
252
|
+
expireTime,
|
|
253
|
+
contractSequenceId,
|
|
254
|
+
],
|
|
255
|
+
];
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Queries the contract (via explorer API) for the next sequence ID
|
|
259
|
+
* @param {String} address - address of the contract
|
|
260
|
+
* @returns {Promise<Number>} sequence ID
|
|
261
|
+
*/
|
|
262
|
+
async querySequenceId(address) {
|
|
263
|
+
// Get sequence ID using contract call
|
|
264
|
+
const sequenceIdMethodSignature = exports.optionalDeps.ethAbi.methodID('getNextSequenceId', []);
|
|
265
|
+
const sequenceIdArgs = exports.optionalDeps.ethAbi.rawEncode([], []);
|
|
266
|
+
const sequenceIdData = Buffer.concat([sequenceIdMethodSignature, sequenceIdArgs]).toString('hex');
|
|
267
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
268
|
+
chainid: this.getChainId().toString(),
|
|
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
|
+
chainid: this.getChainId().toString(),
|
|
441
|
+
module: 'account',
|
|
442
|
+
action: 'txlist',
|
|
443
|
+
address,
|
|
444
|
+
});
|
|
445
|
+
if (!result || !Array.isArray(result.result)) {
|
|
446
|
+
throw new Error('Unable to find next nonce from Etherscan, got: ' + JSON.stringify(result));
|
|
447
|
+
}
|
|
448
|
+
const backupKeyTxList = result.result;
|
|
449
|
+
if (backupKeyTxList.length > 0) {
|
|
450
|
+
// Calculate last nonce used
|
|
451
|
+
const outgoingTxs = backupKeyTxList.filter((tx) => tx.from === address);
|
|
452
|
+
nonce = outgoingTxs.length;
|
|
453
|
+
}
|
|
454
|
+
return nonce;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Helper function for recover()
|
|
458
|
+
* This transforms the unsigned transaction information into a format the BitGo offline vault expects
|
|
459
|
+
* @param {UnformattedTxInfo} txInfo - tx info
|
|
460
|
+
* @param {EthLikeTxLib.Transaction | EthLikeTxLib.FeeMarketEIP1559Transaction} ethTx - the ethereumjs tx object
|
|
461
|
+
* @param {string} userKey - the user's key
|
|
462
|
+
* @param {string} backupKey - the backup key
|
|
463
|
+
* @param {Buffer} gasPrice - gas price for the tx
|
|
464
|
+
* @param {number} gasLimit - gas limit for the tx
|
|
465
|
+
* @param {EIP1559} eip1559 - eip1559 params
|
|
466
|
+
* @param {ReplayProtectionOptions} replayProtectionOptions - replay protection options
|
|
467
|
+
* @returns {Promise<OfflineVaultTxInfo>}
|
|
468
|
+
*/
|
|
469
|
+
async formatForOfflineVault(txInfo, ethTx, userKey, backupKey, gasPrice, gasLimit, eip1559, replayProtectionOptions) {
|
|
470
|
+
if (!ethTx.to) {
|
|
471
|
+
throw new Error('Eth tx must have a `to` address');
|
|
472
|
+
}
|
|
473
|
+
const backupHDNode = secp256k1_1.bip32.fromBase58(backupKey);
|
|
474
|
+
const backupSigningKey = backupHDNode.publicKey;
|
|
475
|
+
const response = {
|
|
476
|
+
tx: ethTx.serialize().toString('hex'),
|
|
477
|
+
userKey,
|
|
478
|
+
backupKey,
|
|
479
|
+
coin: this.getChain(),
|
|
480
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
481
|
+
gasLimit,
|
|
482
|
+
recipients: [txInfo.recipient],
|
|
483
|
+
walletContractAddress: ethTx.to.toString(),
|
|
484
|
+
amount: txInfo.recipient.amount,
|
|
485
|
+
backupKeyNonce: await this.getAddressNonce(`0x${exports.optionalDeps.ethUtil.publicToAddress(backupSigningKey, true).toString('hex')}`),
|
|
486
|
+
eip1559,
|
|
487
|
+
replayProtectionOptions,
|
|
488
|
+
};
|
|
489
|
+
lodash_1.default.extend(response, txInfo);
|
|
490
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
491
|
+
return response;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Helper function for recover()
|
|
495
|
+
* This transforms the unsigned transaction information into a format the BitGo offline vault expects
|
|
496
|
+
* @param {UnformattedTxInfo} txInfo - tx info
|
|
497
|
+
* @param {EthLikeTxLib.Transaction | EthLikeTxLib.FeeMarketEIP1559Transaction} ethTx - the ethereumjs tx object
|
|
498
|
+
* @param {string} userKey - the user's key
|
|
499
|
+
* @param {string} backupKey - the backup key
|
|
500
|
+
* @param {Buffer} gasPrice - gas price for the tx
|
|
501
|
+
* @param {number} gasLimit - gas limit for the tx
|
|
502
|
+
* @param {number} backupKeyNonce - the nonce of the backup key address
|
|
503
|
+
* @param {EIP1559} eip1559 - eip1559 params
|
|
504
|
+
* @param {ReplayProtectionOptions} replayProtectionOptions - replay protection options
|
|
505
|
+
* @returns {Promise<OfflineVaultTxInfo>}
|
|
506
|
+
*/
|
|
507
|
+
formatForOfflineVaultTSS(txInfo, ethTx, userKey, backupKey, gasPrice, gasLimit, backupKeyNonce, eip1559, replayProtectionOptions) {
|
|
508
|
+
if (!ethTx.to) {
|
|
509
|
+
throw new Error('Eth tx must have a `to` address');
|
|
510
|
+
}
|
|
511
|
+
const response = {
|
|
512
|
+
tx: ethTx.serialize().toString('hex'),
|
|
513
|
+
txHex: ethTx.getMessageToSign(false).toString(),
|
|
514
|
+
userKey,
|
|
515
|
+
backupKey,
|
|
516
|
+
coin: this.getChain(),
|
|
517
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
518
|
+
gasLimit,
|
|
519
|
+
recipients: [txInfo.recipient],
|
|
520
|
+
walletContractAddress: ethTx.to.toString(),
|
|
521
|
+
amount: txInfo.recipient.amount,
|
|
522
|
+
backupKeyNonce: backupKeyNonce,
|
|
523
|
+
eip1559,
|
|
524
|
+
replayProtectionOptions,
|
|
525
|
+
};
|
|
526
|
+
lodash_1.default.extend(response, txInfo);
|
|
527
|
+
return response;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Check whether the gas price passed in by user are within our max and min bounds
|
|
531
|
+
* If they are not set, set them to the defaults
|
|
532
|
+
* @param {number} userGasPrice - user defined gas price
|
|
533
|
+
* @returns {number} the gas price to use for this transaction
|
|
534
|
+
*/
|
|
535
|
+
setGasPrice(userGasPrice) {
|
|
536
|
+
if (!userGasPrice) {
|
|
537
|
+
return statics_1.ethGasConfigs.defaultGasPrice;
|
|
538
|
+
}
|
|
539
|
+
const gasPriceMax = statics_1.ethGasConfigs.maximumGasPrice;
|
|
540
|
+
const gasPriceMin = statics_1.ethGasConfigs.minimumGasPrice;
|
|
541
|
+
if (userGasPrice < gasPriceMin || userGasPrice > gasPriceMax) {
|
|
542
|
+
throw new Error(`Gas price must be between ${gasPriceMin} and ${gasPriceMax}`);
|
|
543
|
+
}
|
|
544
|
+
return userGasPrice;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Check whether gas limit passed in by user are within our max and min bounds
|
|
548
|
+
* If they are not set, set them to the defaults
|
|
549
|
+
* @param {number} userGasLimit user defined gas limit
|
|
550
|
+
* @returns {number} the gas limit to use for this transaction
|
|
551
|
+
*/
|
|
552
|
+
setGasLimit(userGasLimit) {
|
|
553
|
+
if (!userGasLimit) {
|
|
554
|
+
return statics_1.ethGasConfigs.defaultGasLimit;
|
|
555
|
+
}
|
|
556
|
+
const gasLimitMax = statics_1.ethGasConfigs.maximumGasLimit;
|
|
557
|
+
const gasLimitMin = statics_1.ethGasConfigs.minimumGasLimit;
|
|
558
|
+
if (userGasLimit < gasLimitMin || userGasLimit > gasLimitMax) {
|
|
559
|
+
throw new Error(`Gas limit must be between ${gasLimitMin} and ${gasLimitMax}`);
|
|
560
|
+
}
|
|
561
|
+
return userGasLimit;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Helper function for signTransaction for the rare case that SDK is doing the second signature
|
|
565
|
+
* Note: we are expecting this to be called from the offline vault
|
|
566
|
+
* @param {SignFinalOptions.txPrebuild} params.txPrebuild
|
|
567
|
+
* @param {string} params.prv
|
|
568
|
+
* @returns {{txHex: string}}
|
|
569
|
+
*/
|
|
570
|
+
async signFinalEthLike(params) {
|
|
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(params.common);
|
|
576
|
+
try {
|
|
577
|
+
txBuilder.from(params.txPrebuild.halfSigned?.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
|
+
// Normally the SDK provides the first signature for an EthLike tx, but occasionally it provides the second and final one.
|
|
594
|
+
if (params.isLastSignature) {
|
|
595
|
+
// In this case when we're doing the second (final) signature, the logic is different.
|
|
596
|
+
return await this.signFinalEthLike(params);
|
|
597
|
+
}
|
|
598
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
599
|
+
txBuilder.from(params.txPrebuild.txHex);
|
|
600
|
+
txBuilder
|
|
601
|
+
.transfer()
|
|
602
|
+
.coin(this.staticsCoin?.name)
|
|
603
|
+
.key(new lib_1.KeyPair({ prv: params.prv }).getKeys().prv);
|
|
604
|
+
if (params.walletVersion) {
|
|
605
|
+
txBuilder.walletVersion(params.walletVersion);
|
|
606
|
+
}
|
|
607
|
+
const transaction = await txBuilder.build();
|
|
608
|
+
// In case of tx with contract data from a custodial wallet, we are running into an issue
|
|
609
|
+
// as halfSigned is not having the data field. So, we are adding the data field to the halfSigned tx
|
|
610
|
+
let recipients = params.txPrebuild.recipients || params.recipients;
|
|
611
|
+
if (recipients === undefined) {
|
|
612
|
+
recipients = transaction.outputs.map((output) => ({ address: output.address, amount: output.value }));
|
|
613
|
+
}
|
|
614
|
+
const txParams = {
|
|
615
|
+
eip1559: params.txPrebuild.eip1559,
|
|
616
|
+
txHex: transaction.toBroadcastFormat(),
|
|
617
|
+
recipients: recipients,
|
|
618
|
+
expiration: params.txPrebuild.expireTime,
|
|
619
|
+
hopTransaction: params.txPrebuild.hopTransaction,
|
|
620
|
+
custodianTransactionId: params.custodianTransactionId,
|
|
621
|
+
expireTime: params.expireTime,
|
|
622
|
+
contractSequenceId: params.txPrebuild.nextContractSequenceId,
|
|
623
|
+
sequenceId: params.sequenceId,
|
|
624
|
+
...(params.txPrebuild.isBatch ? { isBatch: params.txPrebuild.isBatch } : {}),
|
|
625
|
+
};
|
|
626
|
+
return { halfSigned: txParams };
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Method to validate recovery params
|
|
630
|
+
* @param {RecoverOptions} params
|
|
631
|
+
* @returns {void}
|
|
632
|
+
*/
|
|
633
|
+
validateRecoveryParams(params) {
|
|
634
|
+
if (lodash_1.default.isUndefined(params.userKey)) {
|
|
635
|
+
throw new Error('missing userKey');
|
|
636
|
+
}
|
|
637
|
+
if (lodash_1.default.isUndefined(params.backupKey)) {
|
|
638
|
+
throw new Error('missing backupKey');
|
|
639
|
+
}
|
|
640
|
+
if (lodash_1.default.isUndefined(params.walletPassphrase) && !params.userKey.startsWith('xpub') && !params.isTss) {
|
|
641
|
+
throw new Error('missing wallet passphrase');
|
|
642
|
+
}
|
|
643
|
+
if (lodash_1.default.isUndefined(params.walletContractAddress) || !this.isValidAddress(params.walletContractAddress)) {
|
|
644
|
+
throw new Error('invalid walletContractAddress');
|
|
645
|
+
}
|
|
646
|
+
if (lodash_1.default.isUndefined(params.recoveryDestination) || !this.isValidAddress(params.recoveryDestination)) {
|
|
647
|
+
throw new Error('invalid recoveryDestination');
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Helper which Adds signatures to tx object and re-serializes tx
|
|
652
|
+
* @param {EthLikeCommon.default} ethCommon
|
|
653
|
+
* @param {EthLikeTxLib.FeeMarketEIP1559Transaction | EthLikeTxLib.Transaction} tx
|
|
654
|
+
* @param {ECDSAMethodTypes.Signature} signature
|
|
655
|
+
* @returns {EthLikeTxLib.FeeMarketEIP1559Transaction | EthLikeTxLib.Transaction}
|
|
656
|
+
*/
|
|
657
|
+
getSignedTxFromSignature(ethCommon, tx, signature) {
|
|
658
|
+
// get signed Tx from signature
|
|
659
|
+
const txData = tx.toJSON();
|
|
660
|
+
const yParity = signature.recid;
|
|
661
|
+
const baseParams = {
|
|
662
|
+
to: txData.to,
|
|
663
|
+
nonce: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.nonce), 'hex'),
|
|
664
|
+
value: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.value), 'hex'),
|
|
665
|
+
gasLimit: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.gasLimit), 'hex'),
|
|
666
|
+
data: txData.data,
|
|
667
|
+
r: (0, ethereumjs_util_1.addHexPrefix)(signature.r),
|
|
668
|
+
s: (0, ethereumjs_util_1.addHexPrefix)(signature.s),
|
|
669
|
+
};
|
|
670
|
+
let finalTx;
|
|
671
|
+
if (txData.maxFeePerGas && txData.maxPriorityFeePerGas) {
|
|
672
|
+
finalTx = tx_1.FeeMarketEIP1559Transaction.fromTxData({
|
|
673
|
+
...baseParams,
|
|
674
|
+
maxPriorityFeePerGas: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.maxPriorityFeePerGas), 'hex'),
|
|
675
|
+
maxFeePerGas: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.maxFeePerGas), 'hex'),
|
|
676
|
+
v: new bn_js_1.default(yParity.toString()),
|
|
677
|
+
}, { common: ethCommon });
|
|
678
|
+
}
|
|
679
|
+
else if (txData.gasPrice) {
|
|
680
|
+
const v = BigInt(35) + BigInt(yParity) + BigInt(ethCommon.chainIdBN().toNumber()) * BigInt(2);
|
|
681
|
+
finalTx = tx_1.Transaction.fromTxData({
|
|
682
|
+
...baseParams,
|
|
683
|
+
v: new bn_js_1.default(v.toString()),
|
|
684
|
+
gasPrice: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.gasPrice.toString()), 'hex'),
|
|
685
|
+
}, { common: ethCommon });
|
|
686
|
+
}
|
|
687
|
+
return finalTx;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Builds a funds recovery transaction without BitGo
|
|
691
|
+
* @param params
|
|
692
|
+
* @param {string} params.userKey - [encrypted] xprv
|
|
693
|
+
* @param {string} params.backupKey - [encrypted] xprv or xpub if the xprv is held by a KRS provider
|
|
694
|
+
* @param {string} params.walletPassphrase - used to decrypt userKey and backupKey
|
|
695
|
+
* @param {string} params.walletContractAddress - the ETH address of the wallet contract
|
|
696
|
+
* @param {string} params.krsProvider - necessary if backup key is held by KRS
|
|
697
|
+
* @param {string} params.recoveryDestination - target address to send recovered funds to
|
|
698
|
+
* @param {string} params.bitgoFeeAddress - wrong chain wallet fee address for evm based cross chain recovery txn
|
|
699
|
+
* @param {string} params.bitgoDestinationAddress - target bitgo address where fee will be sent for evm based cross chain recovery txn
|
|
700
|
+
*/
|
|
701
|
+
async recover(params) {
|
|
702
|
+
if (params.isTss === true) {
|
|
703
|
+
return this.recoverTSS(params);
|
|
704
|
+
}
|
|
705
|
+
return this.recoverEthLike(params);
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Builds a funds recovery transaction without BitGo for non-TSS transaction
|
|
709
|
+
* @param params
|
|
710
|
+
* @param {string} params.userKey [encrypted] xprv or xpub
|
|
711
|
+
* @param {string} params.backupKey [encrypted] xprv or xpub if the xprv is held by a KRS provider
|
|
712
|
+
* @param {string} params.walletPassphrase used to decrypt userKey and backupKey
|
|
713
|
+
* @param {string} params.walletContractAddress the EthLike address of the wallet contract
|
|
714
|
+
* @param {string} params.krsProvider necessary if backup key is held by KRS
|
|
715
|
+
* @param {string} params.recoveryDestination target address to send recovered funds to
|
|
716
|
+
* @param {string} params.bitgoFeeAddress wrong chain wallet fee address for evm based cross chain recovery txn
|
|
717
|
+
* @param {string} params.bitgoDestinationAddress target bitgo address where fee will be sent for evm based cross chain recovery txn
|
|
718
|
+
* @returns {Promise<RecoveryInfo | OfflineVaultTxInfo>}
|
|
719
|
+
*/
|
|
720
|
+
async recoverEthLike(params) {
|
|
721
|
+
// bitgoFeeAddress is only defined when it is a evm cross chain recovery
|
|
722
|
+
// as we use fee from this wrong chain address for the recovery txn on the correct chain.
|
|
723
|
+
if (params.bitgoFeeAddress) {
|
|
724
|
+
return this.recoverEthLikeforEvmBasedRecovery(params);
|
|
725
|
+
}
|
|
726
|
+
this.validateRecoveryParams(params);
|
|
727
|
+
const isUnsignedSweep = (0, sdk_core_1.getIsUnsignedSweep)(params);
|
|
728
|
+
// Clean up whitespace from entered values
|
|
729
|
+
let userKey = params.userKey.replace(/\s/g, '');
|
|
730
|
+
const backupKey = params.backupKey.replace(/\s/g, '');
|
|
731
|
+
const gasLimit = new exports.optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit));
|
|
732
|
+
const gasPrice = params.eip1559
|
|
733
|
+
? new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas)
|
|
734
|
+
: new exports.optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice));
|
|
735
|
+
if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) {
|
|
736
|
+
try {
|
|
737
|
+
userKey = this.bitgo.decrypt({
|
|
738
|
+
input: userKey,
|
|
739
|
+
password: params.walletPassphrase,
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
catch (e) {
|
|
743
|
+
throw new Error(`Error decrypting user keychain: ${e.message}`);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
let backupKeyAddress;
|
|
747
|
+
let backupSigningKey;
|
|
748
|
+
if (isUnsignedSweep) {
|
|
749
|
+
const backupHDNode = secp256k1_1.bip32.fromBase58(backupKey);
|
|
750
|
+
backupSigningKey = backupHDNode.publicKey;
|
|
751
|
+
backupKeyAddress = `0x${exports.optionalDeps.ethUtil.publicToAddress(backupSigningKey, true).toString('hex')}`;
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
// Decrypt backup private key and get address
|
|
755
|
+
let backupPrv;
|
|
756
|
+
try {
|
|
757
|
+
backupPrv = this.bitgo.decrypt({
|
|
758
|
+
input: backupKey,
|
|
759
|
+
password: params.walletPassphrase,
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
catch (e) {
|
|
763
|
+
throw new Error(`Error decrypting backup keychain: ${e.message}`);
|
|
764
|
+
}
|
|
765
|
+
const keyPair = new lib_1.KeyPair({ prv: backupPrv });
|
|
766
|
+
backupSigningKey = keyPair.getKeys().prv;
|
|
767
|
+
if (!backupSigningKey) {
|
|
768
|
+
throw new Error('no private key');
|
|
769
|
+
}
|
|
770
|
+
backupKeyAddress = keyPair.getAddress();
|
|
771
|
+
}
|
|
772
|
+
const backupKeyNonce = await this.getAddressNonce(backupKeyAddress);
|
|
773
|
+
// get balance of backupKey to ensure funds are available to pay fees
|
|
774
|
+
const backupKeyBalance = await this.queryAddressBalance(backupKeyAddress);
|
|
775
|
+
let totalGasNeeded = gasPrice.mul(gasLimit);
|
|
776
|
+
// On optimism chain, L1 fees is to be paid as well apart from L2 fees
|
|
777
|
+
// So we are adding the amount that can be used up as l1 fees
|
|
778
|
+
if (this.staticsCoin?.family === 'opeth') {
|
|
779
|
+
totalGasNeeded = totalGasNeeded.add(new exports.optionalDeps.ethUtil.BN(statics_1.ethGasConfigs.opethGasL1Fees));
|
|
780
|
+
}
|
|
781
|
+
const weiToGwei = 10 ** 9;
|
|
782
|
+
if (backupKeyBalance.lt(totalGasNeeded)) {
|
|
783
|
+
throw new Error(`Backup key address ${backupKeyAddress} has balance ${(backupKeyBalance / weiToGwei).toString()} Gwei.` +
|
|
784
|
+
`This address must have a balance of at least ${(totalGasNeeded / weiToGwei).toString()}` +
|
|
785
|
+
` Gwei to perform recoveries. Try sending some funds to this address then retry.`);
|
|
786
|
+
}
|
|
787
|
+
// get balance of wallet
|
|
788
|
+
const txAmount = await this.queryAddressBalance(params.walletContractAddress);
|
|
789
|
+
if (new bignumber_js_1.BigNumber(txAmount).isLessThanOrEqualTo(0)) {
|
|
790
|
+
throw new Error('Wallet does not have enough funds to recover');
|
|
791
|
+
}
|
|
792
|
+
// build recipients object
|
|
793
|
+
const recipients = [
|
|
794
|
+
{
|
|
795
|
+
address: params.recoveryDestination,
|
|
796
|
+
amount: txAmount.toString(10),
|
|
797
|
+
},
|
|
798
|
+
];
|
|
799
|
+
// Get sequence ID using contract call
|
|
800
|
+
// we need to wait between making two explorer api calls to avoid getting banned
|
|
801
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
802
|
+
const sequenceId = await this.querySequenceId(params.walletContractAddress);
|
|
803
|
+
let operationHash, signature;
|
|
804
|
+
// Get operation hash and sign it
|
|
805
|
+
if (!isUnsignedSweep) {
|
|
806
|
+
operationHash = this.getOperationSha3ForExecuteAndConfirm(recipients, this.getDefaultExpireTime(), sequenceId);
|
|
807
|
+
signature = sdk_core_1.Util.ethSignMsgHash(operationHash, sdk_core_1.Util.xprvToEthPrivateKey(userKey));
|
|
808
|
+
try {
|
|
809
|
+
sdk_core_1.Util.ecRecoverEthAddress(operationHash, signature);
|
|
810
|
+
}
|
|
811
|
+
catch (e) {
|
|
812
|
+
throw new Error('Invalid signature');
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
const txInfo = {
|
|
816
|
+
recipient: recipients[0],
|
|
817
|
+
expireTime: this.getDefaultExpireTime(),
|
|
818
|
+
contractSequenceId: sequenceId,
|
|
819
|
+
operationHash: operationHash,
|
|
820
|
+
signature: signature,
|
|
821
|
+
gasLimit: gasLimit.toString(10),
|
|
822
|
+
};
|
|
823
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
824
|
+
txBuilder.counter(backupKeyNonce);
|
|
825
|
+
txBuilder.contract(params.walletContractAddress);
|
|
826
|
+
let txFee;
|
|
827
|
+
if (params.eip1559) {
|
|
828
|
+
txFee = {
|
|
829
|
+
eip1559: {
|
|
830
|
+
maxPriorityFeePerGas: params.eip1559.maxPriorityFeePerGas,
|
|
831
|
+
maxFeePerGas: params.eip1559.maxFeePerGas,
|
|
832
|
+
},
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
txFee = { fee: gasPrice.toString() };
|
|
837
|
+
}
|
|
838
|
+
txBuilder.fee({
|
|
839
|
+
...txFee,
|
|
840
|
+
gasLimit: gasLimit.toString(),
|
|
841
|
+
});
|
|
842
|
+
const transferBuilder = txBuilder.transfer();
|
|
843
|
+
transferBuilder
|
|
844
|
+
.coin(this.staticsCoin?.name)
|
|
845
|
+
.amount(recipients[0].amount)
|
|
846
|
+
.contractSequenceId(sequenceId)
|
|
847
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
848
|
+
.to(params.recoveryDestination);
|
|
849
|
+
const tx = await txBuilder.build();
|
|
850
|
+
if (isUnsignedSweep) {
|
|
851
|
+
const response = {
|
|
852
|
+
txHex: tx.toBroadcastFormat(),
|
|
853
|
+
userKey,
|
|
854
|
+
backupKey,
|
|
855
|
+
coin: this.getChain(),
|
|
856
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
857
|
+
gasLimit,
|
|
858
|
+
recipients: [txInfo.recipient],
|
|
859
|
+
walletContractAddress: tx.toJson().to,
|
|
860
|
+
amount: txInfo.recipient.amount,
|
|
861
|
+
backupKeyNonce,
|
|
862
|
+
eip1559: params.eip1559,
|
|
863
|
+
};
|
|
864
|
+
lodash_1.default.extend(response, txInfo);
|
|
865
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
866
|
+
return response;
|
|
867
|
+
}
|
|
868
|
+
txBuilder
|
|
869
|
+
.transfer()
|
|
870
|
+
.coin(this.staticsCoin?.name)
|
|
871
|
+
.key(new lib_1.KeyPair({ prv: userKey }).getKeys().prv);
|
|
872
|
+
txBuilder.sign({ key: backupSigningKey });
|
|
873
|
+
const signedTx = await txBuilder.build();
|
|
874
|
+
return {
|
|
875
|
+
id: signedTx.toJson().id,
|
|
876
|
+
tx: signedTx.toBroadcastFormat(),
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
async sendCrossChainRecoveryTransaction(params) {
|
|
880
|
+
const buildResponse = await this.buildCrossChainRecoveryTransaction(params.recoveryId);
|
|
881
|
+
if (params.walletType === 'cold') {
|
|
882
|
+
return buildResponse;
|
|
883
|
+
}
|
|
884
|
+
if (!params.encryptedPrv) {
|
|
885
|
+
throw new Error('missing encryptedPrv');
|
|
886
|
+
}
|
|
887
|
+
let userKeyPrv;
|
|
888
|
+
try {
|
|
889
|
+
userKeyPrv = this.bitgo.decrypt({
|
|
890
|
+
input: params.encryptedPrv,
|
|
891
|
+
password: params.walletPassphrase,
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
catch (e) {
|
|
895
|
+
throw new Error(`Error decrypting user keychain: ${e.message}`);
|
|
896
|
+
}
|
|
897
|
+
const keyPair = new lib_1.KeyPair({ prv: userKeyPrv });
|
|
898
|
+
const userSigningKey = keyPair.getKeys().prv;
|
|
899
|
+
if (!userSigningKey) {
|
|
900
|
+
throw new Error('no private key');
|
|
901
|
+
}
|
|
902
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
903
|
+
const txHex = buildResponse.txHex;
|
|
904
|
+
txBuilder.from(txHex);
|
|
905
|
+
if (buildResponse.walletVersion) {
|
|
906
|
+
// If walletVersion is provided, set it in the txBuilder
|
|
907
|
+
txBuilder.walletVersion(buildResponse.walletVersion);
|
|
908
|
+
}
|
|
909
|
+
txBuilder
|
|
910
|
+
.transfer()
|
|
911
|
+
.coin(this.staticsCoin?.name)
|
|
912
|
+
.key(userSigningKey);
|
|
913
|
+
const tx = await txBuilder.build();
|
|
914
|
+
const res = await this.bitgo
|
|
915
|
+
.post(this.bitgo.microservicesUrl(`/api/recovery/v1/crosschain/${params.recoveryId}/sign`))
|
|
916
|
+
.send({ txHex: tx.toBroadcastFormat() });
|
|
917
|
+
return {
|
|
918
|
+
coin: this.staticsCoin?.name,
|
|
919
|
+
txid: res.body.txid,
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
async buildCrossChainRecoveryTransaction(recoveryId) {
|
|
923
|
+
const res = await this.bitgo.get(this.bitgo.microservicesUrl(`/api/recovery/v1/crosschain/${recoveryId}/buildtx`));
|
|
924
|
+
return {
|
|
925
|
+
coin: res.body.coin,
|
|
926
|
+
txHex: res.body.txHex,
|
|
927
|
+
txid: res.body.txid,
|
|
928
|
+
walletVersion: res.body.walletVersion,
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Builds a unsigned (for cold, custody wallet) or
|
|
933
|
+
* half-signed (for hot wallet) evm cross chain recovery transaction with
|
|
934
|
+
* same expected arguments as recover method.
|
|
935
|
+
* This helps recover funds from evm based wrong chain.
|
|
936
|
+
* @param {RecoverOptions} params
|
|
937
|
+
* @returns {Promise<RecoveryInfo | OfflineVaultTxInfo>}
|
|
938
|
+
*/
|
|
939
|
+
async recoverEthLikeforEvmBasedRecovery(params) {
|
|
940
|
+
this.validateEvmBasedRecoveryParams(params);
|
|
941
|
+
// Clean up whitespace from entered values
|
|
942
|
+
const userKey = params.userKey.replace(/\s/g, '');
|
|
943
|
+
const bitgoFeeAddress = params.bitgoFeeAddress?.replace(/\s/g, '').toLowerCase();
|
|
944
|
+
const bitgoDestinationAddress = params.bitgoDestinationAddress?.replace(/\s/g, '').toLowerCase();
|
|
945
|
+
const recoveryDestination = params.recoveryDestination?.replace(/\s/g, '').toLowerCase();
|
|
946
|
+
const walletContractAddress = params.walletContractAddress?.replace(/\s/g, '').toLowerCase();
|
|
947
|
+
const tokenContractAddress = params.tokenContractAddress?.replace(/\s/g, '').toLowerCase();
|
|
948
|
+
let userSigningKey;
|
|
949
|
+
let userKeyPrv;
|
|
950
|
+
if (params.walletPassphrase) {
|
|
951
|
+
if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) {
|
|
952
|
+
try {
|
|
953
|
+
userKeyPrv = this.bitgo.decrypt({
|
|
954
|
+
input: userKey,
|
|
955
|
+
password: params.walletPassphrase,
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
catch (e) {
|
|
959
|
+
throw new Error(`Error decrypting user keychain: ${e.message}`);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
const keyPair = new lib_1.KeyPair({ prv: userKeyPrv });
|
|
963
|
+
userSigningKey = keyPair.getKeys().prv;
|
|
964
|
+
if (!userSigningKey) {
|
|
965
|
+
throw new Error('no private key');
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
// Use default gasLimit for cold and custody wallets
|
|
969
|
+
let gasLimit = params.gasLimit || userKey.startsWith('xpub') || !userKey
|
|
970
|
+
? new exports.optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit))
|
|
971
|
+
: new exports.optionalDeps.ethUtil.BN(0);
|
|
972
|
+
const gasPrice = params.eip1559
|
|
973
|
+
? new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas)
|
|
974
|
+
: params.gasPrice
|
|
975
|
+
? new exports.optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice))
|
|
976
|
+
: await this.getGasPriceFromExternalAPI(this.staticsCoin?.name);
|
|
977
|
+
const bitgoFeeAddressNonce = await this.getAddressNonce(bitgoFeeAddress);
|
|
978
|
+
if (tokenContractAddress) {
|
|
979
|
+
return this.recoverEthLikeTokenforEvmBasedRecovery(params, bitgoFeeAddressNonce, gasLimit, gasPrice, userKey, userSigningKey);
|
|
980
|
+
}
|
|
981
|
+
// get balance of wallet
|
|
982
|
+
const txAmount = await this.queryAddressBalance(walletContractAddress);
|
|
983
|
+
const bitgoFeePercentage = 0; // TODO: BG-71912 can change the fee% here.
|
|
984
|
+
const bitgoFeeAmount = txAmount * (bitgoFeePercentage / 100);
|
|
985
|
+
// build recipients object
|
|
986
|
+
const recipients = [
|
|
987
|
+
{
|
|
988
|
+
address: recoveryDestination,
|
|
989
|
+
amount: new bignumber_js_1.BigNumber(txAmount).minus(bitgoFeeAmount).toFixed(),
|
|
990
|
+
},
|
|
991
|
+
];
|
|
992
|
+
if (bitgoFeePercentage > 0) {
|
|
993
|
+
if (lodash_1.default.isUndefined(bitgoDestinationAddress) || !this.isValidAddress(bitgoDestinationAddress)) {
|
|
994
|
+
throw new Error('invalid bitgoDestinationAddress');
|
|
995
|
+
}
|
|
996
|
+
recipients.push({
|
|
997
|
+
address: bitgoDestinationAddress,
|
|
998
|
+
amount: bitgoFeeAmount.toString(10),
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
// calculate batch data
|
|
1002
|
+
const BATCH_METHOD_NAME = 'batch';
|
|
1003
|
+
const BATCH_METHOD_TYPES = ['address[]', 'uint256[]'];
|
|
1004
|
+
const batchExecutionInfo = this.getBatchExecutionInfo(recipients);
|
|
1005
|
+
const batchData = exports.optionalDeps.ethUtil.addHexPrefix(this.getMethodCallData(BATCH_METHOD_NAME, BATCH_METHOD_TYPES, batchExecutionInfo.values).toString('hex'));
|
|
1006
|
+
// Get sequence ID using contract call
|
|
1007
|
+
// we need to wait between making two explorer api calls to avoid getting banned
|
|
1008
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1009
|
+
const sequenceId = await this.querySequenceId(walletContractAddress);
|
|
1010
|
+
const network = this.getNetwork();
|
|
1011
|
+
const batcherContractAddress = network?.batcherContractAddress;
|
|
1012
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
1013
|
+
txBuilder.counter(bitgoFeeAddressNonce);
|
|
1014
|
+
txBuilder.contract(walletContractAddress);
|
|
1015
|
+
let txFee;
|
|
1016
|
+
if (params.eip1559) {
|
|
1017
|
+
txFee = {
|
|
1018
|
+
eip1559: {
|
|
1019
|
+
maxPriorityFeePerGas: params.eip1559.maxPriorityFeePerGas,
|
|
1020
|
+
maxFeePerGas: params.eip1559.maxFeePerGas,
|
|
1021
|
+
},
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
txFee = { fee: gasPrice.toString() };
|
|
1026
|
+
}
|
|
1027
|
+
txBuilder.fee({
|
|
1028
|
+
...txFee,
|
|
1029
|
+
gasLimit: gasLimit.toString(),
|
|
1030
|
+
});
|
|
1031
|
+
const transferBuilder = txBuilder.transfer();
|
|
1032
|
+
if (!batcherContractAddress) {
|
|
1033
|
+
transferBuilder
|
|
1034
|
+
.coin(this.staticsCoin?.name)
|
|
1035
|
+
.amount(batchExecutionInfo.totalAmount)
|
|
1036
|
+
.contractSequenceId(sequenceId)
|
|
1037
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
1038
|
+
.to(recoveryDestination);
|
|
1039
|
+
}
|
|
1040
|
+
else {
|
|
1041
|
+
transferBuilder
|
|
1042
|
+
.coin(this.staticsCoin?.name)
|
|
1043
|
+
.amount(batchExecutionInfo.totalAmount)
|
|
1044
|
+
.contractSequenceId(sequenceId)
|
|
1045
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
1046
|
+
.to(batcherContractAddress)
|
|
1047
|
+
.data(batchData);
|
|
1048
|
+
}
|
|
1049
|
+
if (params.walletPassphrase) {
|
|
1050
|
+
transferBuilder.key(userSigningKey);
|
|
1051
|
+
}
|
|
1052
|
+
// If the intended chain is arbitrum or optimism, we need to use wallet version 4
|
|
1053
|
+
// since these contracts construct operationHash differently
|
|
1054
|
+
if (params.intendedChain && ['arbeth', 'opeth'].includes(statics_1.coins.get(params.intendedChain).family)) {
|
|
1055
|
+
txBuilder.walletVersion(4);
|
|
1056
|
+
}
|
|
1057
|
+
// If gasLimit was not passed as a param or if it is not cold/custody wallet, then fetch the gasLimit from Explorer
|
|
1058
|
+
if (!params.gasLimit && userKey && !userKey.startsWith('xpub')) {
|
|
1059
|
+
const sendData = txBuilder.getSendData();
|
|
1060
|
+
gasLimit = await this.getGasLimitFromExternalAPI(params.intendedChain, params.bitgoFeeAddress, params.walletContractAddress, sendData);
|
|
1061
|
+
txBuilder.fee({
|
|
1062
|
+
...txFee,
|
|
1063
|
+
gasLimit: gasLimit.toString(),
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
// Get the balance of bitgoFeeAddress to ensure funds are available to pay fees
|
|
1067
|
+
await this.ensureSufficientBalance(bitgoFeeAddress, gasPrice, gasLimit);
|
|
1068
|
+
const tx = await txBuilder.build();
|
|
1069
|
+
const txInfo = {
|
|
1070
|
+
recipients: recipients,
|
|
1071
|
+
expireTime: this.getDefaultExpireTime(),
|
|
1072
|
+
contractSequenceId: sequenceId,
|
|
1073
|
+
gasLimit: gasLimit.toString(10),
|
|
1074
|
+
isEvmBasedCrossChainRecovery: true,
|
|
1075
|
+
};
|
|
1076
|
+
const response = {
|
|
1077
|
+
txHex: tx.toBroadcastFormat(),
|
|
1078
|
+
userKey,
|
|
1079
|
+
coin: this.getChain(),
|
|
1080
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1081
|
+
gasLimit,
|
|
1082
|
+
recipients: txInfo.recipients,
|
|
1083
|
+
walletContractAddress: tx.toJson().to,
|
|
1084
|
+
amount: batchExecutionInfo.totalAmount,
|
|
1085
|
+
backupKeyNonce: bitgoFeeAddressNonce,
|
|
1086
|
+
eip1559: params.eip1559,
|
|
1087
|
+
...(txBuilder.getWalletVersion() === 4 ? { walletVersion: txBuilder.getWalletVersion() } : {}),
|
|
1088
|
+
};
|
|
1089
|
+
lodash_1.default.extend(response, txInfo);
|
|
1090
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
1091
|
+
if (params.walletPassphrase) {
|
|
1092
|
+
const halfSignedTxn = {
|
|
1093
|
+
halfSigned: {
|
|
1094
|
+
txHex: tx.toBroadcastFormat(),
|
|
1095
|
+
recipients: txInfo.recipients,
|
|
1096
|
+
expireTime: txInfo.expireTime,
|
|
1097
|
+
},
|
|
1098
|
+
};
|
|
1099
|
+
lodash_1.default.extend(response, halfSignedTxn);
|
|
1100
|
+
const feesUsed = {
|
|
1101
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1102
|
+
gasLimit: exports.optionalDeps.ethUtil.bufferToInt(gasLimit).toFixed(),
|
|
1103
|
+
};
|
|
1104
|
+
response['feesUsed'] = feesUsed;
|
|
1105
|
+
}
|
|
1106
|
+
return response;
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Query explorer for the balance of an address for a token
|
|
1110
|
+
* @param {string} tokenContractAddress - address where the token smart contract is hosted
|
|
1111
|
+
* @param {string} walletContractAddress - address of the wallet
|
|
1112
|
+
* @returns {BigNumber} token balaance in base units
|
|
1113
|
+
*/
|
|
1114
|
+
async queryAddressTokenBalance(tokenContractAddress, walletContractAddress) {
|
|
1115
|
+
if (!exports.optionalDeps.ethUtil.isValidAddress(tokenContractAddress)) {
|
|
1116
|
+
throw new Error('cannot get balance for invalid token address');
|
|
1117
|
+
}
|
|
1118
|
+
if (!exports.optionalDeps.ethUtil.isValidAddress(walletContractAddress)) {
|
|
1119
|
+
throw new Error('cannot get token balance for invalid wallet address');
|
|
1120
|
+
}
|
|
1121
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
1122
|
+
chainid: this.getChainId().toString(),
|
|
1123
|
+
module: 'account',
|
|
1124
|
+
action: 'tokenbalance',
|
|
1125
|
+
contractaddress: tokenContractAddress,
|
|
1126
|
+
address: walletContractAddress,
|
|
1127
|
+
tag: 'latest',
|
|
1128
|
+
});
|
|
1129
|
+
// throw if the result does not exist or the result is not a valid number
|
|
1130
|
+
if (!result || !result.result || isNaN(result.result)) {
|
|
1131
|
+
throw new Error(`Could not obtain token address balance for ${tokenContractAddress} from Etherscan, got: ${result.result}`);
|
|
1132
|
+
}
|
|
1133
|
+
return new exports.optionalDeps.ethUtil.BN(result.result, 10);
|
|
1134
|
+
}
|
|
1135
|
+
async recoverEthLikeTokenforEvmBasedRecovery(params, bitgoFeeAddressNonce, gasLimit, gasPrice, userKey, userSigningKey) {
|
|
1136
|
+
// get token balance of wallet
|
|
1137
|
+
const txAmount = await this.queryAddressTokenBalance(params.tokenContractAddress, params.walletContractAddress);
|
|
1138
|
+
// build recipients object
|
|
1139
|
+
const recipients = [
|
|
1140
|
+
{
|
|
1141
|
+
address: params.recoveryDestination,
|
|
1142
|
+
amount: new bignumber_js_1.BigNumber(txAmount).toFixed(),
|
|
1143
|
+
},
|
|
1144
|
+
];
|
|
1145
|
+
// Get sequence ID using contract call
|
|
1146
|
+
// we need to wait between making two explorer api calls to avoid getting banned
|
|
1147
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1148
|
+
const sequenceId = await this.querySequenceId(params.walletContractAddress);
|
|
1149
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
1150
|
+
txBuilder.counter(bitgoFeeAddressNonce);
|
|
1151
|
+
txBuilder.contract(params.walletContractAddress);
|
|
1152
|
+
let txFee;
|
|
1153
|
+
if (params.eip1559) {
|
|
1154
|
+
txFee = {
|
|
1155
|
+
eip1559: {
|
|
1156
|
+
maxPriorityFeePerGas: params.eip1559.maxPriorityFeePerGas,
|
|
1157
|
+
maxFeePerGas: params.eip1559.maxFeePerGas,
|
|
1158
|
+
},
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
txFee = { fee: gasPrice.toString() };
|
|
1163
|
+
}
|
|
1164
|
+
txBuilder.fee({
|
|
1165
|
+
...txFee,
|
|
1166
|
+
gasLimit: gasLimit.toString(),
|
|
1167
|
+
});
|
|
1168
|
+
const transferBuilder = txBuilder.transfer();
|
|
1169
|
+
const network = this.getNetwork();
|
|
1170
|
+
const token = (0, lib_1.getToken)(params.tokenContractAddress, network, this.staticsCoin?.family)?.name;
|
|
1171
|
+
transferBuilder
|
|
1172
|
+
.amount(txAmount)
|
|
1173
|
+
.contractSequenceId(sequenceId)
|
|
1174
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
1175
|
+
.to(params.recoveryDestination);
|
|
1176
|
+
if (token) {
|
|
1177
|
+
transferBuilder.coin(token);
|
|
1178
|
+
}
|
|
1179
|
+
else {
|
|
1180
|
+
transferBuilder
|
|
1181
|
+
.coin(this.staticsCoin?.name)
|
|
1182
|
+
.tokenContractAddress(params.tokenContractAddress);
|
|
1183
|
+
}
|
|
1184
|
+
if (params.walletPassphrase) {
|
|
1185
|
+
txBuilder.transfer().key(userSigningKey);
|
|
1186
|
+
}
|
|
1187
|
+
// If the intended chain is arbitrum or optimism, we need to use wallet version 4
|
|
1188
|
+
// since these contracts construct operationHash differently
|
|
1189
|
+
if (params.intendedChain && ['arbeth', 'opeth'].includes(statics_1.coins.get(params.intendedChain).family)) {
|
|
1190
|
+
txBuilder.walletVersion(4);
|
|
1191
|
+
}
|
|
1192
|
+
if (!params.gasLimit && userKey && !userKey.startsWith('xpub')) {
|
|
1193
|
+
const sendData = txBuilder.getSendData();
|
|
1194
|
+
gasLimit = await this.getGasLimitFromExternalAPI(params.intendedChain, params.bitgoFeeAddress, params.walletContractAddress, sendData);
|
|
1195
|
+
txBuilder.fee({
|
|
1196
|
+
...txFee,
|
|
1197
|
+
gasLimit: gasLimit.toString(),
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
// Get the balance of bitgoFeeAddress to ensure funds are available to pay fees
|
|
1201
|
+
await this.ensureSufficientBalance(params.bitgoFeeAddress, gasPrice, gasLimit);
|
|
1202
|
+
const tx = await txBuilder.build();
|
|
1203
|
+
const txInfo = {
|
|
1204
|
+
recipients: recipients,
|
|
1205
|
+
expireTime: this.getDefaultExpireTime(),
|
|
1206
|
+
contractSequenceId: sequenceId,
|
|
1207
|
+
gasLimit: gasLimit.toString(10),
|
|
1208
|
+
isEvmBasedCrossChainRecovery: true,
|
|
1209
|
+
};
|
|
1210
|
+
const response = {
|
|
1211
|
+
txHex: tx.toBroadcastFormat(),
|
|
1212
|
+
userKey,
|
|
1213
|
+
coin: token ? token : this.getChain(),
|
|
1214
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1215
|
+
gasLimit,
|
|
1216
|
+
recipients: txInfo.recipients,
|
|
1217
|
+
walletContractAddress: tx.toJson().to,
|
|
1218
|
+
amount: txAmount.toString(),
|
|
1219
|
+
backupKeyNonce: bitgoFeeAddressNonce,
|
|
1220
|
+
eip1559: params.eip1559,
|
|
1221
|
+
...(txBuilder.getWalletVersion() === 4 ? { walletVersion: txBuilder.getWalletVersion() } : {}),
|
|
1222
|
+
};
|
|
1223
|
+
lodash_1.default.extend(response, txInfo);
|
|
1224
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
1225
|
+
if (params.walletPassphrase) {
|
|
1226
|
+
const halfSignedTxn = {
|
|
1227
|
+
halfSigned: {
|
|
1228
|
+
txHex: tx.toBroadcastFormat(),
|
|
1229
|
+
recipients: txInfo.recipients,
|
|
1230
|
+
expireTime: txInfo.expireTime,
|
|
1231
|
+
},
|
|
1232
|
+
};
|
|
1233
|
+
lodash_1.default.extend(response, halfSignedTxn);
|
|
1234
|
+
const feesUsed = {
|
|
1235
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1236
|
+
gasLimit: exports.optionalDeps.ethUtil.bufferToInt(gasLimit).toFixed(),
|
|
1237
|
+
};
|
|
1238
|
+
response['feesUsed'] = feesUsed;
|
|
1239
|
+
}
|
|
1240
|
+
return response;
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Validate evm based cross chain recovery params
|
|
1244
|
+
* @param params {RecoverOptions}
|
|
1245
|
+
* @returns {void}
|
|
1246
|
+
*/
|
|
1247
|
+
validateEvmBasedRecoveryParams(params) {
|
|
1248
|
+
if (lodash_1.default.isUndefined(params.bitgoFeeAddress) || !this.isValidAddress(params.bitgoFeeAddress)) {
|
|
1249
|
+
throw new Error('invalid bitgoFeeAddress');
|
|
1250
|
+
}
|
|
1251
|
+
if (lodash_1.default.isUndefined(params.walletContractAddress) || !this.isValidAddress(params.walletContractAddress)) {
|
|
1252
|
+
throw new Error('invalid walletContractAddress');
|
|
1253
|
+
}
|
|
1254
|
+
if (lodash_1.default.isUndefined(params.recoveryDestination) || !this.isValidAddress(params.recoveryDestination)) {
|
|
1255
|
+
throw new Error('invalid recoveryDestination');
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Return types, values, and total amount in wei to send in a batch transaction, using the method signature
|
|
1260
|
+
* `distributeBatch(address[], uint256[])`
|
|
1261
|
+
* @param {Recipient[]} recipients - transaction recipients
|
|
1262
|
+
* @returns {GetBatchExecutionInfoRT} information needed to execute the batch transaction
|
|
1263
|
+
*/
|
|
1264
|
+
getBatchExecutionInfo(recipients) {
|
|
1265
|
+
const addresses = [];
|
|
1266
|
+
const amounts = [];
|
|
1267
|
+
let sum = new bignumber_js_1.BigNumber('0');
|
|
1268
|
+
lodash_1.default.forEach(recipients, ({ address, amount }) => {
|
|
1269
|
+
addresses.push(address);
|
|
1270
|
+
amounts.push(amount);
|
|
1271
|
+
sum = sum.plus(amount);
|
|
1272
|
+
});
|
|
1273
|
+
return {
|
|
1274
|
+
values: [addresses, amounts],
|
|
1275
|
+
totalAmount: sum.toFixed(),
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
/**
|
|
1279
|
+
* Build arguments to call the send method on the wallet contract
|
|
1280
|
+
* @param txInfo
|
|
1281
|
+
*/
|
|
1282
|
+
getSendMethodArgs(txInfo) {
|
|
1283
|
+
// Method signature is
|
|
1284
|
+
// sendMultiSig(address toAddress, uint value, bytes data, uint expireTime, uint sequenceId, bytes signature)
|
|
1285
|
+
return [
|
|
1286
|
+
{
|
|
1287
|
+
name: 'toAddress',
|
|
1288
|
+
type: 'address',
|
|
1289
|
+
value: txInfo.recipient.address,
|
|
1290
|
+
},
|
|
1291
|
+
{
|
|
1292
|
+
name: 'value',
|
|
1293
|
+
type: 'uint',
|
|
1294
|
+
value: txInfo.recipient.amount,
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
name: 'data',
|
|
1298
|
+
type: 'bytes',
|
|
1299
|
+
value: exports.optionalDeps.ethUtil.toBuffer(exports.optionalDeps.ethUtil.addHexPrefix(txInfo.recipient.data || '')),
|
|
1300
|
+
},
|
|
1301
|
+
{
|
|
1302
|
+
name: 'expireTime',
|
|
1303
|
+
type: 'uint',
|
|
1304
|
+
value: txInfo.expireTime,
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
name: 'sequenceId',
|
|
1308
|
+
type: 'uint',
|
|
1309
|
+
value: txInfo.contractSequenceId,
|
|
1310
|
+
},
|
|
1311
|
+
{
|
|
1312
|
+
name: 'signature',
|
|
1313
|
+
type: 'bytes',
|
|
1314
|
+
value: exports.optionalDeps.ethUtil.toBuffer(exports.optionalDeps.ethUtil.addHexPrefix(txInfo.signature)),
|
|
1315
|
+
},
|
|
1316
|
+
];
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Recovers a tx with TSS key shares
|
|
1320
|
+
* same expected arguments as recover method, but with TSS key shares
|
|
1321
|
+
*/
|
|
1322
|
+
async recoverTSS(params) {
|
|
1323
|
+
this.validateRecoveryParams(params);
|
|
1324
|
+
if (!this.staticsCoin?.features.includes(statics_1.CoinFeature.EIP1559)) {
|
|
1325
|
+
delete params.eip1559;
|
|
1326
|
+
}
|
|
1327
|
+
// Clean up whitespace from entered values
|
|
1328
|
+
const userPublicOrPrivateKeyShare = params.userKey.replace(/\s/g, '');
|
|
1329
|
+
const backupPrivateOrPublicKeyShare = params.backupKey.replace(/\s/g, '');
|
|
1330
|
+
if ((0, sdk_core_1.getIsUnsignedSweep)({
|
|
1331
|
+
userKey: userPublicOrPrivateKeyShare,
|
|
1332
|
+
backupKey: backupPrivateOrPublicKeyShare,
|
|
1333
|
+
isTss: params.isTss,
|
|
1334
|
+
})) {
|
|
1335
|
+
return this.buildUnsignedSweepTxnTSS(params);
|
|
1336
|
+
}
|
|
1337
|
+
else {
|
|
1338
|
+
const { userKeyShare, backupKeyShare, commonKeyChain } = await sdk_core_1.ECDSAUtils.getMpcV2RecoveryKeyShares(userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, params.walletPassphrase);
|
|
1339
|
+
const { gasLimit, gasPrice } = await this.getGasValues(params);
|
|
1340
|
+
const MPC = new sdk_core_1.Ecdsa();
|
|
1341
|
+
const derivedCommonKeyChain = MPC.deriveUnhardened(commonKeyChain, 'm/0');
|
|
1342
|
+
const backupKeyPair = new lib_1.KeyPair({ pub: derivedCommonKeyChain.slice(0, 66) });
|
|
1343
|
+
const baseAddress = backupKeyPair.getAddress();
|
|
1344
|
+
const unsignedTx = (await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params)).tx;
|
|
1345
|
+
const messageHash = unsignedTx.getMessageToSign(true);
|
|
1346
|
+
const signature = await sdk_core_1.ECDSAUtils.signRecoveryMpcV2(messageHash, userKeyShare, backupKeyShare, commonKeyChain);
|
|
1347
|
+
const ethCommmon = this.getEthLikeCommon(params.eip1559, params.replayProtectionOptions);
|
|
1348
|
+
const signedTx = this.getSignedTxFromSignature(ethCommmon, unsignedTx, signature);
|
|
1349
|
+
return {
|
|
1350
|
+
id: (0, ethereumjs_util_1.addHexPrefix)(signedTx.hash().toString('hex')),
|
|
1351
|
+
tx: (0, ethereumjs_util_1.addHexPrefix)(signedTx.serialize().toString('hex')),
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
async getGasValues(params) {
|
|
1356
|
+
const gasLimit = new exports.optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit));
|
|
1357
|
+
const gasPrice = params.eip1559
|
|
1358
|
+
? new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas)
|
|
1359
|
+
: new exports.optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice));
|
|
1360
|
+
return { gasLimit, gasPrice };
|
|
1361
|
+
}
|
|
1362
|
+
async buildUnsignedSweepTxnTSS(params) {
|
|
1363
|
+
const userPublicOrPrivateKeyShare = params.userKey.replace(/\s/g, '');
|
|
1364
|
+
const backupPrivateOrPublicKeyShare = params.backupKey.replace(/\s/g, '');
|
|
1365
|
+
const { gasLimit, gasPrice } = await this.getGasValues(params);
|
|
1366
|
+
const backupKeyPair = new lib_1.KeyPair({ pub: backupPrivateOrPublicKeyShare });
|
|
1367
|
+
const baseAddress = backupKeyPair.getAddress();
|
|
1368
|
+
const { txInfo, tx, nonce } = await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params);
|
|
1369
|
+
return this.formatForOfflineVaultTSS(txInfo, tx, userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, gasPrice, gasLimit, nonce, params.eip1559, params.replayProtectionOptions);
|
|
1370
|
+
}
|
|
1371
|
+
async buildUnsignedSweepTxnMPCv2(params) {
|
|
1372
|
+
const { gasLimit, gasPrice } = await this.getGasValues(params);
|
|
1373
|
+
const recoverParams = params;
|
|
1374
|
+
this.validateUnsignedSweepTSSParams(recoverParams);
|
|
1375
|
+
const derivationPath = recoverParams.derivationSeed ? (0, sdk_lib_mpc_1.getDerivationPath)(recoverParams.derivationSeed) : 'm/0';
|
|
1376
|
+
const MPC = new sdk_core_1.Ecdsa();
|
|
1377
|
+
const derivedCommonKeyChain = MPC.deriveUnhardened(recoverParams.backupKey, derivationPath);
|
|
1378
|
+
const backupKeyPair = new lib_1.KeyPair({ pub: derivedCommonKeyChain.slice(0, 66) });
|
|
1379
|
+
const baseAddress = backupKeyPair.getAddress();
|
|
1380
|
+
const { txInfo, tx, nonce } = await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params);
|
|
1381
|
+
return this.buildTxRequestForOfflineVaultMPCv2(txInfo, tx, derivationPath, nonce, gasPrice, gasLimit, params.eip1559, params.replayProtectionOptions, recoverParams.backupKey);
|
|
1382
|
+
}
|
|
1383
|
+
async createBroadcastableSweepTransaction(params) {
|
|
1384
|
+
const req = params.signatureShares;
|
|
1385
|
+
const broadcastableTransactions = [];
|
|
1386
|
+
let lastScanIndex = 0;
|
|
1387
|
+
for (let i = 0; i < req.length; i++) {
|
|
1388
|
+
const MPC = new sdk_core_1.Ecdsa();
|
|
1389
|
+
const transaction = req[i]?.txRequest?.transactions?.[0]?.unsignedTx;
|
|
1390
|
+
if (!req[i].ovc || !req[i].ovc[0].ecdsaSignature) {
|
|
1391
|
+
throw new Error('Missing signature(s)');
|
|
1392
|
+
}
|
|
1393
|
+
if (!transaction.signableHex) {
|
|
1394
|
+
throw new Error('Missing signable hex');
|
|
1395
|
+
}
|
|
1396
|
+
const signature = req[i].ovc[0].ecdsaSignature;
|
|
1397
|
+
if (!signature) {
|
|
1398
|
+
throw new Error('Signature is undefined');
|
|
1399
|
+
}
|
|
1400
|
+
const shares = signature.toString().split(':');
|
|
1401
|
+
if (shares.length !== 4) {
|
|
1402
|
+
throw new Error('Invalid signature');
|
|
1403
|
+
}
|
|
1404
|
+
const finalSignature = {
|
|
1405
|
+
recid: Number(shares[0]),
|
|
1406
|
+
r: shares[1],
|
|
1407
|
+
s: shares[2],
|
|
1408
|
+
y: shares[3],
|
|
1409
|
+
};
|
|
1410
|
+
const signatureHex = Buffer.from(signature.toString(), 'hex');
|
|
1411
|
+
const txBuilder = this.getTransactionBuilder((0, lib_1.getCommon)(this.getNetwork()));
|
|
1412
|
+
txBuilder.from(transaction.serializedTxHex);
|
|
1413
|
+
if (!transaction.coinSpecific?.commonKeyChain) {
|
|
1414
|
+
throw new Error(`Missing common keychain for transaction at index ${i}`);
|
|
1415
|
+
}
|
|
1416
|
+
const commonKeyChain = transaction.coinSpecific.commonKeyChain;
|
|
1417
|
+
if (!transaction.derivationPath) {
|
|
1418
|
+
throw new Error(`Missing derivation path for transaction at index ${i}`);
|
|
1419
|
+
}
|
|
1420
|
+
if (!commonKeyChain) {
|
|
1421
|
+
throw new Error(`Missing common key chain for transaction at index ${i}`);
|
|
1422
|
+
}
|
|
1423
|
+
const derivationPath = transaction.derivationPath ?? 'm/0';
|
|
1424
|
+
const derivedCommonKeyChain = MPC.deriveUnhardened(String(commonKeyChain), String(derivationPath));
|
|
1425
|
+
const derivedPublicKey = new lib_1.KeyPair({ pub: derivedCommonKeyChain.slice(0, 66) });
|
|
1426
|
+
txBuilder.addSignature({ pub: derivedPublicKey.getKeys().pub }, signatureHex);
|
|
1427
|
+
const ethCommmon = this.getEthLikeCommon(transaction.eip1559, transaction.replayProtectionOptions);
|
|
1428
|
+
let unsignedTx;
|
|
1429
|
+
if (transaction.eip1559) {
|
|
1430
|
+
unsignedTx = await tx_1.FeeMarketEIP1559Transaction.fromSerializedTx(Buffer.from(transaction.serializedTxHex, 'hex'));
|
|
1431
|
+
}
|
|
1432
|
+
else {
|
|
1433
|
+
unsignedTx = await tx_1.Transaction.fromSerializedTx(Buffer.from(transaction.serializedTxHex, 'hex'));
|
|
1434
|
+
}
|
|
1435
|
+
const signedTx = this.getSignedTxFromSignature(ethCommmon, unsignedTx, finalSignature);
|
|
1436
|
+
broadcastableTransactions.push({
|
|
1437
|
+
serializedTx: (0, ethereumjs_util_1.addHexPrefix)(signedTx.serialize().toString('hex')),
|
|
1438
|
+
});
|
|
1439
|
+
if (i === req.length - 1 && transaction.coinSpecific.lastScanIndex) {
|
|
1440
|
+
lastScanIndex = transaction.coinSpecific.lastScanIndex;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
return { transactions: broadcastableTransactions, lastScanIndex };
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Method to validate recovery params
|
|
1447
|
+
* @param {RecoverOptions} params
|
|
1448
|
+
* @returns {void}
|
|
1449
|
+
*/
|
|
1450
|
+
async validateUnsignedSweepTSSParams(params) {
|
|
1451
|
+
if (lodash_1.default.isUndefined(params.backupKey) && params.backupKey === '') {
|
|
1452
|
+
throw new Error('missing commonKeyChain');
|
|
1453
|
+
}
|
|
1454
|
+
if (!lodash_1.default.isUndefined(params.derivationSeed) && typeof params.derivationSeed !== 'string') {
|
|
1455
|
+
throw new Error('invalid derivationSeed');
|
|
1456
|
+
}
|
|
1457
|
+
if (lodash_1.default.isUndefined(params.bitgoDestinationAddress) ||
|
|
1458
|
+
typeof params.bitgoDestinationAddress !== 'string' ||
|
|
1459
|
+
!this.isValidAddress(params.bitgoDestinationAddress)) {
|
|
1460
|
+
throw new Error('missing or invalid destinationAddress');
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
/**
|
|
1464
|
+
* Helper function for recover()
|
|
1465
|
+
* This transforms the unsigned transaction information into a format the BitGo offline vault expects
|
|
1466
|
+
* @param {UnformattedTxInfo} txInfo - tx info
|
|
1467
|
+
* @param {EthLikeTxLib.Transaction | EthLikeTxLib.FeeMarketEIP1559Transaction} ethTx - the ethereumjs tx object
|
|
1468
|
+
* @param {string} derivationPath - the derivationPath
|
|
1469
|
+
* @param {number} nonce - the nonce of the backup key address
|
|
1470
|
+
* @param {Buffer} gasPrice - gas price for the tx
|
|
1471
|
+
* @param {number} gasLimit - gas limit for the tx
|
|
1472
|
+
* @param {EIP1559} eip1559 - eip1559 params
|
|
1473
|
+
* @returns {Promise<OfflineVaultTxInfo>}
|
|
1474
|
+
*/
|
|
1475
|
+
buildTxRequestForOfflineVaultMPCv2(txInfo, ethTx, derivationPath, nonce, gasPrice, gasLimit, eip1559, replayProtectionOptions, commonKeyChain) {
|
|
1476
|
+
if (!ethTx.to) {
|
|
1477
|
+
throw new Error('Eth tx must have a `to` address');
|
|
1478
|
+
}
|
|
1479
|
+
const fee = eip1559
|
|
1480
|
+
? gasLimit * eip1559.maxFeePerGas
|
|
1481
|
+
: gasLimit * exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed();
|
|
1482
|
+
const unsignedTx = {
|
|
1483
|
+
serializedTxHex: ethTx.serialize().toString('hex'),
|
|
1484
|
+
signableHex: ethTx.getMessageToSign(false).toString('hex'),
|
|
1485
|
+
derivationPath: derivationPath,
|
|
1486
|
+
feeInfo: {
|
|
1487
|
+
fee: fee,
|
|
1488
|
+
feeString: fee.toString(),
|
|
1489
|
+
},
|
|
1490
|
+
parsedTx: {
|
|
1491
|
+
spendAmount: txInfo.recipient.amount,
|
|
1492
|
+
outputs: [
|
|
1493
|
+
{
|
|
1494
|
+
coinName: this.getChain(),
|
|
1495
|
+
address: txInfo.recipient.address,
|
|
1496
|
+
valueString: txInfo.recipient.amount,
|
|
1497
|
+
},
|
|
1498
|
+
],
|
|
1499
|
+
},
|
|
1500
|
+
coinSpecific: {
|
|
1501
|
+
commonKeyChain: commonKeyChain,
|
|
1502
|
+
},
|
|
1503
|
+
eip1559: eip1559,
|
|
1504
|
+
replayProtectionOptions: replayProtectionOptions,
|
|
1505
|
+
};
|
|
1506
|
+
return {
|
|
1507
|
+
txRequests: [
|
|
1508
|
+
{
|
|
1509
|
+
walletCoin: this.getChain(),
|
|
1510
|
+
transactions: [
|
|
1511
|
+
{
|
|
1512
|
+
unsignedTx: unsignedTx,
|
|
1513
|
+
nonce: nonce,
|
|
1514
|
+
signatureShares: [],
|
|
1515
|
+
},
|
|
1516
|
+
],
|
|
1517
|
+
},
|
|
1518
|
+
],
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
async buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params) {
|
|
1522
|
+
const nonce = await this.getAddressNonce(baseAddress);
|
|
1523
|
+
const txAmount = await this.validateBalanceAndGetTxAmount(baseAddress, gasPrice, gasLimit);
|
|
1524
|
+
const recipients = [
|
|
1525
|
+
{
|
|
1526
|
+
address: params.recoveryDestination,
|
|
1527
|
+
amount: txAmount.toString(10),
|
|
1528
|
+
},
|
|
1529
|
+
];
|
|
1530
|
+
const txInfo = {
|
|
1531
|
+
recipient: recipients[0],
|
|
1532
|
+
expireTime: this.getDefaultExpireTime(),
|
|
1533
|
+
gasLimit: gasLimit.toString(10),
|
|
1534
|
+
};
|
|
1535
|
+
const txParams = {
|
|
1536
|
+
to: params.recoveryDestination,
|
|
1537
|
+
nonce: nonce,
|
|
1538
|
+
value: txAmount,
|
|
1539
|
+
gasPrice: gasPrice,
|
|
1540
|
+
gasLimit: gasLimit,
|
|
1541
|
+
data: Buffer.from('0x'),
|
|
1542
|
+
eip1559: params.eip1559,
|
|
1543
|
+
replayProtectionOptions: params.replayProtectionOptions,
|
|
1544
|
+
};
|
|
1545
|
+
const tx = this.buildTransaction(txParams);
|
|
1546
|
+
return { txInfo, tx, nonce };
|
|
1547
|
+
}
|
|
1548
|
+
async validateBalanceAndGetTxAmount(baseAddress, gasPrice, gasLimit) {
|
|
1549
|
+
const baseAddressBalance = await this.queryAddressBalance(baseAddress);
|
|
1550
|
+
const totalGasNeeded = gasPrice.mul(gasLimit);
|
|
1551
|
+
const weiToGwei = new bn_js_1.default(10 ** 9);
|
|
1552
|
+
if (baseAddressBalance.lt(totalGasNeeded)) {
|
|
1553
|
+
throw new Error(`Backup key address ${baseAddress} has balance ${baseAddressBalance.div(weiToGwei).toString()} Gwei.` +
|
|
1554
|
+
`This address must have a balance of at least ${totalGasNeeded.div(weiToGwei).toString()}` +
|
|
1555
|
+
` Gwei to perform recoveries. Try sending some ETH to this address then retry.`);
|
|
1556
|
+
}
|
|
1557
|
+
const txAmount = baseAddressBalance.sub(totalGasNeeded);
|
|
1558
|
+
return txAmount;
|
|
1559
|
+
}
|
|
1560
|
+
async recoveryBlockchainExplorerQuery(query) {
|
|
1561
|
+
throw new Error('method not implemented');
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Creates the extra parameters needed to build a hop transaction
|
|
1565
|
+
* @param buildParams The original build parameters
|
|
1566
|
+
* @returns extra parameters object to merge with the original build parameters object and send to the platform
|
|
1567
|
+
*/
|
|
1568
|
+
async createHopTransactionParams(buildParams) {
|
|
1569
|
+
const wallet = buildParams.wallet;
|
|
1570
|
+
const recipients = buildParams.recipients;
|
|
1571
|
+
const walletPassphrase = buildParams.walletPassphrase;
|
|
1572
|
+
const userKeychain = await this.keychains().get({ id: wallet.keyIds()[0] });
|
|
1573
|
+
const userPrv = wallet.getUserPrv({ keychain: userKeychain, walletPassphrase });
|
|
1574
|
+
const userPrvBuffer = secp256k1_1.bip32.fromBase58(userPrv).privateKey;
|
|
1575
|
+
if (!userPrvBuffer) {
|
|
1576
|
+
throw new Error('invalid userPrv');
|
|
1577
|
+
}
|
|
1578
|
+
if (!recipients || !Array.isArray(recipients)) {
|
|
1579
|
+
throw new Error('expecting array of recipients');
|
|
1580
|
+
}
|
|
1581
|
+
// Right now we only support 1 recipient
|
|
1582
|
+
if (recipients.length !== 1) {
|
|
1583
|
+
throw new Error('must send to exactly 1 recipient');
|
|
1584
|
+
}
|
|
1585
|
+
const recipientAddress = recipients[0].address;
|
|
1586
|
+
const recipientAmount = recipients[0].amount;
|
|
1587
|
+
const feeEstimateParams = {
|
|
1588
|
+
recipient: recipientAddress,
|
|
1589
|
+
amount: recipientAmount,
|
|
1590
|
+
hop: true,
|
|
1591
|
+
};
|
|
1592
|
+
const feeEstimate = await this.feeEstimate(feeEstimateParams);
|
|
1593
|
+
const gasLimit = feeEstimate.gasLimitEstimate;
|
|
1594
|
+
const gasPrice = Math.round(feeEstimate.feeEstimate / gasLimit);
|
|
1595
|
+
const gasPriceMax = gasPrice * 5;
|
|
1596
|
+
// Payment id a random number so its different for every tx
|
|
1597
|
+
const paymentId = Math.floor(Math.random() * 10000000000).toString();
|
|
1598
|
+
const hopDigest = AbstractEthLikeNewCoins.getHopDigest([
|
|
1599
|
+
recipientAddress,
|
|
1600
|
+
recipientAmount,
|
|
1601
|
+
gasPriceMax.toString(),
|
|
1602
|
+
gasLimit.toString(),
|
|
1603
|
+
paymentId,
|
|
1604
|
+
]);
|
|
1605
|
+
const userReqSig = exports.optionalDeps.ethUtil.addHexPrefix(Buffer.from(secp256k1_2.default.ecdsaSign(hopDigest, userPrvBuffer).signature).toString('hex'));
|
|
1606
|
+
return {
|
|
1607
|
+
hopParams: {
|
|
1608
|
+
gasPriceMax,
|
|
1609
|
+
userReqSig,
|
|
1610
|
+
paymentId,
|
|
1611
|
+
gasLimit,
|
|
1612
|
+
},
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Validates that the hop prebuild from the HSM is valid and correct
|
|
1617
|
+
* @param {IWallet} wallet - The wallet that the prebuild is for
|
|
1618
|
+
* @param {HopPrebuild} hopPrebuild - The prebuild to validate
|
|
1619
|
+
* @param {Object} originalParams - The original parameters passed to prebuildTransaction
|
|
1620
|
+
* @param {Recipient[]} originalParams.recipients - The original recipients array
|
|
1621
|
+
* @returns {void}
|
|
1622
|
+
* @throws Error if The prebuild is invalid
|
|
1623
|
+
*/
|
|
1624
|
+
async validateHopPrebuild(wallet, hopPrebuild, originalParams) {
|
|
1625
|
+
const { tx, id, signature } = hopPrebuild;
|
|
1626
|
+
// first, validate the HSM signature
|
|
1627
|
+
const serverXpub = sdk_core_1.common.Environments[this.bitgo.getEnv()].hsmXpub;
|
|
1628
|
+
const serverPubkeyBuffer = secp256k1_1.bip32.fromBase58(serverXpub).publicKey;
|
|
1629
|
+
const signatureBuffer = Buffer.from(exports.optionalDeps.ethUtil.stripHexPrefix(signature), 'hex');
|
|
1630
|
+
const messageBuffer = Buffer.from(exports.optionalDeps.ethUtil.padToEven(exports.optionalDeps.ethUtil.stripHexPrefix(id)), 'hex');
|
|
1631
|
+
const sig = new Uint8Array(signatureBuffer.slice(1));
|
|
1632
|
+
const isValidSignature = secp256k1_2.default.ecdsaVerify(sig, messageBuffer, serverPubkeyBuffer);
|
|
1633
|
+
if (!isValidSignature) {
|
|
1634
|
+
throw new Error(`Hop txid signature invalid - pub: ${serverXpub}, msg: ${messageBuffer?.toString()}, sig: ${signatureBuffer?.toString()}`);
|
|
1635
|
+
}
|
|
1636
|
+
const builtHopTx = exports.optionalDeps.EthTx.TransactionFactory.fromSerializedData(exports.optionalDeps.ethUtil.toBuffer(tx));
|
|
1637
|
+
// If original params are given, we can check them against the transaction prebuild params
|
|
1638
|
+
if (!lodash_1.default.isNil(originalParams)) {
|
|
1639
|
+
const { recipients } = originalParams;
|
|
1640
|
+
// Then validate that the tx params actually equal the requested params
|
|
1641
|
+
const originalAmount = new bignumber_js_1.BigNumber(recipients[0].amount);
|
|
1642
|
+
const originalDestination = recipients[0].address;
|
|
1643
|
+
const hopAmount = new bignumber_js_1.BigNumber(exports.optionalDeps.ethUtil.bufferToHex(builtHopTx.value));
|
|
1644
|
+
if (!builtHopTx.to) {
|
|
1645
|
+
throw new Error(`Transaction does not have a destination address`);
|
|
1646
|
+
}
|
|
1647
|
+
const hopDestination = builtHopTx.to.toString();
|
|
1648
|
+
if (!hopAmount.eq(originalAmount)) {
|
|
1649
|
+
throw new Error(`Hop amount: ${hopAmount} does not equal original amount: ${originalAmount}`);
|
|
1650
|
+
}
|
|
1651
|
+
if (hopDestination.toLowerCase() !== originalDestination.toLowerCase()) {
|
|
1652
|
+
throw new Error(`Hop destination: ${hopDestination} does not equal original recipient: ${hopDestination}`);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
if (!builtHopTx.verifySignature()) {
|
|
1656
|
+
// We dont want to continue at all in this case, at risk of ETH being stuck on the hop address
|
|
1657
|
+
throw new Error(`Invalid hop transaction signature, txid: ${id}`);
|
|
1658
|
+
}
|
|
1659
|
+
if (exports.optionalDeps.ethUtil.addHexPrefix(builtHopTx.hash().toString('hex')) !== id) {
|
|
1660
|
+
throw new Error(`Signed hop txid does not equal actual txid`);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
/**
|
|
1664
|
+
* Gets the hop digest for the user to sign. This is validated in the HSM to prove that the user requested this tx
|
|
1665
|
+
* @param {string[]} paramsArr - The parameters to hash together for the digest
|
|
1666
|
+
* @returns {Buffer}
|
|
1667
|
+
*/
|
|
1668
|
+
static getHopDigest(paramsArr) {
|
|
1669
|
+
const hash = (0, keccak_1.default)('keccak256');
|
|
1670
|
+
hash.update([AbstractEthLikeNewCoins.hopTransactionSalt, ...paramsArr].join('$'));
|
|
1671
|
+
return hash.digest();
|
|
1672
|
+
}
|
|
1673
|
+
/**
|
|
1674
|
+
* Modify prebuild before sending it to the server. Add things like hop transaction params
|
|
1675
|
+
* @param {BuildOptions} buildParams - The whitelisted parameters for this prebuild
|
|
1676
|
+
* @param {boolean} buildParams.hop - True if this should prebuild a hop tx, else false
|
|
1677
|
+
* @param {Recipient[]} buildParams.recipients - The recipients array of this transaction
|
|
1678
|
+
* @param {Wallet} buildParams.wallet - The wallet sending this tx
|
|
1679
|
+
* @param {string} buildParams.walletPassphrase - the passphrase for this wallet
|
|
1680
|
+
* @returns {Promise<BuildOptions>}
|
|
1681
|
+
*/
|
|
1682
|
+
async getExtraPrebuildParams(buildParams) {
|
|
1683
|
+
if (!lodash_1.default.isUndefined(buildParams.hop) &&
|
|
1684
|
+
buildParams.hop &&
|
|
1685
|
+
!lodash_1.default.isUndefined(buildParams.wallet) &&
|
|
1686
|
+
!lodash_1.default.isUndefined(buildParams.recipients) &&
|
|
1687
|
+
!lodash_1.default.isUndefined(buildParams.walletPassphrase)) {
|
|
1688
|
+
if (this instanceof ethLikeToken_1.EthLikeToken) {
|
|
1689
|
+
throw new Error(`Hop transactions are not enabled for ERC-20 tokens, nor are they necessary. Please remove the 'hop' parameter and try again.`);
|
|
1690
|
+
}
|
|
1691
|
+
return (await this.createHopTransactionParams({
|
|
1692
|
+
wallet: buildParams.wallet,
|
|
1693
|
+
recipients: buildParams.recipients,
|
|
1694
|
+
walletPassphrase: buildParams.walletPassphrase,
|
|
1695
|
+
}));
|
|
1696
|
+
}
|
|
1697
|
+
return {};
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Modify prebuild after receiving it from the server. Add things like nlocktime
|
|
1701
|
+
* @param {TransactionPrebuild} params - The prebuild to modify
|
|
1702
|
+
* @returns {TransactionPrebuild} The modified prebuild
|
|
1703
|
+
*/
|
|
1704
|
+
async postProcessPrebuild(params) {
|
|
1705
|
+
if (!lodash_1.default.isUndefined(params.hopTransaction) && !lodash_1.default.isUndefined(params.wallet) && !lodash_1.default.isUndefined(params.buildParams)) {
|
|
1706
|
+
await this.validateHopPrebuild(params.wallet, params.hopTransaction, params.buildParams);
|
|
1707
|
+
}
|
|
1708
|
+
return params;
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Coin-specific things done before signing a transaction, i.e. verification
|
|
1712
|
+
* @param {PresignTransactionOptions} params
|
|
1713
|
+
* @returns {Promise<PresignTransactionOptions>}
|
|
1714
|
+
*/
|
|
1715
|
+
async presignTransaction(params) {
|
|
1716
|
+
if (!lodash_1.default.isUndefined(params.hopTransaction) && !lodash_1.default.isUndefined(params.wallet) && !lodash_1.default.isUndefined(params.buildParams)) {
|
|
1717
|
+
await this.validateHopPrebuild(params.wallet, params.hopTransaction);
|
|
1718
|
+
}
|
|
1719
|
+
return params;
|
|
1720
|
+
}
|
|
1721
|
+
/**
|
|
1722
|
+
* Fetch fee estimate information from the server
|
|
1723
|
+
* @param {Object} params - The params passed into the function
|
|
1724
|
+
* @param {boolean} [params.hop] - True if we should estimate fee for a hop transaction
|
|
1725
|
+
* @param {string} [params.recipient] - The recipient of the transaction to estimate a send to
|
|
1726
|
+
* @param {string} [params.data] - The ETH tx data to estimate a send for
|
|
1727
|
+
* @returns {Object} The fee info returned from the server
|
|
1728
|
+
*/
|
|
1729
|
+
async feeEstimate(params) {
|
|
1730
|
+
const query = {};
|
|
1731
|
+
if (params && params.hop) {
|
|
1732
|
+
query.hop = params.hop;
|
|
1733
|
+
}
|
|
1734
|
+
if (params && params.recipient) {
|
|
1735
|
+
query.recipient = params.recipient;
|
|
1736
|
+
}
|
|
1737
|
+
if (params && params.data) {
|
|
1738
|
+
query.data = params.data;
|
|
1739
|
+
}
|
|
1740
|
+
if (params && params.amount) {
|
|
1741
|
+
query.amount = params.amount;
|
|
1742
|
+
}
|
|
1743
|
+
return await this.bitgo.get(this.url('/tx/fee')).query(query).result();
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Generate secp256k1 key pair
|
|
1747
|
+
*
|
|
1748
|
+
* @param {Buffer} seed
|
|
1749
|
+
* @returns {KeyPair} object with generated pub and prv
|
|
1750
|
+
*/
|
|
1751
|
+
generateKeyPair(seed) {
|
|
1752
|
+
if (!seed) {
|
|
1753
|
+
// An extended private key has both a normal 256 bit private key and a 256
|
|
1754
|
+
// bit chain code, both of which must be random. 512 bits is therefore the
|
|
1755
|
+
// maximum entropy and gives us maximum security against cracking.
|
|
1756
|
+
seed = (0, crypto_1.randomBytes)(512 / 8);
|
|
1757
|
+
}
|
|
1758
|
+
const extendedKey = secp256k1_1.bip32.fromSeed(seed);
|
|
1759
|
+
const xpub = extendedKey.neutered().toBase58();
|
|
1760
|
+
return {
|
|
1761
|
+
pub: xpub,
|
|
1762
|
+
prv: extendedKey.toBase58(),
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
async parseTransaction(params) {
|
|
1766
|
+
return {};
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Make sure an address is a wallet address and throw an error if it's not.
|
|
1770
|
+
* @param {Object} params
|
|
1771
|
+
* @param {string} params.address - The derived address string on the network
|
|
1772
|
+
* @param {Object} params.coinSpecific - Coin-specific details for the address such as a forwarderVersion
|
|
1773
|
+
* @param {string} params.baseAddress - The base address of the wallet on the network
|
|
1774
|
+
* @throws {InvalidAddressError}
|
|
1775
|
+
* @throws {InvalidAddressVerificationObjectPropertyError}
|
|
1776
|
+
* @throws {UnexpectedAddressError}
|
|
1777
|
+
* @returns {boolean} True iff address is a wallet address
|
|
1778
|
+
*/
|
|
1779
|
+
async isWalletAddress(params) {
|
|
1780
|
+
const ethUtil = exports.optionalDeps.ethUtil;
|
|
1781
|
+
let expectedAddress;
|
|
1782
|
+
let actualAddress;
|
|
1783
|
+
const { address, coinSpecific, baseAddress, impliedForwarderVersion = coinSpecific?.forwarderVersion } = params;
|
|
1784
|
+
if (address && !this.isValidAddress(address)) {
|
|
1785
|
+
throw new sdk_core_1.InvalidAddressError(`invalid address: ${address}`);
|
|
1786
|
+
}
|
|
1787
|
+
// base address is required to calculate the salt which is used in calculateForwarderV1Address method
|
|
1788
|
+
if (lodash_1.default.isUndefined(baseAddress) || !this.isValidAddress(baseAddress)) {
|
|
1789
|
+
throw new sdk_core_1.InvalidAddressError('invalid base address');
|
|
1790
|
+
}
|
|
1791
|
+
if (!lodash_1.default.isObject(coinSpecific)) {
|
|
1792
|
+
throw new sdk_core_1.InvalidAddressVerificationObjectPropertyError('address validation failure: coinSpecific field must be an object');
|
|
1793
|
+
}
|
|
1794
|
+
if (impliedForwarderVersion === 0 || impliedForwarderVersion === 3 || impliedForwarderVersion === 5) {
|
|
1795
|
+
return true;
|
|
1796
|
+
}
|
|
1797
|
+
else {
|
|
1798
|
+
const ethNetwork = this.getNetwork();
|
|
1799
|
+
const forwarderFactoryAddress = ethNetwork?.forwarderFactoryAddress;
|
|
1800
|
+
const forwarderImplementationAddress = ethNetwork?.forwarderImplementationAddress;
|
|
1801
|
+
const initcode = (0, lib_1.getProxyInitcode)(forwarderImplementationAddress);
|
|
1802
|
+
const saltBuffer = ethUtil.setLengthLeft(Buffer.from(ethUtil.padToEven(ethUtil.stripHexPrefix(coinSpecific.salt || '')), 'hex'), 32);
|
|
1803
|
+
// Hash the wallet base address with the given salt, so the address directly relies on the base address
|
|
1804
|
+
const calculationSalt = exports.optionalDeps.ethUtil.bufferToHex(exports.optionalDeps.ethAbi.soliditySHA3(['address', 'bytes32'], [baseAddress, saltBuffer]));
|
|
1805
|
+
expectedAddress = (0, lib_1.calculateForwarderV1Address)(forwarderFactoryAddress, calculationSalt, initcode);
|
|
1806
|
+
actualAddress = address;
|
|
1807
|
+
}
|
|
1808
|
+
if (expectedAddress !== actualAddress) {
|
|
1809
|
+
throw new sdk_core_1.UnexpectedAddressError(`address validation failure: expected ${expectedAddress} but got ${address}`);
|
|
1810
|
+
}
|
|
1811
|
+
return true;
|
|
1812
|
+
}
|
|
1813
|
+
/**
|
|
1814
|
+
*
|
|
1815
|
+
* @param {TransactionPrebuild} txPrebuild
|
|
1816
|
+
* @returns {boolean}
|
|
1817
|
+
*/
|
|
1818
|
+
verifyCoin(txPrebuild) {
|
|
1819
|
+
const nativeCoin = this.getChain().split(':')[0];
|
|
1820
|
+
return txPrebuild.coin === nativeCoin;
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* Verify if a tss transaction is valid
|
|
1824
|
+
*
|
|
1825
|
+
* @param {VerifyEthTransactionOptions} params
|
|
1826
|
+
* @param {TransactionParams} params.txParams - params object passed to send
|
|
1827
|
+
* @param {TransactionPrebuild} params.txPrebuild - prebuild object returned by server
|
|
1828
|
+
* @param {Wallet} params.wallet - Wallet object to obtain keys to verify against
|
|
1829
|
+
* @returns {boolean}
|
|
1830
|
+
*/
|
|
1831
|
+
async verifyTssTransaction(params) {
|
|
1832
|
+
const { txParams, txPrebuild, wallet } = params;
|
|
1833
|
+
if (!txParams?.recipients &&
|
|
1834
|
+
!(txParams.prebuildTx?.consolidateId ||
|
|
1835
|
+
(txParams.type && ['acceleration', 'fillNonce', 'transferToken', 'tokenApproval'].includes(txParams.type)))) {
|
|
1836
|
+
throw new Error(`missing txParams`);
|
|
1837
|
+
}
|
|
1838
|
+
if (!wallet || !txPrebuild) {
|
|
1839
|
+
throw new Error(`missing params`);
|
|
1840
|
+
}
|
|
1841
|
+
if (txParams.hop && txParams.recipients && txParams.recipients.length > 1) {
|
|
1842
|
+
throw new Error(`tx cannot be both a batch and hop transaction`);
|
|
1843
|
+
}
|
|
1844
|
+
if (txParams.type && ['transfer'].includes(txParams.type)) {
|
|
1845
|
+
if (txParams.recipients && txParams.recipients.length === 1) {
|
|
1846
|
+
const recipients = txParams.recipients;
|
|
1847
|
+
const expectedAmount = recipients[0].amount.toString();
|
|
1848
|
+
const expectedDestination = recipients[0].address;
|
|
1849
|
+
const txBuilder = this.getTransactionBuilder();
|
|
1850
|
+
txBuilder.from(txPrebuild.txHex);
|
|
1851
|
+
const tx = await txBuilder.build();
|
|
1852
|
+
const txJson = tx.toJson();
|
|
1853
|
+
if (txJson.data === '0x') {
|
|
1854
|
+
if (expectedAmount !== txJson.value) {
|
|
1855
|
+
throw new Error('the transaction amount in txPrebuild does not match the value given by client');
|
|
1856
|
+
}
|
|
1857
|
+
if (expectedDestination.toLowerCase() !== txJson.to.toLowerCase()) {
|
|
1858
|
+
throw new Error('destination address does not match with the recipient address');
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
else if (txJson.data.startsWith('0xa9059cbb')) {
|
|
1862
|
+
const [recipientAddress, amount] = (0, lib_1.getRawDecoded)(['address', 'uint256'], (0, lib_1.getBufferedByteCode)('0xa9059cbb', txJson.data));
|
|
1863
|
+
if (expectedAmount !== amount.toString()) {
|
|
1864
|
+
throw new Error('the transaction amount in txPrebuild does not match the value given by client');
|
|
1865
|
+
}
|
|
1866
|
+
if (expectedDestination.toLowerCase() !== (0, ethereumjs_util_1.addHexPrefix)(recipientAddress.toString()).toLowerCase()) {
|
|
1867
|
+
throw new Error('destination address does not match with the recipient address');
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
return true;
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Verify that a transaction prebuild complies with the original intention
|
|
1876
|
+
*
|
|
1877
|
+
* @param {VerifyEthTransactionOptions} params
|
|
1878
|
+
* @param {TransactionParams} params.txParams - params object passed to send
|
|
1879
|
+
* @param {TransactionPrebuild} params.txPrebuild - prebuild object returned by server
|
|
1880
|
+
* @param {Wallet} params.wallet - Wallet object to obtain keys to verify against
|
|
1881
|
+
* @returns {boolean}
|
|
1882
|
+
*/
|
|
1883
|
+
async verifyTransaction(params) {
|
|
1884
|
+
const ethNetwork = this.getNetwork();
|
|
1885
|
+
const { txParams, txPrebuild, wallet, walletType } = params;
|
|
1886
|
+
if (walletType === 'tss') {
|
|
1887
|
+
return this.verifyTssTransaction(params);
|
|
1888
|
+
}
|
|
1889
|
+
if (!txParams?.recipients || !txPrebuild?.recipients || !wallet) {
|
|
1890
|
+
throw new Error(`missing params`);
|
|
1891
|
+
}
|
|
1892
|
+
if (txParams.hop && txParams.recipients.length > 1) {
|
|
1893
|
+
throw new Error(`tx cannot be both a batch and hop transaction`);
|
|
1894
|
+
}
|
|
1895
|
+
if (txPrebuild.recipients.length > 1) {
|
|
1896
|
+
throw new Error(`${this.getChain()} doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`);
|
|
1897
|
+
}
|
|
1898
|
+
if (txParams.hop && txPrebuild.hopTransaction) {
|
|
1899
|
+
// Check recipient amount for hop transaction
|
|
1900
|
+
if (txParams.recipients.length !== 1) {
|
|
1901
|
+
throw new Error(`hop transaction only supports 1 recipient but ${txParams.recipients.length} found`);
|
|
1902
|
+
}
|
|
1903
|
+
// Check tx sends to hop address
|
|
1904
|
+
const decodedHopTx = exports.optionalDeps.EthTx.TransactionFactory.fromSerializedData(exports.optionalDeps.ethUtil.toBuffer(txPrebuild.hopTransaction.tx));
|
|
1905
|
+
const expectedHopAddress = exports.optionalDeps.ethUtil.stripHexPrefix(decodedHopTx.getSenderAddress().toString());
|
|
1906
|
+
const actualHopAddress = exports.optionalDeps.ethUtil.stripHexPrefix(txPrebuild.recipients[0].address);
|
|
1907
|
+
if (expectedHopAddress.toLowerCase() !== actualHopAddress.toLowerCase()) {
|
|
1908
|
+
throw new Error('recipient address of txPrebuild does not match hop address');
|
|
1909
|
+
}
|
|
1910
|
+
// Convert TransactionRecipient array to Recipient array
|
|
1911
|
+
const recipients = txParams.recipients.map((r) => {
|
|
1912
|
+
return {
|
|
1913
|
+
address: r.address,
|
|
1914
|
+
amount: typeof r.amount === 'number' ? r.amount.toString() : r.amount,
|
|
1915
|
+
};
|
|
1916
|
+
});
|
|
1917
|
+
// Check destination address and amount
|
|
1918
|
+
await this.validateHopPrebuild(wallet, txPrebuild.hopTransaction, { recipients });
|
|
1919
|
+
}
|
|
1920
|
+
else if (txParams.recipients.length > 1) {
|
|
1921
|
+
// Check total amount for batch transaction
|
|
1922
|
+
if (txParams.tokenName) {
|
|
1923
|
+
const expectedTotalAmount = new bignumber_js_1.BigNumber(0);
|
|
1924
|
+
if (!expectedTotalAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
|
|
1925
|
+
throw new Error('batch token transaction amount in txPrebuild should be zero for token transfers');
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
else {
|
|
1929
|
+
let expectedTotalAmount = new bignumber_js_1.BigNumber(0);
|
|
1930
|
+
for (let i = 0; i < txParams.recipients.length; i++) {
|
|
1931
|
+
expectedTotalAmount = expectedTotalAmount.plus(txParams.recipients[i].amount);
|
|
1932
|
+
}
|
|
1933
|
+
if (!expectedTotalAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
|
|
1934
|
+
throw new Error('batch transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client');
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
// Check batch transaction is sent to the batcher contract address for the chain
|
|
1938
|
+
const batcherContractAddress = ethNetwork?.batcherContractAddress;
|
|
1939
|
+
if (!batcherContractAddress ||
|
|
1940
|
+
batcherContractAddress.toLowerCase() !== txPrebuild.recipients[0].address.toLowerCase()) {
|
|
1941
|
+
throw new Error('recipient address of txPrebuild does not match batcher address');
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
else {
|
|
1945
|
+
// Check recipient address and amount for normal transaction
|
|
1946
|
+
if (txParams.recipients.length !== 1) {
|
|
1947
|
+
throw new Error(`normal transaction only supports 1 recipient but ${txParams.recipients.length} found`);
|
|
1948
|
+
}
|
|
1949
|
+
const expectedAmount = new bignumber_js_1.BigNumber(txParams.recipients[0].amount);
|
|
1950
|
+
if (!expectedAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
|
|
1951
|
+
throw new Error('normal transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client');
|
|
1952
|
+
}
|
|
1953
|
+
if (this.isETHAddress(txParams.recipients[0].address) &&
|
|
1954
|
+
txParams.recipients[0].address !== txPrebuild.recipients[0].address) {
|
|
1955
|
+
throw new Error('destination address in normal txPrebuild does not match that in txParams supplied by client');
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
// Check coin is correct for all transaction types
|
|
1959
|
+
if (!this.verifyCoin(txPrebuild)) {
|
|
1960
|
+
throw new Error(`coin in txPrebuild did not match that in txParams supplied by client`);
|
|
1961
|
+
}
|
|
1962
|
+
return true;
|
|
1963
|
+
}
|
|
1964
|
+
/**
|
|
1965
|
+
* Check if address is valid eth address
|
|
1966
|
+
* @param address
|
|
1967
|
+
* @returns {boolean}
|
|
1968
|
+
*/
|
|
1969
|
+
isETHAddress(address) {
|
|
1970
|
+
return !!address.match(/0x[a-fA-F0-9]{40}/);
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* Transform message to accommodate specific blockchain requirements.
|
|
1974
|
+
* @param {string} message - the message to prepare
|
|
1975
|
+
* @return {string} the prepared message as a hex encoded string.
|
|
1976
|
+
*/
|
|
1977
|
+
encodeMessage(message) {
|
|
1978
|
+
const prefix = `\u0019Ethereum Signed Message:\n${message.length}`;
|
|
1979
|
+
return Buffer.from(prefix.concat(message)).toString('hex');
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Transform the Typed data to accomodate the blockchain requirements (EIP-712)
|
|
1983
|
+
* @param {TypedData} typedData - the typed data to prepare
|
|
1984
|
+
* @return {Buffer} a buffer of the result
|
|
1985
|
+
*/
|
|
1986
|
+
encodeTypedData(typedData) {
|
|
1987
|
+
const version = typedData.version;
|
|
1988
|
+
if (version === eth_sig_util_1.SignTypedDataVersion.V1) {
|
|
1989
|
+
throw new Error('SignTypedData v1 is not supported due to security concerns');
|
|
1990
|
+
}
|
|
1991
|
+
const typedDataRaw = JSON.parse(typedData.typedDataRaw);
|
|
1992
|
+
const sanitizedData = eth_sig_util_1.TypedDataUtils.sanitizeData(typedDataRaw);
|
|
1993
|
+
const parts = [Buffer.from('1901', 'hex')];
|
|
1994
|
+
const eip712Domain = 'EIP712Domain';
|
|
1995
|
+
parts.push(eth_sig_util_1.TypedDataUtils.hashStruct(eip712Domain, sanitizedData.domain, sanitizedData.types, version));
|
|
1996
|
+
if (sanitizedData.primaryType !== eip712Domain) {
|
|
1997
|
+
parts.push(eth_sig_util_1.TypedDataUtils.hashStruct(sanitizedData.primaryType, sanitizedData.message, sanitizedData.types, version));
|
|
1998
|
+
}
|
|
1999
|
+
return Buffer.concat(parts);
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Build the data to transfer an ERC-721 or ERC-1155 token to another address
|
|
2003
|
+
* @param params
|
|
2004
|
+
*/
|
|
2005
|
+
buildNftTransferData(params) {
|
|
2006
|
+
const { tokenContractAddress, recipientAddress, fromAddress } = params;
|
|
2007
|
+
switch (params.type) {
|
|
2008
|
+
case 'ERC721': {
|
|
2009
|
+
const tokenId = params.tokenId;
|
|
2010
|
+
const contractData = new lib_1.ERC721TransferBuilder()
|
|
2011
|
+
.tokenContractAddress(tokenContractAddress)
|
|
2012
|
+
.to(recipientAddress)
|
|
2013
|
+
.from(fromAddress)
|
|
2014
|
+
.tokenId(tokenId)
|
|
2015
|
+
.build();
|
|
2016
|
+
return contractData;
|
|
2017
|
+
}
|
|
2018
|
+
case 'ERC1155': {
|
|
2019
|
+
const entries = params.entries;
|
|
2020
|
+
const transferBuilder = new lib_1.ERC1155TransferBuilder()
|
|
2021
|
+
.tokenContractAddress(tokenContractAddress)
|
|
2022
|
+
.to(recipientAddress)
|
|
2023
|
+
.from(fromAddress);
|
|
2024
|
+
for (const entry of entries) {
|
|
2025
|
+
transferBuilder.entry(parseInt(entry.tokenId, 10), entry.amount);
|
|
2026
|
+
}
|
|
2027
|
+
return transferBuilder.build();
|
|
2028
|
+
}
|
|
2029
|
+
default:
|
|
2030
|
+
throw new Error(`Unsupported NFT type: ${params.type}`);
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Fetch the gas price from the explorer
|
|
2035
|
+
*/
|
|
2036
|
+
async getGasPriceFromExternalAPI(wrongChainCoin) {
|
|
2037
|
+
try {
|
|
2038
|
+
const res = await this.recoveryBlockchainExplorerQuery({
|
|
2039
|
+
chainid: this.getChainId().toString(),
|
|
2040
|
+
module: 'proxy',
|
|
2041
|
+
action: 'eth_gasPrice',
|
|
2042
|
+
});
|
|
2043
|
+
const gasPrice = new bn_js_1.default(res.result.slice(2), 16);
|
|
2044
|
+
console.log(` Got gas price: ${gasPrice}`);
|
|
2045
|
+
return gasPrice;
|
|
2046
|
+
}
|
|
2047
|
+
catch (e) {
|
|
2048
|
+
throw new Error(`Failed to get gas price. Please make sure to use the api key of ${wrongChainCoin}`);
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
/**
|
|
2052
|
+
* Fetch the gas limit from the explorer
|
|
2053
|
+
* @param intendedChain
|
|
2054
|
+
* @param from
|
|
2055
|
+
* @param to
|
|
2056
|
+
* @param data
|
|
2057
|
+
*/
|
|
2058
|
+
async getGasLimitFromExternalAPI(intendedChain, from, to, data) {
|
|
2059
|
+
try {
|
|
2060
|
+
const res = await this.recoveryBlockchainExplorerQuery({
|
|
2061
|
+
chainid: this.getChainId().toString(),
|
|
2062
|
+
module: 'proxy',
|
|
2063
|
+
action: 'eth_estimateGas',
|
|
2064
|
+
from,
|
|
2065
|
+
to,
|
|
2066
|
+
data,
|
|
2067
|
+
});
|
|
2068
|
+
const gasLimit = new bn_js_1.default(res.result.slice(2), 16);
|
|
2069
|
+
console.log(`Got gas limit: ${gasLimit}`);
|
|
2070
|
+
return gasLimit;
|
|
2071
|
+
}
|
|
2072
|
+
catch (e) {
|
|
2073
|
+
throw new Error(`Failed to get gas limit. Please make sure to use the privateKey aka userKey of ${intendedChain} wallet ${to}`);
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Get the balance of bitgoFeeAddress to ensure funds are available to pay fees
|
|
2078
|
+
* @param bitgoFeeAddress
|
|
2079
|
+
* @param gasPrice
|
|
2080
|
+
* @param gasLimit
|
|
2081
|
+
*/
|
|
2082
|
+
async ensureSufficientBalance(bitgoFeeAddress, gasPrice, gasLimit) {
|
|
2083
|
+
const bitgoFeeAddressBalance = await this.queryAddressBalance(bitgoFeeAddress);
|
|
2084
|
+
const totalGasNeeded = Number(gasPrice.mul(gasLimit));
|
|
2085
|
+
const weiToGwei = 10 ** 9;
|
|
2086
|
+
if (bitgoFeeAddressBalance.lt(totalGasNeeded)) {
|
|
2087
|
+
throw new Error(`Fee address ${bitgoFeeAddress} has balance ${(bitgoFeeAddressBalance / weiToGwei).toString()} Gwei.` +
|
|
2088
|
+
`This address must have a balance of at least ${(totalGasNeeded / weiToGwei).toString()}` +
|
|
2089
|
+
` Gwei to perform recoveries. Try sending some ${this.getChain()} to this address then retry.`);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
exports.AbstractEthLikeNewCoins = AbstractEthLikeNewCoins;
|
|
2094
|
+
AbstractEthLikeNewCoins.hopTransactionSalt = 'bitgoHopAddressRequestSalt';
|
|
2095
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUE7O0dBRUc7QUFDSCxtREFvQzhCO0FBQzlCLHlEQUE0RDtBQUM1RCxxREFBOEM7QUFDOUMsaURBTzZCO0FBRzdCLHVDQUErRjtBQUMvRix5REFBNEY7QUFDNUYsK0NBQXlDO0FBQ3pDLGtEQUF1QjtBQUN2QixtQ0FBcUM7QUFDckMsa0RBQTZCO0FBQzdCLHFEQUErRDtBQUMvRCxvREFBNEI7QUFDNUIsb0RBQXVCO0FBQ3ZCLDBEQUFrQztBQUVsQywrREFBNEQ7QUFDNUQsaURBQThDO0FBQzlDLCtCQVllO0FBaVNmLE1BQU0sS0FBSyxHQUFHLElBQUEsZUFBUSxFQUFDLGtCQUFrQixDQUFDLENBQUM7QUFFOUIsUUFBQSxZQUFZLEdBQUc7SUFDMUIsSUFBSSxNQUFNO1FBQ1IsSUFBSSxDQUFDO1lBQ0gsT0FBTyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUNuQyxDQUFDO1FBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUNYLEtBQUssQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFDO1lBQ3hDLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDZixNQUFNLElBQUksMENBQStCLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUM5RCxDQUFDO0lBQ0gsQ0FBQztJQUVELElBQUksT0FBTztRQUNULElBQUksQ0FBQztZQUNILE9BQU8sT0FBTyxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDcEMsQ0FBQztRQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDWCxLQUFLLENBQUMsaUNBQWlDLENBQUMsQ0FBQztZQUN6QyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ2YsTUFBTSxJQUFJLDBDQUErQixDQUFDLGlCQUFp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QUMsQ0FBQyxRQUFRLEVBQUUsRUFBRTtnQkFDekYsaURBQWlELElBQUksQ0FBQyxRQUFRLEVBQUUsOEJBQThCLENBQ2pHLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQzs7QUFyNEVILDBEQXM0RUM7QUFyNEVRLDBDQUFrQixHQUFHLDRCQUE0QixBQUEvQixDQUFnQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQHByZXR0aWVyXG4gKi9cbmltcG9ydCB7XG4gIEFkZHJlc3NDb2luU3BlY2lmaWMsXG4gIEJpdEdvQmFzZSxcbiAgQnVpbGROZnRUcmFuc2ZlckRhdGFPcHRpb25zLFxuICBjb21tb24sXG4gIEVjZHNhLFxuICBFQ0RTQU1ldGhvZFR5cGVzLFxuICBFQ0RTQVV0aWxzLFxuICBFdGhlcmV1bUxpYnJhcnlVbmF2YWlsYWJsZUVycm9yLFxuICBGZWVFc3RpbWF0ZU9wdGlvbnMsXG4gIEZ1bGx5U2lnbmVkVHJhbnNhY3Rpb24sXG4gIGdldElzVW5zaWduZWRTd2VlcCxcbiAgSGFsZlNpZ25lZFRyYW5zYWN0aW9uLFxuICBJbnZhbGlkQWRkcmVzc0Vycm9yLFxuICBJbnZhbGlkQWRkcmVzc1ZlcmlmaWNhdGlvbk9iamVjdFByb3BlcnR5RXJyb3IsXG4gIElXYWxsZXQsXG4gIEtleVBhaXIsXG4gIE1QQ1N3ZWVwUmVjb3ZlcnlPcHRpb25zLFxuICBNUENUeCxcbiAgTVBDVHhzLFxuICBQYXJzZWRUcmFuc2FjdGlvbixcbiAgUGFyc2VUcmFuc2FjdGlvbk9wdGlvbnMsXG4gIFByZWJ1aWxkVHJhbnNhY3Rpb25SZXN1bHQsXG4gIFByZXNpZ25UcmFuc2FjdGlvbk9wdGlvbnMgYXMgQmFzZVByZXNpZ25UcmFuc2FjdGlvbk9wdGlvbnMsXG4gIFJlY2lwaWVudCxcbiAgU2lnblRyYW5zYWN0aW9uT3B0aW9ucyBhcyBCYXNlU2lnblRyYW5zYWN0aW9uT3B0aW9ucyxcbiAgVHJhbnNhY3Rpb25QYXJhbXMsXG4gIFRyYW5zYWN0aW9uUHJlYnVpbGQgYXMgQmFzZVRyYW5zYWN0aW9uUHJlYnVpbGQsXG4gIFRyYW5zYWN0aW9uUmVjaXBpZW50LFxuICBUeXBlZERhdGEsXG4gIFVuZXhwZWN0ZWRBZGRyZXNzRXJyb3IsXG4gIFVuc2lnbmVkVHJhbnNhY3Rpb25Uc3MsXG4gIFV0aWwsXG4gIFZlcmlmeUFkZHJlc3NPcHRpb25zIGFzIEJhc2VWZXJpZnlBZGRyZXNzT3B0aW9ucyxcbiAgVmVyaWZ5VHJhbnNhY3Rpb25PcHRpb25zLFxuICBXYWxsZXQsXG59IGZyb20gJ0BiaXRnby1iZXRhL3Nkay1jb3JlJztcbmltcG9ydCB7IGdldERlcml2YXRpb25QYXRoIH0gZnJvbSAnQGJpdGdvLWJldGEvc2RrLWxpYi1tcGMnO1xuaW1wb3J0IHsgYmlwMzIgfSBmcm9tICdAYml0Z28tYmV0YS9zZWNwMjU2azEnO1xuaW1wb3J0IHtcbiAgQmFzZUNvaW4gYXMgU3RhdGljc0Jhc2VDb2luLFxuICBDb2luRmVhdHVyZSxcbiAgQ29pbk1hcCxcbiAgY29pbnMsXG4gIEV0aGVyZXVtTmV0d29yayBhcyBFdGhMaWtlTmV0d29yayxcbiAgZXRoR2FzQ29uZmlncyxcbn0gZnJvbSAnQGJpdGdvLWJldGEvc3RhdGljcyc7XG5pbXBvcnQgdHlwZSAqIGFzIEV0aExpa2VDb21tb24gZnJvbSAnQGV0aGVyZXVtanMvY29tbW9uJztcbmltcG9ydCB0eXBlICogYXMgRXRoTGlrZVR4TGliIGZyb20gJ0BldGhlcmV1bWpzL3R4JztcbmltcG9ydCB7IEZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbiwgVHJhbnNhY3Rpb24gYXMgTGVnYWN5VHJhbnNhY3Rpb24gfSBmcm9tICdAZXRoZXJldW1qcy90eCc7XG5pbXBvcnQgeyBTaWduVHlwZWREYXRhVmVyc2lvbiwgVHlwZWREYXRhVXRpbHMsIFR5cGVkTWVzc2FnZSB9IGZyb20gJ0BtZXRhbWFzay9ldGgtc2lnLXV0aWwnO1xuaW1wb3J0IHsgQmlnTnVtYmVyIH0gZnJvbSAnYmlnbnVtYmVyLmpzJztcbmltcG9ydCBCTiBmcm9tICdibi5qcyc7XG5pbXBvcnQgeyByYW5kb21CeXRlcyB9IGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgZGVidWdMaWIgZnJvbSAnZGVidWcnO1xuaW1wb3J0IHsgYWRkSGV4UHJlZml4LCBzdHJpcEhleFByZWZpeCB9IGZyb20gJ2V0aGVyZXVtanMtdXRpbCc7XG5pbXBvcnQgS2VjY2FrIGZyb20gJ2tlY2Nhayc7XG5pbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IHNlY3AyNTZrMSBmcm9tICdzZWNwMjU2azEnO1xuXG5pbXBvcnQgeyBBYnN0cmFjdEV0aExpa2VDb2luIH0gZnJvbSAnLi9hYnN0cmFjdEV0aExpa2VDb2luJztcbmltcG9ydCB7IEV0aExpa2VUb2tlbiB9IGZyb20gJy4vZXRoTGlrZVRva2VuJztcbmltcG9ydCB7XG4gIGNhbGN1bGF0ZUZvcndhcmRlclYxQWRkcmVzcyxcbiAgRVJDMTE1NVRyYW5zZmVyQnVpbGRlcixcbiAgRVJDNzIxVHJhbnNmZXJCdWlsZGVyLFxuICBnZXRCdWZmZXJlZEJ5dGVDb2RlLFxuICBnZXRDb21tb24sXG4gIGdldFByb3h5SW5pdGNvZGUsXG4gIGdldFJhd0RlY29kZWQsXG4gIGdldFRva2VuLFxuICBLZXlQYWlyIGFzIEtleVBhaXJMaWIsXG4gIFRyYW5zYWN0aW9uQnVpbGRlcixcbiAgVHJhbnNmZXJCdWlsZGVyLFxufSBmcm9tICcuL2xpYic7XG5pbXBvcnQgeyBTZW5kQ3Jvc3NDaGFpblJlY292ZXJ5T3B0aW9ucyB9IGZyb20gJy4vdHlwZXMnO1xuXG4vKipcbiAqIFRoZSBwcmVidWlsdCBob3AgdHJhbnNhY3Rpb24gcmV0dXJuZWQgZnJvbSB0aGUgSFNNXG4gKi9cbmludGVyZmFjZSBIb3BQcmVidWlsZCB7XG4gIHR4OiBzdHJpbmc7XG4gIGlkOiBzdHJpbmc7XG4gIHNpZ25hdHVyZTogc3RyaW5nO1xuICBwYXltZW50SWQ6IHN0cmluZztcbiAgZ2FzUHJpY2U6IG51bWJlcjtcbiAgZ2FzTGltaXQ6IG51bWJlcjtcbiAgYW1vdW50OiBudW1iZXI7XG4gIHJlY2lwaWVudDogc3RyaW5nO1xuICBub25jZTogbnVtYmVyO1xuICB1c2VyUmVxU2lnOiBzdHJpbmc7XG4gIGdhc1ByaWNlTWF4OiBudW1iZXI7XG59XG5cbi8qKlxuICogVGhlIGV4dHJhIHBhcmFtZXRlcnMgdG8gc2VuZCB0byBwbGF0Zm9ybSBidWlsZCByb3V0ZSBmb3IgaG9wIHRyYW5zYWN0aW9uc1xuICovXG5pbnRlcmZhY2UgSG9wUGFyYW1zIHtcbiAgaG9wUGFyYW1zOiB7XG4gICAgZ2FzUHJpY2VNYXg6IG51bWJlcjtcbiAgICB1c2VyUmVxU2lnOiBzdHJpbmc7XG4gICAgcGF5bWVudElkOiBzdHJpbmc7XG4gICAgZ2FzTGltaXQ6IG51bWJlcjtcbiAgfTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBFSVAxNTU5IHtcbiAgbWF4UHJpb3JpdHlGZWVQZXJHYXM6IG51bWJlcjtcbiAgbWF4RmVlUGVyR2FzOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVwbGF5UHJvdGVjdGlvbk9wdGlvbnMge1xuICBjaGFpbjogc3RyaW5nIHwgbnVtYmVyO1xuICBoYXJkZm9yazogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFRyYW5zYWN0aW9uUHJlYnVpbGQgZXh0ZW5kcyBCYXNlVHJhbnNhY3Rpb25QcmVidWlsZCB7XG4gIGhvcFRyYW5zYWN0aW9uPzogSG9wUHJlYnVpbGQ7XG4gIGJ1aWxkUGFyYW1zOiB7XG4gICAgcmVjaXBpZW50czogUmVjaXBpZW50W107XG4gIH07XG4gIHJlY2lwaWVudHM6IFRyYW5zYWN0aW9uUmVjaXBpZW50W107XG4gIG5leHRDb250cmFjdFNlcXVlbmNlSWQ6IG51bWJlcjtcbiAgZ2FzUHJpY2U6IG51bWJlcjtcbiAgZ2FzTGltaXQ6IG51bWJlcjtcbiAgaXNCYXRjaDogYm9vbGVhbjtcbiAgY29pbjogc3RyaW5nO1xuICB0b2tlbj86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTaWduRmluYWxPcHRpb25zIHtcbiAgdHhQcmVidWlsZDoge1xuICAgIGVpcDE1NTk/OiBFSVAxNTU5O1xuICAgIHJlcGxheVByb3RlY3Rpb25PcHRpb25zPzogUmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM7XG4gICAgZ2FzUHJpY2U/OiBzdHJpbmc7XG4gICAgZ2FzTGltaXQ/OiBzdHJpbmc7XG4gICAgcmVjaXBpZW50cz86IFJlY2lwaWVudFtdO1xuICAgIGhhbGZTaWduZWQ/OiB7XG4gICAgICBleHBpcmVUaW1lOiBudW1iZXI7XG4gICAgICBjb250cmFjdFNlcXVlbmNlSWQ6IG51bWJlcjtcbiAgICAgIGJhY2t1cEtleU5vbmNlPzogbnVtYmVyO1xuICAgICAgc2lnbmF0dXJlOiBzdHJpbmc7XG4gICAgICB0eEhleD86IHN0cmluZztcbiAgICB9O1xuICAgIG5leHRDb250cmFjdFNlcXVlbmNlSWQ/OiBudW1iZXI7XG4gICAgaG9wVHJhbnNhY3Rpb24/OiBzdHJpbmc7XG4gICAgYmFja3VwS2V5Tm9uY2U/OiBudW1iZXI7XG4gICAgaXNCYXRjaD86IGJvb2xlYW47XG4gICAgdHhIZXg/OiBzdHJpbmc7XG4gICAgZXhwaXJlVGltZT86IG51bWJlcjtcbiAgfTtcbiAgc2lnbmluZ0tleU5vbmNlPzogbnVtYmVyO1xuICB3YWxsZXRDb250cmFjdEFkZHJlc3M/OiBzdHJpbmc7XG4gIHBydjogc3RyaW5nO1xuICByZWNpcGllbnRzPzogUmVjaXBpZW50W107XG4gIGNvbW1vbj86IEV0aExpa2VDb21tb24uZGVmYXVsdDtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTaWduVHJhbnNhY3Rpb25PcHRpb25zIGV4dGVuZHMgQmFzZVNpZ25UcmFuc2FjdGlvbk9wdGlvbnMsIFNpZ25GaW5hbE9wdGlvbnMge1xuICBpc0xhc3RTaWduYXR1cmU/OiBib29sZWFuO1xuICBleHBpcmVUaW1lPzogbnVtYmVyO1xuICBzZXF1ZW5jZUlkPzogbnVtYmVyO1xuICBnYXNMaW1pdD86IG51bWJlcjtcbiAgZ2FzUHJpY2U/OiBudW1iZXI7XG4gIGN1c3RvZGlhblRyYW5zYWN0aW9uSWQ/OiBzdHJpbmc7XG4gIGNvbW1vbj86IEV0aExpa2VDb21tb24uZGVmYXVsdDtcbiAgd2FsbGV0VmVyc2lvbj86IG51bWJlcjtcbn1cblxuZXhwb3J0IHR5cGUgU2lnbmVkVHJhbnNhY3Rpb24gPSBIYWxmU2lnbmVkVHJhbnNhY3Rpb24gfCBGdWxseVNpZ25lZFRyYW5zYWN0aW9uO1xuXG5leHBvcnQgaW50ZXJmYWNlIEZlZXNVc2VkIHtcbiAgZ2FzUHJpY2U6IG51bWJlcjtcbiAgZ2FzTGltaXQ6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFByZWNyZWF0ZUJpdEdvT3B0aW9ucyB7XG4gIGVudGVycHJpc2U/OiBzdHJpbmc7XG4gIG5ld0ZlZUFkZHJlc3M/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgT2ZmbGluZVZhdWx0VHhJbmZvIHtcbiAgbmV4dENvbnRyYWN0U2VxdWVuY2VJZD86IHN0cmluZztcbiAgY29udHJhY3RTZXF1ZW5jZUlkPzogc3RyaW5nO1xuICB0eD86IHN0cmluZztcbiAgdHhIZXg/OiBzdHJpbmc7XG4gIHVzZXJLZXk/OiBzdHJpbmc7XG4gIGJhY2t1cEtleT86IHN0cmluZztcbiAgY29pbjogc3RyaW5nO1xuICBnYXNQcmljZTogbnVtYmVyO1xuICBnYXNMaW1pdDogbnVtYmVyO1xuICByZWNpcGllbnRzOiBSZWNpcGllbnRbXTtcbiAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiBzdHJpbmc7XG4gIGFtb3VudDogc3RyaW5nO1xuICBiYWNrdXBLZXlOb25jZTogbnVtYmVyO1xuICAvLyBGb3IgRXRoIFNwZWNpZmljIENvaW5zXG4gIGVpcDE1NTk/OiBFSVAxNTU5O1xuICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zO1xuICAvLyBGb3IgSG90IFdhbGxldCBFdm1CYXNlZENyb3NzQ2hhaW5SZWNvdmVyeSBTcGVjaWZpY1xuICBoYWxmU2lnbmVkPzogSGFsZlNpZ25lZFRyYW5zYWN0aW9uO1xuICBmZWVzVXNlZD86IEZlZXNVc2VkO1xuICBpc0V2bUJhc2VkQ3Jvc3NDaGFpblJlY292ZXJ5PzogYm9vbGVhbjtcbiAgd2FsbGV0VmVyc2lvbj86IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFVuZm9ybWF0dGVkVHhJbmZvIHtcbiAgcmVjaXBpZW50OiBSZWNpcGllbnQ7XG59XG5cbmV4cG9ydCB0eXBlIFVuc2lnbmVkU3dlZXBUeE1QQ3YyID0ge1xuICB0eFJlcXVlc3RzOiB7XG4gICAgdHJhbnNhY3Rpb25zOiBbXG4gICAgICB7XG4gICAgICAgIHVuc2lnbmVkVHg6IFVuc2lnbmVkVHJhbnNhY3Rpb25Uc3M7XG4gICAgICAgIG5vbmNlOiBudW1iZXI7XG4gICAgICAgIHNpZ25hdHVyZVNoYXJlczogW107XG4gICAgICB9XG4gICAgXTtcbiAgICB3YWxsZXRDb2luOiBzdHJpbmc7XG4gIH1bXTtcbn07XG5cbmV4cG9ydCB0eXBlIFJlY292ZXJPcHRpb25zV2l0aEJ5dGVzID0ge1xuICBpc1RzczogdHJ1ZTtcbiAgLyoqXG4gICAqIEBkZXByZWNhdGVkIHRoaXMgaXMgbm8gbG9uZ2VyIHVzZWRcbiAgICovXG4gIG9wZW5TU0xCeXRlcz86IFVpbnQ4QXJyYXk7XG59O1xuXG5leHBvcnQgdHlwZSBOb25UU1NSZWNvdmVyT3B0aW9ucyA9IHtcbiAgaXNUc3M/OiBmYWxzZSB8IHVuZGVmaW5lZDtcbn07XG5cbmV4cG9ydCB0eXBlIFRTU1JlY292ZXJPcHRpb25zID0gUmVjb3Zlck9wdGlvbnNXaXRoQnl0ZXMgfCBOb25UU1NSZWNvdmVyT3B0aW9ucztcblxuZXhwb3J0IHR5cGUgUmVjb3Zlck9wdGlvbnMgPSB7XG4gIHVzZXJLZXk6IHN0cmluZztcbiAgYmFja3VwS2V5OiBzdHJpbmc7XG4gIHdhbGxldFBhc3NwaHJhc2U/OiBzdHJpbmc7XG4gIHdhbGxldENvbnRyYWN0QWRkcmVzczogc3RyaW5nOyAvLyB1c2UgdGhpcyBhcyB3YWxsZXRCYXNlQWRkcmVzcyBmb3IgVFNTXG4gIHJlY292ZXJ5RGVzdGluYXRpb246IHN0cmluZztcbiAga3JzUHJvdmlkZXI/OiBzdHJpbmc7XG4gIGdhc1ByaWNlPzogbnVtYmVyO1xuICBnYXNMaW1pdD86IG51bWJlcjtcbiAgZWlwMTU1OT86IEVJUDE1NTk7XG4gIHJlcGxheVByb3RlY3Rpb25PcHRpb25zPzogUmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM7XG4gIGJpdGdvRmVlQWRkcmVzcz86IHN0cmluZztcbiAgYml0Z29EZXN0aW5hdGlvbkFkZHJlc3M/OiBzdHJpbmc7XG4gIHRva2VuQ29udHJhY3RBZGRyZXNzPzogc3RyaW5nO1xuICBpbnRlbmRlZENoYWluPzogc3RyaW5nO1xuICBjb21tb24/OiBFdGhMaWtlQ29tbW9uLmRlZmF1bHQ7XG4gIGRlcml2YXRpb25TZWVkPzogc3RyaW5nO1xufSAmIFRTU1JlY292ZXJPcHRpb25zO1xuXG5leHBvcnQgdHlwZSBHZXRCYXRjaEV4ZWN1dGlvbkluZm9SVCA9IHtcbiAgdmFsdWVzOiBbc3RyaW5nW10sIHN0cmluZ1tdXTtcbiAgdG90YWxBbW91bnQ6IHN0cmluZztcbn07XG5cbmV4cG9ydCBpbnRlcmZhY2UgQnVpbGRUcmFuc2FjdGlvblBhcmFtcyB7XG4gIHRvOiBzdHJpbmc7XG4gIG5vbmNlPzogbnVtYmVyO1xuICB2YWx1ZTogbnVtYmVyO1xuICBkYXRhPzogQnVmZmVyO1xuICBnYXNQcmljZT86IG51bWJlcjtcbiAgZ2FzTGltaXQ/OiBudW1iZXI7XG4gIGVpcDE1NTk/OiBFSVAxNTU5O1xuICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlY292ZXJ5SW5mbyB7XG4gIGlkOiBzdHJpbmc7XG4gIHR4OiBzdHJpbmc7XG4gIGJhY2t1cEtleT86IHN0cmluZztcbiAgY29pbj86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSZWNvdmVyVG9rZW5UcmFuc2FjdGlvbiB7XG4gIGhhbGZTaWduZWQ6IHtcbiAgICByZWNpcGllbnQ6IFJlY2lwaWVudDtcbiAgICBleHBpcmVUaW1lOiBudW1iZXI7XG4gICAgY29udHJhY3RTZXF1ZW5jZUlkOiBudW1iZXI7XG4gICAgb3BlcmF0aW9uSGFzaDogc3RyaW5nO1xuICAgIHNpZ25hdHVyZTogc3RyaW5nO1xuICAgIGdhc0xpbWl0OiBudW1iZXI7XG4gICAgZ2FzUHJpY2U6IG51bWJlcjtcbiAgICB0b2tlbkNvbnRyYWN0QWRkcmVzczogc3RyaW5nO1xuICAgIHdhbGxldElkOiBzdHJpbmc7XG4gIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVjb3ZlclRva2VuT3B0aW9ucyB7XG4gIHRva2VuQ29udHJhY3RBZGRyZXNzOiBzdHJpbmc7XG4gIHdhbGxldDogV2FsbGV0O1xuICByZWNpcGllbnQ6IHN0cmluZztcbiAgYnJvYWRjYXN0PzogYm9vbGVhbjtcbiAgd2FsbGV0UGFzc3BocmFzZT86IHN0cmluZztcbiAgcHJ2Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEdldFNlbmRNZXRob2RBcmdzT3B0aW9ucyB7XG4gIHJlY2lwaWVudDogUmVjaXBpZW50O1xuICBleHBpcmVUaW1lOiBudW1iZXI7XG4gIGNvbnRyYWN0U2VxdWVuY2VJZDogbnVtYmVyO1xuICBzaWduYXR1cmU6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTZW5kTWV0aG9kQXJncyB7XG4gIG5hbWU6IHN0cmluZztcbiAgdHlwZTogc3RyaW5nO1xuICB2YWx1ZTogYW55O1xufVxuXG5pbnRlcmZhY2UgSG9wVHJhbnNhY3Rpb25CdWlsZE9wdGlvbnMge1xuICB3YWxsZXQ6IFdhbGxldDtcbiAgcmVjaXBpZW50czogUmVjaXBpZW50W107XG4gIHdhbGxldFBhc3NwaHJhc2U6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBCdWlsZE9wdGlvbnMge1xuICBob3A/OiBib29sZWFuO1xuICB3YWxsZXQ/OiBXYWxsZXQ7XG4gIHJlY2lwaWVudHM/OiBSZWNpcGllbnRbXTtcbiAgd2FsbGV0UGFzc3BocmFzZT86IHN0cmluZztcbiAgW2luZGV4OiBzdHJpbmddOiB1bmtub3duO1xufVxuXG5pbnRlcmZhY2UgRmVlRXN0aW1hdGUge1xuICBnYXNMaW1pdEVzdGltYXRlOiBudW1iZXI7XG4gIGZlZUVzdGltYXRlOiBudW1iZXI7XG59XG5cbi8vIFRPRE86IFRoaXMgaW50ZXJmYWNlIHdpbGwgbmVlZCB0byBiZSB1cGRhdGVkIGZvciB0aGUgbmV3IGZlZSBtb2RlbCBpbnRyb2R1Y2VkIGluIHRoZSBMb25kb24gSGFyZCBGb3JrXG5pbnRlcmZhY2UgRXRoVHJhbnNhY3Rpb25QYXJhbXMgZXh0ZW5kcyBUcmFuc2FjdGlvblBhcmFtcyB7XG4gIGdhc1ByaWNlPzogbnVtYmVyO1xuICBnYXNMaW1pdD86IG51bWJlcjtcbiAgaG9wUGFyYW1zPzogSG9wUGFyYW1zO1xuICBob3A/OiBib29sZWFuO1xuICBwcmVidWlsZFR4PzogUHJlYnVpbGRUcmFuc2FjdGlvblJlc3VsdDtcbiAgdG9rZW5OYW1lPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFZlcmlmeUV0aFRyYW5zYWN0aW9uT3B0aW9ucyBleHRlbmRzIFZlcmlmeVRyYW5zYWN0aW9uT3B0aW9ucyB7XG4gIHR4UHJlYnVpbGQ6IFRyYW5zYWN0aW9uUHJlYnVpbGQ7XG4gIHR4UGFyYW1zOiBFdGhUcmFuc2FjdGlvblBhcmFtcztcbn1cblxuaW50ZXJmYWNlIFByZXNpZ25UcmFuc2FjdGlvbk9wdGlvbnMgZXh0ZW5kcyBUcmFuc2FjdGlvblByZWJ1aWxkLCBCYXNlUHJlc2lnblRyYW5zYWN0aW9uT3B0aW9ucyB7XG4gIHdhbGxldDogV2FsbGV0O1xufVxuXG5pbnRlcmZhY2UgRXRoQWRkcmVzc0NvaW5TcGVjaWZpY3MgZXh0ZW5kcyBBZGRyZXNzQ29pblNwZWNpZmljIHtcbiAgZm9yd2FyZGVyVmVyc2lvbjogbnVtYmVyO1xuICBzYWx0Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFZlcmlmeUV0aEFkZHJlc3NPcHRpb25zIGV4dGVuZHMgQmFzZVZlcmlmeUFkZHJlc3NPcHRpb25zIHtcbiAgYmFzZUFkZHJlc3M6IHN0cmluZztcbiAgY29pblNwZWNpZmljOiBFdGhBZGRyZXNzQ29pblNwZWNpZmljcztcbiAgZm9yd2FyZGVyVmVyc2lvbjogbnVtYmVyO1xufVxuXG5jb25zdCBkZWJ1ZyA9IGRlYnVnTGliKCdiaXRnbzp2MjpldGhsaWtlJyk7XG5cbmV4cG9ydCBjb25zdCBvcHRpb25hbERlcHMgPSB7XG4gIGdldCBldGhBYmkoKSB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiByZXF1aXJlKCdldGhlcmV1bWpzLWFiaScpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGRlYnVnKCd1bmFibGUgdG8gbG9hZCBldGhlcmV1bWpzLWFiaTonKTtcbiAgICAgIGRlYnVnKGUuc3RhY2spO1xuICAgICAgdGhyb3cgbmV3IEV0aGVyZXVtTGlicmFyeVVuYXZhaWxhYmxlRXJyb3IoYGV0aGVyZXVtanMtYWJpYCk7XG4gICAgfVxuICB9LFxuXG4gIGdldCBldGhVdGlsKCkge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gcmVxdWlyZSgnZXRoZXJldW1qcy11dGlsJyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgZGVidWcoJ3VuYWJsZSB0byBsb2FkIGV0aGVyZXVtanMtdXRpbDonKTtcbiAgICAgIGRlYnVnKGUuc3RhY2spO1xuICAgICAgdGhyb3cgbmV3IEV0aGVyZXVtTGlicmFyeVVuYXZhaWxhYmxlRXJyb3IoYGV0aGVyZXVtanMtdXRpbGApO1xuICAgIH1cbiAgfSxcblxuICBnZXQgRXRoVHgoKTogdHlwZW9mIEV0aExpa2VUeExpYiB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiByZXF1aXJlKCdAZXRoZXJldW1qcy90eCcpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGRlYnVnKCd1bmFibGUgdG8gbG9hZCBAZXRoZXJldW1qcy90eCcpO1xuICAgICAgZGVidWcoZS5zdGFjayk7XG4gICAgICB0aHJvdyBuZXcgRXRoZXJldW1MaWJyYXJ5VW5hdmFpbGFibGVFcnJvcihgQGV0aGVyZXVtanMvdHhgKTtcbiAgICB9XG4gIH0sXG5cbiAgZ2V0IEV0aENvbW1vbigpOiB0eXBlb2YgRXRoTGlrZUNvbW1vbiB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiByZXF1aXJlKCdAZXRoZXJldW1qcy9jb21tb24nKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBkZWJ1ZygndW5hYmxlIHRvIGxvYWQgQGV0aGVyZXVtanMvY29tbW9uOicpO1xuICAgICAgZGVidWcoZS5zdGFjayk7XG4gICAgICB0aHJvdyBuZXcgRXRoZXJldW1MaWJyYXJ5VW5hdmFpbGFibGVFcnJvcihgQGV0aGVyZXVtanMvY29tbW9uYCk7XG4gICAgfVxuICB9LFxufTtcblxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEFic3RyYWN0RXRoTGlrZU5ld0NvaW5zIGV4dGVuZHMgQWJzdHJhY3RFdGhMaWtlQ29pbiB7XG4gIHN0YXRpYyBob3BUcmFuc2FjdGlvblNhbHQgPSAnYml0Z29Ib3BBZGRyZXNzUmVxdWVzdFNhbHQnO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgc2VuZE1ldGhvZE5hbWU6ICdzZW5kTXVsdGlTaWcnIHwgJ3NlbmRNdWx0aVNpZ1Rva2VuJztcblxuICByZWFkb25seSBzdGF0aWNzQ29pbj86IFJlYWRvbmx5PFN0YXRpY3NCYXNlQ29pbj47XG5cbiAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKGJpdGdvOiBCaXRHb0Jhc2UsIHN0YXRpY3NDb2luPzogUmVhZG9ubHk8U3RhdGljc0Jhc2VDb2luPikge1xuICAgIHN1cGVyKGJpdGdvLCBzdGF0aWNzQ29pbik7XG5cbiAgICBpZiAoIXN0YXRpY3NDb2luKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3NpbmcgcmVxdWlyZWQgY29uc3RydWN0b3IgcGFyYW1ldGVyIHN0YXRpY3NDb2luJyk7XG4gICAgfVxuXG4gICAgdGhpcy5zdGF0aWNzQ29pbiA9IHN0YXRpY3NDb2luO1xuICAgIHRoaXMuc2VuZE1ldGhvZE5hbWUgPSAnc2VuZE11bHRpU2lnJztcbiAgfVxuXG4gIC8qKlxuICAgKiBNZXRob2QgdG8gcmV0dXJuIHRoZSBjb2luJ3MgbmV0d29yayBvYmplY3RcbiAgICogQHJldHVybnMge0V0aExpa2VOZXR3b3JrIHwgdW5kZWZpbmVkfVxuICAgKi9cbiAgZ2V0TmV0d29yaygpOiBFdGhMaWtlTmV0d29yayB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuc3RhdGljc0NvaW4/Lm5ldHdvcmsgYXMgRXRoTGlrZU5ldHdvcms7XG4gIH1cblxuICAvKipcbiAgICogRXZhbHVhdGVzIHdoZXRoZXIgYW4gYWRkcmVzcyBzdHJpbmcgaXMgdmFsaWQgZm9yIHRoaXMgY29pblxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWRkcmVzc1xuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBhZGRyZXNzIGlzIHRoZSB2YWxpZCBldGhsaWtlIGFkZGVyc3NcbiAgICovXG4gIGlzVmFsaWRBZGRyZXNzKGFkZHJlc3M6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBvcHRpb25hbERlcHMuZXRoVXRpbC5pc1ZhbGlkQWRkcmVzcyhvcHRpb25hbERlcHMuZXRoVXRpbC5hZGRIZXhQcmVmaXgoYWRkcmVzcykpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZsYWcgZm9yIHNlbmRpbmcgZGF0YSBhbG9uZyB3aXRoIHRyYW5zYWN0aW9uc1xuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBva2F5IHRvIHNlbmQgdHggZGF0YSAoRVRIKSwgZmFsc2Ugb3RoZXJ3aXNlXG4gICAqL1xuICB0cmFuc2FjdGlvbkRhdGFBbGxvd2VkKCkge1xuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIERlZmF1bHQgZXhwaXJlIHRpbWUgZm9yIGEgY29udHJhY3QgY2FsbCAoMSB3ZWVrKVxuICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaW1lIGluIHNlY29uZHNcbiAgICovXG4gIGdldERlZmF1bHRFeHBpcmVUaW1lKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIE1hdGguZmxvb3IobmV3IERhdGUoKS5nZXRUaW1lKCkgLyAxMDAwKSArIDYwICogNjAgKiAyNCAqIDc7XG4gIH1cblxuICAvKipcbiAgICogTWV0aG9kIHRvIGdldCB0aGUgY3VzdG9tIGNoYWluIGNvbW1vbiBvYmplY3QgYmFzZWQgb24gcGFyYW1zIGZyb20gcmVjb3ZlcnlcbiAgICogQHBhcmFtIHtudW1iZXJ9IGNoYWluSWQgLSB0aGUgY2hhaW4gaWQgb2YgdGhlIGN1c3RvbSBjaGFpblxuICAgKiBAcmV0dXJucyB7RXRoTGlrZUNvbW1vbi5kZWZhdWx0fVxuICAgKi9cbiAgc3RhdGljIGdldEN1c3RvbUNoYWluQ29tbW9uKGNoYWluSWQ6IG51bWJlcik6IEV0aExpa2VDb21tb24uZGVmYXVsdCB7XG4gICAgY29uc3QgY29pbk5hbWUgPSBDb2luTWFwLmNvaW5OYW1lRnJvbUNoYWluSWQoY2hhaW5JZCk7XG4gICAgY29uc3QgY29pbiA9IGNvaW5zLmdldChjb2luTmFtZSk7XG4gICAgY29uc3QgZXRoTGlrZUNvbW1vbiA9IGdldENvbW1vbihjb2luLm5ldHdvcmsgYXMgRXRoTGlrZU5ldHdvcmspO1xuICAgIHJldHVybiBldGhMaWtlQ29tbW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgY29ycmVjdCBFdGggQ29tbW9uIG9iamVjdCBiYXNlZCBvbiBwYXJhbXMgZnJvbSBlaXRoZXIgcmVjb3Zlcnkgb3IgdHggYnVpbGRpbmdcbiAgICogQHBhcmFtIHtFSVAxNTU5fSBlaXAxNTU5IC0gY29uZmlncyB0aGF0IHNwZWNpZnkgd2hldGhlciB3ZSBzaG91bGQgY29uc3RydWN0IGFuIGVpcDE1NTkgdHhcbiAgICogQHBhcmFtIHtSZXBsYXlQcm90ZWN0aW9uT3B0aW9uc30gcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnMgLSBjaGVjayBpZiBjaGFpbiBpZCBzdXBwb3J0cyByZXBsYXkgcHJvdGVjdGlvblxuICAgKiBAcmV0dXJucyB7RXRoTGlrZUNvbW1vbi5kZWZhdWx0fVxuICAgKi9cbiAgcHJvdGVjdGVkIGdldEV0aExpa2VDb21tb24oXG4gICAgZWlwMTU1OT86IEVJUDE1NTksXG4gICAgcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM/OiBSZXBsYXlQcm90ZWN0aW9uT3B0aW9uc1xuICApOiBFdGhMaWtlQ29tbW9uLmRlZmF1bHQge1xuICAgIC8vIGlmIGVpcDE1NTkgcGFyYW1zIGFyZSBzcGVjaWZpZWQsIGRlZmF1bHQgdG8gbG9uZG9uIGhhcmRmb3JrLCBvdGhlcndpc2UsXG4gICAgLy8gZGVmYXVsdCB0byB0YW5nZXJpbmUgd2hpc3RsZSB0byBhdm9pZCByZXBsYXkgcHJvdGVjdGlvbiBpc3N1ZXNcbiAgICBjb25zdCBkZWZhdWx0SGFyZGZvcmsgPSAhIWVpcDE1NTkgPyAnbG9uZG9uJyA6IG9wdGlvbmFsRGVwcy5FdGhDb21tb24uSGFyZGZvcmsuUGV0ZXJzYnVyZztcbiAgICBjb25zdCBldGhMaWtlQ29tbW9uID0gQWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuZ2V0Q3VzdG9tQ2hhaW5Db21tb24ocmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM/LmNoYWluIGFzIG51bWJlcik7XG4gICAgZXRoTGlrZUNvbW1vbi5zZXRIYXJkZm9yayhyZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz8uaGFyZGZvcmsgPz8gZGVmYXVsdEhhcmRmb3JrKTtcbiAgICByZXR1cm4gZXRoTGlrZUNvbW1vbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBNZXRob2QgdG8gYnVpbGQgdGhlIHR4IG9iamVjdFxuICAgKiBAcGFyYW0ge0J1aWxkVHJhbnNhY3Rpb25QYXJhbXN9IHBhcmFtcyAtIHBhcmFtcyB0byBidWlsZCB0cmFuc2FjdGlvblxuICAgKiBAcmV0dXJucyB7RXRoTGlrZVR4TGliLkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbn1cbiAgICovXG4gIGJ1aWxkVHJhbnNhY3Rpb24oXG4gICAgcGFyYW1zOiBCdWlsZFRyYW5zYWN0aW9uUGFyYW1zXG4gICk6IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24gfCBFdGhMaWtlVHhMaWIuVHJhbnNhY3Rpb24ge1xuICAgIC8vIGlmIGVpcDE1NTkgcGFyYW1zIGFyZSBzcGVjaWZpZWQsIGRlZmF1bHQgdG8gbG9uZG9uIGhhcmRmb3JrLCBvdGhlcndpc2UsXG4gICAgLy8gZGVmYXVsdCB0byB0YW5nZXJpbmUgd2hpc3RsZSB0byBhdm9pZCByZXBsYXkgcHJvdGVjdGlvbiBpc3N1ZXNcbiAgICBjb25zdCBldGhMaWtlQ29tbW9uID0gdGhpcy5nZXRFdGhMaWtlQ29tbW9uKHBhcmFtcy5laXAxNTU5LCBwYXJhbXMucmVwbGF5UHJvdGVjdGlvbk9wdGlvbnMpO1xuICAgIGNvbnN0IGJhc2VQYXJhbXMgPSB7XG4gICAgICB0bzogcGFyYW1zLnRvLFxuICAgICAgbm9uY2U6IHBhcmFtcy5ub25jZSxcbiAgICAgIHZhbHVlOiBwYXJhbXMudmFsdWUsXG4gICAgICBkYXRhOiBwYXJhbXMuZGF0YSxcbiAgICAgIGdhc0xpbWl0OiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocGFyYW1zLmdhc0xpbWl0KSxcbiAgICB9O1xuXG4gICAgY29uc3QgdW5zaWduZWRFdGhUeCA9ICEhcGFyYW1zLmVpcDE1NTlcbiAgICAgID8gb3B0aW9uYWxEZXBzLkV0aFR4LkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbi5mcm9tVHhEYXRhKFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIC4uLmJhc2VQYXJhbXMsXG4gICAgICAgICAgICBtYXhGZWVQZXJHYXM6IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihwYXJhbXMuZWlwMTU1OS5tYXhGZWVQZXJHYXMpLFxuICAgICAgICAgICAgbWF4UHJpb3JpdHlGZWVQZXJHYXM6IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihwYXJhbXMuZWlwMTU1OS5tYXhQcmlvcml0eUZlZVBlckdhcyksXG4gICAgICAgICAgfSxcbiAgICAgICAgICB7IGNvbW1vbjogZXRoTGlrZUNvbW1vbiB9XG4gICAgICAgIClcbiAgICAgIDogb3B0aW9uYWxEZXBzLkV0aFR4LlRyYW5zYWN0aW9uLmZyb21UeERhdGEoXG4gICAgICAgICAge1xuICAgICAgICAgICAgLi4uYmFzZVBhcmFtcyxcbiAgICAgICAgICAgIGdhc1ByaWNlOiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocGFyYW1zLmdhc1ByaWNlKSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHsgY29tbW9uOiBldGhMaWtlQ29tbW9uIH1cbiAgICAgICAgKTtcblxuICAgIHJldHVybiB1bnNpZ25lZEV0aFR4O1xuICB9XG5cbiAgLyoqXG4gICAqIFF1ZXJ5IGV4cGxvcmVyIGZvciB0aGUgYmFsYW5jZSBvZiBhbiBhZGRyZXNzXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhZGRyZXNzIC0gdGhlIEVUSExpa2UgYWRkcmVzc1xuICAgKiBAcmV0dXJucyB7QmlnTnVtYmVyfSBhZGRyZXNzIGJhbGFuY2VcbiAgICovXG4gIGFzeW5jIHF1ZXJ5QWRkcmVzc0JhbGFuY2UoYWRkcmVzczogc3RyaW5nKTogUHJvbWlzZTxhbnk+IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLnJlY292ZXJ5QmxvY2tjaGFpbkV4cGxvcmVyUXVlcnkoe1xuICAgICAgY2hhaW5pZDogdGhpcy5nZXRDaGFpbklkKCkudG9TdHJpbmcoKSxcbiAgICAgIG1vZHVsZTogJ2FjY291bnQnLFxuICAgICAgYWN0aW9uOiAnYmFsYW5jZScsXG4gICAgICBhZGRyZXNzOiBhZGRyZXNzLFxuICAgIH0pO1xuICAgIC8vIHRocm93IGlmIHRoZSByZXN1bHQgZG9lcyBub3QgZXhpc3Qgb3IgdGhlIHJlc3VsdCBpcyBub3QgYSB2YWxpZCBudW1iZXJcbiAgICBpZiAoIXJlc3VsdCB8fCAhcmVzdWx0LnJlc3VsdCB8fCBpc05hTihyZXN1bHQucmVzdWx0KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBDb3VsZCBub3Qgb2J0YWluIGFkZHJlc3MgYmFsYW5jZSBmb3IgJHthZGRyZXNzfSBmcm9tIHRoZSBleHBsb3JlciwgZ290OiAke3Jlc3VsdC5yZXN1bHR9YCk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocmVzdWx0LnJlc3VsdCwgMTApO1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7UmVjaXBpZW50W119IHJlY2lwaWVudHMgLSB0aGUgcmVjaXBpZW50cyBvZiB0aGUgdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtudW1iZXJ9IGV4cGlyZVRpbWUgLSB0aGUgZXhwaXJlIHRpbWUgb2YgdGhlIHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBjb250cmFjdFNlcXVlbmNlSWQgLSB0aGUgY29udHJhY3Qgc2VxdWVuY2UgaWQgb2YgdGhlIHRyYW5zYWN0aW9uXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICAqL1xuICBnZXRPcGVyYXRpb25TaGEzRm9yRXhlY3V0ZUFuZENvbmZpcm0oXG4gICAgcmVjaXBpZW50czogUmVjaXBpZW50W10sXG4gICAgZXhwaXJlVGltZTogbnVtYmVyLFxuICAgIGNvbnRyYWN0U2VxdWVuY2VJZDogbnVtYmVyXG4gICk6IHN0cmluZyB7XG4gICAgaWYgKCFyZWNpcGllbnRzIHx8ICFBcnJheS5pc0FycmF5KHJlY2lwaWVudHMpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2V4cGVjdGluZyBhcnJheSBvZiByZWNpcGllbnRzJyk7XG4gICAgfVxuXG4gICAgLy8gUmlnaHQgbm93IHdlIG9ubHkgc3VwcG9ydCAxIHJlY2lwaWVudFxuICAgIGlmIChyZWNpcGllbnRzLmxlbmd0aCAhPT0gMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtdXN0IHNlbmQgdG8gZXhhY3RseSAxIHJlY2lwaWVudCcpO1xuICAgIH1cblxuICAgIGlmICghXy5pc051bWJlcihleHBpcmVUaW1lKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdleHBpcmVUaW1lIG11c3QgYmUgbnVtYmVyIG9mIHNlY29uZHMgc2luY2UgZXBvY2gnKTtcbiAgICB9XG5cbiAgICBpZiAoIV8uaXNOdW1iZXIoY29udHJhY3RTZXF1ZW5jZUlkKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdjb250cmFjdFNlcXVlbmNlSWQgbXVzdCBiZSBudW1iZXInKTtcbiAgICB9XG5cbiAgICAvLyBDaGVjayBpbnB1dHNcbiAgICByZWNpcGllbnRzLmZvckVhY2goZnVuY3Rpb24gKHJlY2lwaWVudCkge1xuICAgICAgaWYgKFxuICAgICAgICAhXy5pc1N0cmluZyhyZWNpcGllbnQuYWRkcmVzcykgfHxcbiAgICAgICAgIW9wdGlvbmFsRGVwcy5ldGhVdGlsLmlzVmFsaWRBZGRyZXNzKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmFkZEhleFByZWZpeChyZWNpcGllbnQuYWRkcmVzcykpXG4gICAgICApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGFkZHJlc3M6ICcgKyByZWNpcGllbnQuYWRkcmVzcyk7XG4gICAgICB9XG5cbiAgICAgIGxldCBhbW91bnQ6IEJpZ051bWJlcjtcbiAgICAgIHRyeSB7XG4gICAgICAgIGFtb3VudCA9IG5ldyBCaWdOdW1iZXIocmVjaXBpZW50LmFtb3VudCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBhbW91bnQgZm9yOiAnICsgcmVjaXBpZW50LmFkZHJlc3MgKyAnIC0gc2hvdWxkIGJlIG51bWVyaWMnKTtcbiAgICAgIH1cblxuICAgICAgcmVjaXBpZW50LmFtb3VudCA9IGFtb3VudC50b0ZpeGVkKDApO1xuXG4gICAgICBpZiAocmVjaXBpZW50LmRhdGEgJiYgIV8uaXNTdHJpbmcocmVjaXBpZW50LmRhdGEpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignRGF0YSBmb3IgcmVjaXBpZW50ICcgKyByZWNpcGllbnQuYWRkcmVzcyArICcgLSBzaG91bGQgYmUgb2YgdHlwZSBoZXggc3RyaW5nJyk7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBjb25zdCByZWNpcGllbnQgPSByZWNpcGllbnRzWzBdO1xuICAgIHJldHVybiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0hleChcbiAgICAgIG9wdGlvbmFsRGVwcy5ldGhBYmkuc29saWRpdHlTSEEzKC4uLnRoaXMuZ2V0T3BlcmF0aW9uKHJlY2lwaWVudCwgZXhwaXJlVGltZSwgY29udHJhY3RTZXF1ZW5jZUlkKSlcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0cmFuc2ZlciBvcGVyYXRpb24gZm9yIGNvaW5cbiAgICogQHBhcmFtIHtSZWNpcGllbnR9IHJlY2lwaWVudCAtIHJlY2lwaWVudCBpbmZvXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBleHBpcmVUaW1lIC0gZXhwaXJ5IHRpbWVcbiAgICogQHBhcmFtIHtudW1iZXJ9IGNvbnRyYWN0U2VxdWVuY2VJZCAtIHNlcXVlbmNlIGlkXG4gICAqIEByZXR1cm5zIHtBcnJheX0gb3BlcmF0aW9uIGFycmF5XG4gICAqL1xuICBnZXRPcGVyYXRpb24ocmVjaXBpZW50OiBSZWNpcGllbnQsIGV4cGlyZVRpbWU6IG51bWJlciwgY29udHJhY3RTZXF1ZW5jZUlkOiBudW1iZXIpOiAoc3RyaW5nIHwgQnVmZmVyKVtdW10ge1xuICAgIGNvbnN0IG5ldHdvcmsgPSB0aGlzLmdldE5ldHdvcmsoKSBhcyBFdGhMaWtlTmV0d29yaztcbiAgICByZXR1cm4gW1xuICAgICAgWydzdHJpbmcnLCAnYWRkcmVzcycsICd1aW50JywgJ2J5dGVzJywgJ3VpbnQnLCAndWludCddLFxuICAgICAgW1xuICAgICAgICBuZXR3b3JrLm5hdGl2ZUNvaW5PcGVyYXRpb25IYXNoUHJlZml4LFxuICAgICAgICBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ob3B0aW9uYWxEZXBzLmV0aFV0aWwuc3RyaXBIZXhQcmVmaXgocmVjaXBpZW50LmFkZHJlc3MpLCAxNiksXG4gICAgICAgIHJlY2lwaWVudC5hbW91bnQsXG4gICAgICAgIEJ1ZmZlci5mcm9tKG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KG9wdGlvbmFsRGVwcy5ldGhVdGlsLnBhZFRvRXZlbihyZWNpcGllbnQuZGF0YSB8fCAnJykpLCAnaGV4JyksXG4gICAgICAgIGV4cGlyZVRpbWUsXG4gICAgICAgIGNvbnRyYWN0U2VxdWVuY2VJZCxcbiAgICAgIF0sXG4gICAgXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBRdWVyaWVzIHRoZSBjb250cmFjdCAodmlhIGV4cGxvcmVyIEFQSSkgZm9yIHRoZSBuZXh0IHNlcXVlbmNlIElEXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhZGRyZXNzIC0gYWRkcmVzcyBvZiB0aGUgY29udHJhY3RcbiAgICogQHJldHVybnMge1Byb21pc2U8TnVtYmVyPn0gc2VxdWVuY2UgSURcbiAgICovXG4gIGFzeW5jIHF1ZXJ5U2VxdWVuY2VJZChhZGRyZXNzOiBzdHJpbmcpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIC8vIEdldCBzZXF1ZW5jZSBJRCB1c2luZyBjb250cmFjdCBjYWxsXG4gICAgY29uc3Qgc2VxdWVuY2VJZE1ldGhvZFNpZ25hdHVyZSA9IG9wdGlvbmFsRGVwcy5ldGhBYmkubWV0aG9kSUQoJ2dldE5leHRTZXF1ZW5jZUlkJywgW10pO1xuICAgIGNvbnN0IHNlcXVlbmNlSWRBcmdzID0gb3B0aW9uYWxEZXBzLmV0aEFiaS5yYXdFbmNvZGUoW10sIFtdKTtcbiAgICBjb25zdCBzZXF1ZW5jZUlkRGF0YSA9IEJ1ZmZlci5jb25jYXQoW3NlcXVlbmNlSWRNZXRob2RTaWduYXR1cmUsIHNlcXVlbmNlSWRBcmdzXSkudG9TdHJpbmcoJ2hleCcpO1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMucmVjb3ZlcnlCbG9ja2NoYWluRXhwbG9yZXJRdWVyeSh7XG4gICAgICBjaGFpbmlkOiB0aGlzLmdldENoYWluSWQoKS50b1N0cmluZygpLFxuICAgICAgbW9kdWxlOiAncHJveHknLFxuICAgICAgYWN0aW9uOiAnZXRoX2NhbGwnLFxuICAgICAgdG86IGFkZHJlc3MsXG4gICAgICBkYXRhOiBzZXF1ZW5jZUlkRGF0YSxcbiAgICAgIHRhZzogJ2xhdGVzdCcsXG4gICAgfSk7XG4gICAgaWYgKCFyZXN1bHQgfHwgIXJlc3VsdC5yZXN1bHQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IG9idGFpbiBzZXF1ZW5jZSBJRCBmcm9tIGV4cGxvcmVyLCBnb3Q6ICcgKyByZXN1bHQucmVzdWx0KTtcbiAgICB9XG4gICAgY29uc3Qgc2VxdWVuY2VJZEhleCA9IHJlc3VsdC5yZXN1bHQ7XG4gICAgcmV0dXJuIG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihzZXF1ZW5jZUlkSGV4LnNsaWNlKDIpLCAxNikudG9OdW1iZXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvdmVyIGFuIHVuc3VwcG9ydGVkIHRva2VuIGZyb20gYSBCaXRHbyBtdWx0aXNpZyB3YWxsZXRcbiAgICogVGhpcyBidWlsZHMgYSBoYWxmLXNpZ25lZCB0cmFuc2FjdGlvbiwgZm9yIHdoaWNoIHRoZXJlIHdpbGwgYmUgYW4gYWRtaW4gcm91dGUgdG8gY28tc2lnbiBhbmQgYnJvYWRjYXN0LiBPcHRpb25hbGx5XG4gICAqIHRoZSB1c2VyIGNhbiBzZXQgcGFyYW1zLmJyb2FkY2FzdCA9IHRydWUgYW5kIHRoZSBoYWxmLXNpZ25lZCB0eCB3aWxsIGJlIHNlbnQgdG8gQml0R28gZm9yIGNvc2lnbmluZyBhbmQgYnJvYWRjYXN0aW5nXG4gICAqIEBwYXJhbSB7UmVjb3ZlclRva2VuT3B0aW9uc30gcGFyYW1zXG4gICAqIEBwYXJhbSB7V2FsbGV0fSBwYXJhbXMud2FsbGV0IC0gdGhlIHdhbGxldCB0byByZWNvdmVyIHRoZSB0b2tlbiBmcm9tXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3MgLSB0aGUgY29udHJhY3QgYWRkcmVzcyBvZiB0aGUgdW5zdXBwb3J0ZWQgdG9rZW5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5yZWNpcGllbnQgLSB0aGUgZGVzdGluYXRpb24gYWRkcmVzcyByZWNvdmVyZWQgdG9rZW5zIHNob3VsZCBiZSBzZW50IHRvXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMud2FsbGV0UGFzc3BocmFzZSAtIHRoZSB3YWxsZXQgcGFzc3BocmFzZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLnBydiAtIHRoZSB4cHJ2XG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gcGFyYW1zLmJyb2FkY2FzdCAtIGlmIHRydWUsIHdlIHdpbGwgYXV0b21hdGljYWxseSBzdWJtaXQgdGhlIGhhbGYtc2lnbmVkIHR4IHRvIEJpdEdvIGZvciBjb3NpZ25pbmcgYW5kIGJyb2FkY2FzdGluZ1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxSZWNvdmVyVG9rZW5UcmFuc2FjdGlvbj59XG4gICAqL1xuICBhc3luYyByZWNvdmVyVG9rZW4ocGFyYW1zOiBSZWNvdmVyVG9rZW5PcHRpb25zKTogUHJvbWlzZTxSZWNvdmVyVG9rZW5UcmFuc2FjdGlvbj4ge1xuICAgIGNvbnN0IG5ldHdvcmsgPSB0aGlzLmdldE5ldHdvcmsoKSBhcyBFdGhMaWtlTmV0d29yaztcbiAgICBpZiAoIV8uaXNPYmplY3QocGFyYW1zKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGByZWNvdmVyVG9rZW4gbXVzdCBiZSBwYXNzZWQgYSBwYXJhbXMgb2JqZWN0LiBHb3QgJHtwYXJhbXN9ICh0eXBlICR7dHlwZW9mIHBhcmFtc30pYCk7XG4gICAgfVxuXG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzKSB8fCAhXy5pc1N0cmluZyhwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGB0b2tlbkNvbnRyYWN0QWRkcmVzcyBtdXN0IGJlIGEgc3RyaW5nLCBnb3QgJHtcbiAgICAgICAgICBwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3NcbiAgICAgICAgfSAodHlwZSAke3R5cGVvZiBwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3N9KWBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmlzVmFsaWRBZGRyZXNzKHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigndG9rZW5Db250cmFjdEFkZHJlc3Mgbm90IGEgdmFsaWQgYWRkcmVzcycpO1xuICAgIH1cblxuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy53YWxsZXQpIHx8ICEocGFyYW1zLndhbGxldCBpbnN0YW5jZW9mIFdhbGxldCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgd2FsbGV0IG11c3QgYmUgYSB3YWxsZXQgaW5zdGFuY2UsIGdvdCAke3BhcmFtcy53YWxsZXR9ICh0eXBlICR7dHlwZW9mIHBhcmFtcy53YWxsZXR9KWApO1xuICAgIH1cblxuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy5yZWNpcGllbnQpIHx8ICFfLmlzU3RyaW5nKHBhcmFtcy5yZWNpcGllbnQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYHJlY2lwaWVudCBtdXN0IGJlIGEgc3RyaW5nLCBnb3QgJHtwYXJhbXMucmVjaXBpZW50fSAodHlwZSAke3R5cGVvZiBwYXJhbXMucmVjaXBpZW50fSlgKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuaXNWYWxpZEFkZHJlc3MocGFyYW1zLnJlY2lwaWVudCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigncmVjaXBpZW50IG5vdCBhIHZhbGlkIGFkZHJlc3MnKTtcbiAgICB9XG5cbiAgICBpZiAoIW9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSGV4IHx8ICFvcHRpb25hbERlcHMuZXRoQWJpLnNvbGlkaXR5U0hBMykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdldGhlcmV1bSBub3QgZnVsbHkgc3VwcG9ydGVkIGluIHRoaXMgZW52aXJvbm1lbnQnKTtcbiAgICB9XG5cbiAgICAvLyBHZXQgdG9rZW4gYmFsYW5jZSBmcm9tIGV4dGVybmFsIEFQSVxuICAgIGNvbnN0IGNvaW5TcGVjaWZpYyA9IHBhcmFtcy53YWxsZXQuY29pblNwZWNpZmljKCk7XG4gICAgaWYgKCFjb2luU3BlY2lmaWMgfHwgIV8uaXNTdHJpbmcoY29pblNwZWNpZmljLmJhc2VBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtaXNzaW5nIHJlcXVpcmVkIGNvaW4gc3BlY2lmaWMgcHJvcGVydHkgYmFzZUFkZHJlc3MnKTtcbiAgICB9XG4gICAgY29uc3QgcmVjb3ZlcnlBbW91bnQgPSBhd2FpdCB0aGlzLnF1ZXJ5QWRkcmVzc1Rva2VuQmFsYW5jZShwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3MsIGNvaW5TcGVjaWZpYy5iYXNlQWRkcmVzcyk7XG5cbiAgICBpZiAocGFyYW1zLmJyb2FkY2FzdCkge1xuICAgICAgLy8gV2UncmUgZ29pbmcgdG8gY3JlYXRlIGEgbm9ybWFsIEVUSCB0cmFuc2FjdGlvbiB0aGF0IHNlbmRzIGFuIGFtb3VudCBvZiAwIEVUSCB0byB0aGVcbiAgICAgIC8vIHRva2VuQ29udHJhY3RBZGRyZXNzIGFuZCBlbmNvZGUgdGhlIHVuc3VwcG9ydGVkLXRva2VuLXNlbmQgZGF0YSBpbiB0aGUgZGF0YSBmaWVsZFxuICAgICAgLy8gI3RyaWNrc3lcbiAgICAgIGNvbnN0IHNlbmRNZXRob2RBcmdzID0gW1xuICAgICAgICB7XG4gICAgICAgICAgbmFtZTogJ190bycsXG4gICAgICAgICAgdHlwZTogJ2FkZHJlc3MnLFxuICAgICAgICAgIHZhbHVlOiBwYXJhbXMucmVjaXBpZW50LFxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgbmFtZTogJ192YWx1ZScsXG4gICAgICAgICAgdHlwZTogJ3VpbnQyNTYnLFxuICAgICAgICAgIHZhbHVlOiByZWNvdmVyeUFtb3VudC50b1N0cmluZygxMCksXG4gICAgICAgIH0sXG4gICAgICBdO1xuICAgICAgY29uc3QgbWV0aG9kU2lnbmF0dXJlID0gb3B0aW9uYWxEZXBzLmV0aEFiaS5tZXRob2RJRCgndHJhbnNmZXInLCBfLm1hcChzZW5kTWV0aG9kQXJncywgJ3R5cGUnKSk7XG4gICAgICBjb25zdCBlbmNvZGVkQXJncyA9IG9wdGlvbmFsRGVwcy5ldGhBYmkucmF3RW5jb2RlKF8ubWFwKHNlbmRNZXRob2RBcmdzLCAndHlwZScpLCBfLm1hcChzZW5kTWV0aG9kQXJncywgJ3ZhbHVlJykpO1xuICAgICAgY29uc3Qgc2VuZERhdGEgPSBCdWZmZXIuY29uY2F0KFttZXRob2RTaWduYXR1cmUsIGVuY29kZWRBcmdzXSk7XG5cbiAgICAgIGNvbnN0IGJyb2FkY2FzdFBhcmFtczogYW55ID0ge1xuICAgICAgICBhZGRyZXNzOiBwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3MsXG4gICAgICAgIGFtb3VudDogJzAnLFxuICAgICAgICBkYXRhOiBzZW5kRGF0YS50b1N0cmluZygnaGV4JyksXG4gICAgICB9O1xuXG4gICAgICBpZiAocGFyYW1zLndhbGxldFBhc3NwaHJhc2UpIHtcbiAgICAgICAgYnJvYWRjYXN0UGFyYW1zLndhbGxldFBhc3NwaHJhc2UgPSBwYXJhbXMud2FsbGV0UGFzc3BocmFzZTtcbiAgICAgIH0gZWxzZSBpZiAocGFyYW1zLnBydikge1xuICAgICAgICBicm9hZGNhc3RQYXJhbXMucHJ2ID0gcGFyYW1zLnBydjtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGF3YWl0IHBhcmFtcy53YWxsZXQuc2VuZChicm9hZGNhc3RQYXJhbXMpO1xuICAgIH1cblxuICAgIGNvbnN0IHJlY2lwaWVudCA9IHtcbiAgICAgIGFkZHJlc3M6IHBhcmFtcy5yZWNpcGllbnQsXG4gICAgICBhbW91bnQ6IHJlY292ZXJ5QW1vdW50LnRvU3RyaW5nKDEwKSxcbiAgICB9O1xuXG4gICAgLy8gVGhpcyBzaWduYXR1cmUgd2lsbCBiZSB2YWxpZCBmb3Igb25lIHdlZWtcbiAgICBjb25zdCBleHBpcmVUaW1lID0gTWF0aC5mbG9vcihuZXcgRGF0ZSgpLmdldFRpbWUoKSAvIDEwMDApICsgNjAgKiA2MCAqIDI0ICogNztcblxuICAgIC8vIEdldCBzZXF1ZW5jZSBJRC4gV2UgZG8gdGhpcyBieSBidWlsZGluZyBhICdmYWtlJyBldGggdHJhbnNhY3Rpb24sIHNvIHRoZSBwbGF0Zm9ybSB3aWxsIGluY3JlbWVudCBhbmQgcmV0dXJuIHVzIHRoZSBuZXcgc2VxdWVuY2UgaWRcbiAgICAvLyBUaGlzIF9kb2VzXyByZXF1aXJlIHRoZSB1c2VyIHRvIGhhdmUgYSBub24temVybyB3YWxsZXQgYmFsYW5jZVxuICAgIGNvbnN0IHsgbmV4dENvbnRyYWN0U2VxdWVuY2VJZCwgZ2FzUHJpY2UsIGdhc0xpbWl0IH0gPSAoYXdhaXQgcGFyYW1zLndhbGxldC5wcmVidWlsZFRyYW5zYWN0aW9uKHtcbiAgICAgIHJlY2lwaWVudHM6IFtcbiAgICAgICAge1xuICAgICAgICAgIGFkZHJlc3M6IHBhcmFtcy5yZWNpcGllbnQsXG4gICAgICAgICAgYW1vdW50OiAnMScsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0pKSBhcyBhbnk7XG5cbiAgICAvLyB0aGVzZSByZWNvdmVyaWVzIG5lZWQgdG8gYmUgcHJvY2Vzc2VkIGJ5IHN1cHBvcnQsIGJ1dCBpZiB0aGUgY3VzdG9tZXIgc2VuZHMgYW55IHRyYW5zYWN0aW9ucyBiZWZvcmUgcmVjb3ZlcnkgaXNcbiAgICAvLyBjb21wbGV0ZSB0aGUgc2VxdWVuY2UgSUQgd2lsbCBiZSBpbnZhbGlkLiBhcnRpZmljaWFsbHkgaW5mbGF0ZSB0aGUgc2VxdWVuY2UgSUQgdG8gYWxsb3cgbW9yZSB0aW1lIGZvciBwcm9jZXNzaW5nXG4gICAgY29uc3Qgc2FmZVNlcXVlbmNlSWQgPSBuZXh0Q29udHJhY3RTZXF1ZW5jZUlkICsgMTAwMDtcblxuICAgIC8vIEJ1aWxkIHNlbmREYXRhIGZvciBldGhlcmV1bSB0eFxuICAgIGNvbnN0IG9wZXJhdGlvblR5cGVzID0gWydzdHJpbmcnLCAnYWRkcmVzcycsICd1aW50JywgJ2FkZHJlc3MnLCAndWludCcsICd1aW50J107XG4gICAgY29uc3Qgb3BlcmF0aW9uQXJncyA9IFtcbiAgICAgIC8vIFRva2VuIG9wZXJhdGlvbiBoYXMgcHJlZml4IGhhcyBiZWVuIGFkZGVkIGhlcmUgc28gdGhhdCBldGhlciBvcGVyYXRpb24gaGFzaGVzLCBzaWduYXR1cmVzIGNhbm5vdCBiZSByZS11c2VkIGZvciB0b2tlblNlbmRpbmdcbiAgICAgIG5ldHdvcmsudG9rZW5PcGVyYXRpb25IYXNoUHJlZml4LFxuICAgICAgbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KHJlY2lwaWVudC5hZGRyZXNzKSwgMTYpLFxuICAgICAgcmVjaXBpZW50LmFtb3VudCxcbiAgICAgIG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihvcHRpb25hbERlcHMuZXRoVXRpbC5zdHJpcEhleFByZWZpeChwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3MpLCAxNiksXG4gICAgICBleHBpcmVUaW1lLFxuICAgICAgc2FmZVNlcXVlbmNlSWQsXG4gICAgXTtcblxuICAgIGNvbnN0IG9wZXJhdGlvbkhhc2ggPSBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0hleChcbiAgICAgIG9wdGlvbmFsRGVwcy5ldGhBYmkuc29saWRpdHlTSEEzKG9wZXJhdGlvblR5cGVzLCBvcGVyYXRpb25BcmdzKVxuICAgICk7XG5cbiAgICBjb25zdCB1c2VyUHJ2ID0gYXdhaXQgcGFyYW1zLndhbGxldC5nZXRQcnYoe1xuICAgICAgcHJ2OiBwYXJhbXMucHJ2LFxuICAgICAgd2FsbGV0UGFzc3BocmFzZTogcGFyYW1zLndhbGxldFBhc3NwaHJhc2UsXG4gICAgfSk7XG5cbiAgICBjb25zdCBzaWduYXR1cmUgPSBVdGlsLmV0aFNpZ25Nc2dIYXNoKG9wZXJhdGlvbkhhc2gsIFV0aWwueHBydlRvRXRoUHJpdmF0ZUtleSh1c2VyUHJ2KSk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgaGFsZlNpZ25lZDoge1xuICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCxcbiAgICAgICAgZXhwaXJlVGltZTogZXhwaXJlVGltZSxcbiAgICAgICAgY29udHJhY3RTZXF1ZW5jZUlkOiBzYWZlU2VxdWVuY2VJZCxcbiAgICAgICAgb3BlcmF0aW9uSGFzaDogb3BlcmF0aW9uSGFzaCxcbiAgICAgICAgc2lnbmF0dXJlOiBzaWduYXR1cmUsXG4gICAgICAgIGdhc0xpbWl0OiBnYXNMaW1pdCxcbiAgICAgICAgZ2FzUHJpY2U6IGdhc1ByaWNlLFxuICAgICAgICB0b2tlbkNvbnRyYWN0QWRkcmVzczogcGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzLFxuICAgICAgICB3YWxsZXRJZDogcGFyYW1zLndhbGxldC5pZCgpLFxuICAgICAgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEVuc3VyZSBlaXRoZXIgZW50ZXJwcmlzZSBvciBuZXdGZWVBZGRyZXNzIGlzIHBhc3NlZCwgdG8ga25vdyB3aGV0aGVyIHRvIGNyZWF0ZSBuZXcga2V5IG9yIHVzZSBlbnRlcnByaXNlIGtleVxuICAgKiBAcGFyYW0ge1ByZWNyZWF0ZUJpdEdvT3B0aW9uc30gcGFyYW1zXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuZW50ZXJwcmlzZSB7U3RyaW5nfSB0aGUgZW50ZXJwcmlzZSBpZCB0byBhc3NvY2lhdGUgd2l0aCB0aGlzIGtleVxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLm5ld0ZlZUFkZHJlc3Mge0Jvb2xlYW59IGNyZWF0ZSBhIG5ldyBmZWUgYWRkcmVzcyAoZW50ZXJwcmlzZSBub3QgbmVlZGVkIGluIHRoaXMgY2FzZSlcbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBwcmVDcmVhdGVCaXRHbyhwYXJhbXM6IFByZWNyZWF0ZUJpdEdvT3B0aW9ucyk6IHZvaWQge1xuICAgIC8vIFdlIGFsd2F5cyBuZWVkIHBhcmFtcyBvYmplY3QsIHNpbmNlIGVpdGhlciBlbnRlcnByaXNlIG9yIG5ld0ZlZUFkZHJlc3MgaXMgcmVxdWlyZWRcbiAgICBpZiAoIV8uaXNPYmplY3QocGFyYW1zKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBwcmVDcmVhdGVCaXRHbyBtdXN0IGJlIHBhc3NlZCBhIHBhcmFtcyBvYmplY3QuIEdvdCAke3BhcmFtc30gKHR5cGUgJHt0eXBlb2YgcGFyYW1zfSlgKTtcbiAgICB9XG5cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMuZW50ZXJwcmlzZSkgJiYgXy5pc1VuZGVmaW5lZChwYXJhbXMubmV3RmVlQWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ2V4cGVjdGluZyBlbnRlcnByaXNlIHdoZW4gYWRkaW5nIEJpdEdvIGtleS4gSWYgeW91IHdhbnQgdG8gY3JlYXRlIGEgbmV3IEVUSCBiaXRnbyBrZXksIHNldCB0aGUgbmV3RmVlQWRkcmVzcyBwYXJhbWV0ZXIgdG8gdHJ1ZS4nXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIENoZWNrIHdoZXRoZXIga2V5IHNob3VsZCBiZSBhbiBlbnRlcnByaXNlIGtleSBvciBhIEJpdEdvIGtleSBmb3IgYSBuZXcgZmVlIGFkZHJlc3NcbiAgICBpZiAoIV8uaXNVbmRlZmluZWQocGFyYW1zLmVudGVycHJpc2UpICYmICFfLmlzVW5kZWZpbmVkKHBhcmFtcy5uZXdGZWVBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbmNvbXBhdGlibGUgYXJndW1lbnRzIC0gY2Fubm90IHBhc3MgYm90aCBlbnRlcnByaXNlIGFuZCBuZXdGZWVBZGRyZXNzIHBhcmFtZXRlci5gKTtcbiAgICB9XG5cbiAgICBpZiAoIV8uaXNVbmRlZmluZWQocGFyYW1zLmVudGVycHJpc2UpICYmICFfLmlzU3RyaW5nKHBhcmFtcy5lbnRlcnByaXNlKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBlbnRlcnByaXNlIHNob3VsZCBiZSBhIHN0cmluZyAtIGdvdCAke3BhcmFtcy5lbnRlcnByaXNlfSAodHlwZSAke3R5cGVvZiBwYXJhbXMuZW50ZXJwcmlzZX0pYCk7XG4gICAgfVxuXG4gICAgaWYgKCFfLmlzVW5kZWZpbmVkKHBhcmFtcy5uZXdGZWVBZGRyZXNzKSAmJiAhXy5pc0Jvb2xlYW4ocGFyYW1zLm5ld0ZlZUFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBuZXdGZWVBZGRyZXNzIHNob3VsZCBiZSBhIGJvb2xlYW4gLSBnb3QgJHtwYXJhbXMubmV3RmVlQWRkcmVzc30gKHR5cGUgJHt0eXBlb2YgcGFyYW1zLm5ld0ZlZUFkZHJlc3N9KWBcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFF1ZXJpZXMgcHVibGljIGJsb2NrIGV4cGxvcmVyIHRvIGdldCB0aGUgbmV4dCBFVEhMaWtlIGNvaW4ncyBub25jZSB0aGF0IHNob3VsZCBiZSB1c2VkIGZvciB0aGUgZ2l2ZW4gRVRIIGFkZHJlc3NcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFkZHJlc3NcbiAgICogQHJldHVybnMge1Byb21pc2U8bnVtYmVyPn1cbiAgICovXG4gIGFzeW5jIGdldEFkZHJlc3NOb25jZShhZGRyZXNzOiBzdHJpbmcpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIC8vIEdldCBub25jZSBmb3IgYmFja3VwIGtleSAoc2hvdWxkIGJlIDApXG4gICAgbGV0IG5vbmNlID0gMDtcblxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMucmVjb3ZlcnlCbG9ja2NoYWluRXhwbG9yZXJRdWVyeSh7XG4gICAgICBjaGFpbmlkOiB0aGlzLmdldENoYWluSWQoKS50b1N0cmluZygpLFxuICAgICAgbW9kdWxlOiAnYWNjb3VudCcsXG4gICAgICBhY3Rpb246ICd0eGxpc3QnLFxuICAgICAgYWRkcmVzcyxcbiAgICB9KTtcbiAgICBpZiAoIXJlc3VsdCB8fCAhQXJyYXkuaXNBcnJheShyZXN1bHQucmVzdWx0KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbmFibGUgdG8gZmluZCBuZXh0IG5vbmNlIGZyb20gRXRoZXJzY2FuLCBnb3Q6ICcgKyBKU09OLnN0cmluZ2lmeShyZXN1bHQpKTtcbiAgICB9XG4gICAgY29uc3QgYmFja3VwS2V5VHhMaXN0ID0gcmVzdWx0LnJlc3VsdDtcbiAgICBpZiAoYmFja3VwS2V5VHhMaXN0Lmxlbmd0aCA+IDApIHtcbiAgICAgIC8vIENhbGN1bGF0ZSBsYXN0IG5vbmNlIHVzZWRcbiAgICAgIGNvbnN0IG91dGdvaW5nVHhzID0gYmFja3VwS2V5VHhMaXN0LmZpbHRlcigodHgpID0+IHR4LmZyb20gPT09IGFkZHJlc3MpO1xuICAgICAgbm9uY2UgPSBvdXRnb2luZ1R4cy5sZW5ndGg7XG4gICAgfVxuICAgIHJldHVybiBub25jZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBIZWxwZXIgZnVuY3Rpb24gZm9yIHJlY292ZXIoKVxuICAgKiBUaGlzIHRyYW5zZm9ybXMgdGhlIHVuc2lnbmVkIHRyYW5zYWN0aW9uIGluZm9ybWF0aW9uIGludG8gYSBmb3JtYXQgdGhlIEJpdEdvIG9mZmxpbmUgdmF1bHQgZXhwZWN0c1xuICAgKiBAcGFyYW0ge1VuZm9ybWF0dGVkVHhJbmZvfSB0eEluZm8gLSB0eCBpbmZvXG4gICAqIEBwYXJhbSB7RXRoTGlrZVR4TGliLlRyYW5zYWN0aW9uIHwgRXRoTGlrZVR4TGliLkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbn0gZXRoVHggLSB0aGUgZXRoZXJldW1qcyB0eCBvYmplY3RcbiAgICogQHBhcmFtIHtzdHJpbmd9IHVzZXJLZXkgLSB0aGUgdXNlcidzIGtleVxuICAgKiBAcGFyYW0ge3N0cmluZ30gYmFja3VwS2V5IC0gdGhlIGJhY2t1cCBrZXlcbiAgICogQHBhcmFtIHtCdWZmZXJ9IGdhc1ByaWNlIC0gZ2FzIHByaWNlIGZvciB0aGUgdHhcbiAgICogQHBhcmFtIHtudW1iZXJ9IGdhc0xpbWl0IC0gZ2FzIGxpbWl0IGZvciB0aGUgdHhcbiAgICogQHBhcmFtIHtFSVAxNTU5fSBlaXAxNTU5IC0gZWlwMTU1OSBwYXJhbXNcbiAgICogQHBhcmFtIHtSZXBsYXlQcm90ZWN0aW9uT3B0aW9uc30gcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnMgLSByZXBsYXkgcHJvdGVjdGlvbiBvcHRpb25zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPE9mZmxpbmVWYXVsdFR4SW5mbz59XG4gICAqL1xuICBhc3luYyBmb3JtYXRGb3JPZmZsaW5lVmF1bHQoXG4gICAgdHhJbmZvOiBVbmZvcm1hdHRlZFR4SW5mbyxcbiAgICBldGhUeDogRXRoTGlrZVR4TGliLlRyYW5zYWN0aW9uIHwgRXRoTGlrZVR4TGliLkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbixcbiAgICB1c2VyS2V5OiBzdHJpbmcsXG4gICAgYmFja3VwS2V5OiBzdHJpbmcsXG4gICAgZ2FzUHJpY2U6IEJ1ZmZlcixcbiAgICBnYXNMaW1pdDogbnVtYmVyLFxuICAgIGVpcDE1NTk/OiBFSVAxNTU5LFxuICAgIHJlcGxheVByb3RlY3Rpb25PcHRpb25zPzogUmVwbGF5UHJvdGVjdGlvbk9wdGlvbnNcbiAgKTogUHJvbWlzZTxPZmZsaW5lVmF1bHRUeEluZm8+IHtcbiAgICBpZiAoIWV0aFR4LnRvKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0V0aCB0eCBtdXN0IGhhdmUgYSBgdG9gIGFkZHJlc3MnKTtcbiAgICB9XG4gICAgY29uc3QgYmFja3VwSEROb2RlID0gYmlwMzIuZnJvbUJhc2U1OChiYWNrdXBLZXkpO1xuICAgIGNvbnN0IGJhY2t1cFNpZ25pbmdLZXkgPSBiYWNrdXBIRE5vZGUucHVibGljS2V5O1xuICAgIGNvbnN0IHJlc3BvbnNlOiBPZmZsaW5lVmF1bHRUeEluZm8gPSB7XG4gICAgICB0eDogZXRoVHguc2VyaWFsaXplKCkudG9TdHJpbmcoJ2hleCcpLFxuICAgICAgdXNlcktleSxcbiAgICAgIGJhY2t1cEtleSxcbiAgICAgIGNvaW46IHRoaXMuZ2V0Q2hhaW4oKSxcbiAgICAgIGdhc1ByaWNlOiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0ludChnYXNQcmljZSkudG9GaXhlZCgpLFxuICAgICAgZ2FzTGltaXQsXG4gICAgICByZWNpcGllbnRzOiBbdHhJbmZvLnJlY2lwaWVudF0sXG4gICAgICB3YWxsZXRDb250cmFjdEFkZHJlc3M6IGV0aFR4LnRvLnRvU3RyaW5nKCksXG4gICAgICBhbW91bnQ6IHR4SW5mby5yZWNpcGllbnQuYW1vdW50IGFzIHN0cmluZyxcbiAgICAgIGJhY2t1cEtleU5vbmNlOiBhd2FpdCB0aGlzLmdldEFkZHJlc3NOb25jZShcbiAgICAgICAgYDB4JHtvcHRpb25hbERlcHMuZXRoVXRpbC5wdWJsaWNUb0FkZHJlc3MoYmFja3VwU2lnbmluZ0tleSwgdHJ1ZSkudG9TdHJpbmcoJ2hleCcpfWBcbiAgICAgICksXG4gICAgICBlaXAxNTU5LFxuICAgICAgcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnMsXG4gICAgfTtcbiAgICBfLmV4dGVuZChyZXNwb25zZSwgdHhJbmZvKTtcbiAgICByZXNwb25zZS5uZXh0Q29udHJhY3RTZXF1ZW5jZUlkID0gcmVzcG9uc2UuY29udHJhY3RTZXF1ZW5jZUlkO1xuICAgIHJldHVybiByZXNwb25zZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBIZWxwZXIgZnVuY3Rpb24gZm9yIHJlY292ZXIoKVxuICAgKiBUaGlzIHRyYW5zZm9ybXMgdGhlIHVuc2lnbmVkIHRyYW5zYWN0aW9uIGluZm9ybWF0aW9uIGludG8gYSBmb3JtYXQgdGhlIEJpdEdvIG9mZmxpbmUgdmF1bHQgZXhwZWN0c1xuICAgKiBAcGFyYW0ge1VuZm9ybWF0dGVkVHhJbmZvfSB0eEluZm8gLSB0eCBpbmZvXG4gICAqIEBwYXJhbSB7RXRoTGlrZVR4TGliLlRyYW5zYWN0aW9uIHwgRXRoTGlrZVR4TGliLkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbn0gZXRoVHggLSB0aGUgZXRoZXJldW1qcyB0eCBvYmplY3RcbiAgICogQHBhcmFtIHtzdHJpbmd9IHVzZXJLZXkgLSB0aGUgdXNlcidzIGtleVxuICAgKiBAcGFyYW0ge3N0cmluZ30gYmFja3VwS2V5IC0gdGhlIGJhY2t1cCBrZXlcbiAgICogQHBhcmFtIHtCdWZmZXJ9IGdhc1ByaWNlIC0gZ2FzIHByaWNlIGZvciB0aGUgdHhcbiAgICogQHBhcmFtIHtudW1iZXJ9IGdhc0xpbWl0IC0gZ2FzIGxpbWl0IGZvciB0aGUgdHhcbiAgICogQHBhcmFtIHtudW1iZXJ9IGJhY2t1cEtleU5vbmNlIC0gdGhlIG5vbmNlIG9mIHRoZSBiYWNrdXAga2V5IGFkZHJlc3NcbiAgICogQHBhcmFtIHtFSVAxNTU5fSBlaXAxNTU5IC0gZWlwMTU1OSBwYXJhbXNcbiAgICogQHBhcmFtIHtSZXBsYXlQcm90ZWN0aW9uT3B0aW9uc30gcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnMgLSByZXBsYXkgcHJvdGVjdGlvbiBvcHRpb25zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPE9mZmxpbmVWYXVsdFR4SW5mbz59XG4gICAqL1xuICBmb3JtYXRGb3JPZmZsaW5lVmF1bHRUU1MoXG4gICAgdHhJbmZvOiBVbmZvcm1hdHRlZFR4SW5mbyxcbiAgICBldGhUeDogRXRoTGlrZVR4TGliLlRyYW5zYWN0aW9uIHwgRXRoTGlrZVR4TGliLkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbixcbiAgICB1c2VyS2V5OiBzdHJpbmcsXG4gICAgYmFja3VwS2V5OiBzdHJpbmcsXG4gICAgZ2FzUHJpY2U6IEJ1ZmZlcixcbiAgICBnYXNMaW1pdDogbnVtYmVyLFxuICAgIGJhY2t1cEtleU5vbmNlOiBudW1iZXIsXG4gICAgZWlwMTU1OT86IEVJUDE1NTksXG4gICAgcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM/OiBSZXBsYXlQcm90ZWN0aW9uT3B0aW9uc1xuICApOiBPZmZsaW5lVmF1bHRUeEluZm8ge1xuICAgIGlmICghZXRoVHgudG8pIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRXRoIHR4IG11c3QgaGF2ZSBhIGB0b2AgYWRkcmVzcycpO1xuICAgIH1cbiAgICBjb25zdCByZXNwb25zZTogT2ZmbGluZVZhdWx0VHhJbmZvID0ge1xuICAgICAgdHg6IGV0aFR4LnNlcmlhbGl6ZSgpLnRvU3RyaW5nKCdoZXgnKSxcbiAgICAgIHR4SGV4OiBldGhUeC5nZXRNZXNzYWdlVG9TaWduKGZhbHNlKS50b1N0cmluZygpLFxuICAgICAgdXNlcktleSxcbiAgICAgIGJhY2t1cEtleSxcbiAgICAgIGNvaW46IHRoaXMuZ2V0Q2hhaW4oKSxcbiAgICAgIGdhc1ByaWNlOiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0ludChnYXNQcmljZSkudG9GaXhlZCgpLFxuICAgICAgZ2FzTGltaXQsXG4gICAgICByZWNpcGllbnRzOiBbdHhJbmZvLnJlY2lwaWVudF0sXG4gICAgICB3YWxsZXRDb250cmFjdEFkZHJlc3M6IGV0aFR4LnRvLnRvU3RyaW5nKCksXG4gICAgICBhbW91bnQ6IHR4SW5mby5yZWNpcGllbnQuYW1vdW50IGFzIHN0cmluZyxcbiAgICAgIGJhY2t1cEtleU5vbmNlOiBiYWNrdXBLZXlOb25jZSxcbiAgICAgIGVpcDE1NTksXG4gICAgICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyxcbiAgICB9O1xuICAgIF8uZXh0ZW5kKHJlc3BvbnNlLCB0eEluZm8pO1xuICAgIHJldHVybiByZXNwb25zZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayB3aGV0aGVyIHRoZSBnYXMgcHJpY2UgcGFzc2VkIGluIGJ5IHVzZXIgYXJlIHdpdGhpbiBvdXIgbWF4IGFuZCBtaW4gYm91bmRzXG4gICAqIElmIHRoZXkgYXJlIG5vdCBzZXQsIHNldCB0aGVtIHRvIHRoZSBkZWZhdWx0c1xuICAgKiBAcGFyYW0ge251bWJlcn0gdXNlckdhc1ByaWNlIC0gdXNlciBkZWZpbmVkIGdhcyBwcmljZVxuICAgKiBAcmV0dXJucyB7bnVtYmVyfSB0aGUgZ2FzIHByaWNlIHRvIHVzZSBmb3IgdGhpcyB0cmFuc2FjdGlvblxuICAgKi9cbiAgc2V0R2FzUHJpY2UodXNlckdhc1ByaWNlPzogbnVtYmVyKTogbnVtYmVyIHtcbiAgICBpZiAoIXVzZXJHYXNQcmljZSkge1xuICAgICAgcmV0dXJuIGV0aEdhc0NvbmZpZ3MuZGVmYXVsdEdhc1ByaWNlO1xuICAgIH1cblxuICAgIGNvbnN0IGdhc1ByaWNlTWF4ID0gZXRoR2FzQ29uZmlncy5tYXhpbXVtR2FzUHJpY2U7XG4gICAgY29uc3QgZ2FzUHJpY2VNaW4gPSBldGhHYXNDb25maWdzLm1pbmltdW1HYXNQcmljZTtcbiAgICBpZiAodXNlckdhc1ByaWNlIDwgZ2FzUHJpY2VNaW4gfHwgdXNlckdhc1ByaWNlID4gZ2FzUHJpY2VNYXgpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgR2FzIHByaWNlIG11c3QgYmUgYmV0d2VlbiAke2dhc1ByaWNlTWlufSBhbmQgJHtnYXNQcmljZU1heH1gKTtcbiAgICB9XG4gICAgcmV0dXJuIHVzZXJHYXNQcmljZTtcbiAgfVxuICAvKipcbiAgICogQ2hlY2sgd2hldGhlciBnYXMgbGltaXQgcGFzc2VkIGluIGJ5IHVzZXIgYXJlIHdpdGhpbiBvdXIgbWF4IGFuZCBtaW4gYm91bmRzXG4gICAqIElmIHRoZXkgYXJlIG5vdCBzZXQsIHNldCB0aGVtIHRvIHRoZSBkZWZhdWx0c1xuICAgKiBAcGFyYW0ge251bWJlcn0gdXNlckdhc0xpbWl0IHVzZXIgZGVmaW5lZCBnYXMgbGltaXRcbiAgICogQHJldHVybnMge251bWJlcn0gdGhlIGdhcyBsaW1pdCB0byB1c2UgZm9yIHRoaXMgdHJhbnNhY3Rpb25cbiAgICovXG4gIHNldEdhc0xpbWl0KHVzZXJHYXNMaW1pdD86IG51bWJlcik6IG51bWJlciB7XG4gICAgaWYgKCF1c2VyR2FzTGltaXQpIHtcbiAgICAgIHJldHVybiBldGhHYXNDb25maWdzLmRlZmF1bHRHYXNMaW1pdDtcbiAgICB9XG4gICAgY29uc3QgZ2FzTGltaXRNYXggPSBldGhHYXNDb25maWdzLm1heGltdW1HYXNMaW1pdDtcbiAgICBjb25zdCBnYXNMaW1pdE1pbiA9IGV0aEdhc0NvbmZpZ3MubWluaW11bUdhc0xpbWl0O1xuICAgIGlmICh1c2VyR2FzTGltaXQgPCBnYXNMaW1pdE1pbiB8fCB1c2VyR2FzTGltaXQgPiBnYXNMaW1pdE1heCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBHYXMgbGltaXQgbXVzdCBiZSBiZXR3ZWVuICR7Z2FzTGltaXRNaW59IGFuZCAke2dhc0xpbWl0TWF4fWApO1xuICAgIH1cbiAgICByZXR1cm4gdXNlckdhc0xpbWl0O1xuICB9XG5cbiAgLyoqXG4gICAqIEhlbHBlciBmdW5jdGlvbiBmb3Igc2lnblRyYW5zYWN0aW9uIGZvciB0aGUgcmFyZSBjYXNlIHRoYXQgU0RLIGlzIGRvaW5nIHRoZSBzZWNvbmQgc2lnbmF0dXJlXG4gICAqIE5vdGU6IHdlIGFyZSBleHBlY3RpbmcgdGhpcyB0byBiZSBjYWxsZWQgZnJvbSB0aGUgb2ZmbGluZSB2YXVsdFxuICAgKiBAcGFyYW0ge1NpZ25GaW5hbE9wdGlvbnMudHhQcmVidWlsZH0gcGFyYW1zLnR4UHJlYnVpbGRcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5wcnZcbiAgICogQHJldHVybnMge3t0eEhleDogc3RyaW5nfX1cbiAgICovXG4gIGFzeW5jIHNpZ25GaW5hbEV0aExpa2UocGFyYW1zOiBTaWduRmluYWxPcHRpb25zKTogUHJvbWlzZTxGdWxseVNpZ25lZFRyYW5zYWN0aW9uPiB7XG4gICAgY29uc3Qgc2lnbmluZ0tleSA9IG5ldyBLZXlQYWlyTGliKHsgcHJ2OiBwYXJhbXMucHJ2IH0pLmdldEtleXMoKS5wcnY7XG4gICAgaWYgKF8uaXNVbmRlZmluZWQoc2lnbmluZ0tleSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyBwcml2YXRlIGtleScpO1xuICAgIH1cbiAgICBjb25zdCB0eEJ1aWxkZXIgPSB0aGlzLmdldFRyYW5zYWN0aW9uQnVpbGRlcihwYXJhbXMuY29tbW9uKTtcbiAgICB0cnkge1xuICAgICAgdHhCdWlsZGVyLmZyb20ocGFyYW1zLnR4UHJlYnVpbGQuaGFsZlNpZ25lZD8udHhIZXgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCBoYWxmLXNpZ25lZCB0cmFuc2FjdGlvbicpO1xuICAgIH1cbiAgICB0eEJ1aWxkZXIuc2lnbih7IGtleTogc2lnbmluZ0tleSB9KTtcbiAgICBjb25zdCB0eCA9IGF3YWl0IHR4QnVpbGRlci5idWlsZCgpO1xuICAgIHJldHVybiB7XG4gICAgICB0eEhleDogdHgudG9Ccm9hZGNhc3RGb3JtYXQoKSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEFzc2VtYmxlIGhhbGYtc2lnbiBwcmVidWlsdCB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0ge1NpZ25UcmFuc2FjdGlvbk9wdGlvbnN9IHBhcmFtc1xuICAgKi9cbiAgYXN5bmMgc2lnblRyYW5zYWN0aW9uKHBhcmFtczogU2lnblRyYW5zYWN0aW9uT3B0aW9ucyk6IFByb21pc2U8U2lnbmVkVHJhbnNhY3Rpb24+IHtcbiAgICAvLyBOb3JtYWxseSB0aGUgU0RLIHByb3ZpZGVzIHRoZSBmaXJzdCBzaWduYXR1cmUgZm9yIGFuIEV0aExpa2UgdHgsIGJ1dCBvY2Nhc2lvbmFsbHkgaXQgcHJvdmlkZXMgdGhlIHNlY29uZCBhbmQgZmluYWwgb25lLlxuICAgIGlmIChwYXJhbXMuaXNMYXN0U2lnbmF0dXJlKSB7XG4gICAgICAvLyBJbiB0aGlzIGNhc2Ugd2hlbiB3ZSdyZSBkb2luZyB0aGUgc2Vjb25kIChmaW5hbCkgc2lnbmF0dXJlLCB0aGUgbG9naWMgaXMgZGlmZmVyZW50LlxuICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuc2lnbkZpbmFsRXRoTGlrZShwYXJhbXMpO1xuICAgIH1cbiAgICBjb25zdCB0eEJ1aWxkZXIgPSB0aGlzLmdldFRyYW5zYWN0aW9uQnVpbGRlcihwYXJhbXMuY29tbW9uKTtcbiAgICB0eEJ1aWxkZXIuZnJvbShwYXJhbXMudHhQcmVidWlsZC50eEhleCk7XG4gICAgdHhCdWlsZGVyXG4gICAgICAudHJhbnNmZXIoKVxuICAgICAgLmNvaW4odGhpcy5zdGF0aWNzQ29pbj8ubmFtZSBhcyBzdHJpbmcpXG4gICAgICAua2V5KG5ldyBLZXlQYWlyTGliKHsgcHJ2OiBwYXJhbXMucHJ2IH0pLmdldEtleXMoKS5wcnYhKTtcbiAgICBpZiAocGFyYW1zLndhbGxldFZlcnNpb24pIHtcbiAgICAgIHR4QnVpbGRlci53YWxsZXRWZXJzaW9uKHBhcmFtcy53YWxsZXRWZXJzaW9uKTtcbiAgICB9XG4gICAgY29uc3QgdHJhbnNhY3Rpb24gPSBhd2FpdCB0eEJ1aWxkZXIuYnVpbGQoKTtcblxuICAgIC8vIEluIGNhc2Ugb2YgdHggd2l0aCBjb250cmFjdCBkYXRhIGZyb20gYSBjdXN0b2RpYWwgd2FsbGV0LCB3ZSBhcmUgcnVubmluZyBpbnRvIGFuIGlzc3VlXG4gICAgLy8gYXMgaGFsZlNpZ25lZCBpcyBub3QgaGF2aW5nIHRoZSBkYXRhIGZpZWxkLiBTbywgd2UgYXJlIGFkZGluZyB0aGUgZGF0YSBmaWVsZCB0byB0aGUgaGFsZlNpZ25lZCB0eFxuICAgIGxldCByZWNpcGllbnRzID0gcGFyYW1zLnR4UHJlYnVpbGQucmVjaXBpZW50cyB8fCBwYXJhbXMucmVjaXBpZW50cztcbiAgICBpZiAocmVjaXBpZW50cyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZWNpcGllbnRzID0gdHJhbnNhY3Rpb24ub3V0cHV0cy5tYXAoKG91dHB1dCkgPT4gKHsgYWRkcmVzczogb3V0cHV0LmFkZHJlc3MsIGFtb3VudDogb3V0cHV0LnZhbHVlIH0pKTtcbiAgICB9XG5cbiAgICBjb25zdCB0eFBhcmFtcyA9IHtcbiAgICAgIGVpcDE1NTk6IHBhcmFtcy50eFByZWJ1aWxkLmVpcDE1NTksXG4gICAgICB0eEhleDogdHJhbnNhY3Rpb24udG9Ccm9hZGNhc3RGb3JtYXQoKSxcbiAgICAgIHJlY2lwaWVudHM6IHJlY2lwaWVudHMsXG4gICAgICBleHBpcmF0aW9uOiBwYXJhbXMudHhQcmVidWlsZC5leHBpcmVUaW1lLFxuICAgICAgaG9wVHJhbnNhY3Rpb246IHBhcmFtcy50eFByZWJ1aWxkLmhvcFRyYW5zYWN0aW9uLFxuICAgICAgY3VzdG9kaWFuVHJhbnNhY3Rpb25JZDogcGFyYW1zLmN1c3RvZGlhblRyYW5zYWN0aW9uSWQsXG4gICAgICBleHBpcmVUaW1lOiBwYXJhbXMuZXhwaXJlVGltZSxcbiAgICAgIGNvbnRyYWN0U2VxdWVuY2VJZDogcGFyYW1zLnR4UHJlYnVpbGQubmV4dENvbnRyYWN0U2VxdWVuY2VJZCBhcyBudW1iZXIsXG4gICAgICBzZXF1ZW5jZUlkOiBwYXJhbXMuc2VxdWVuY2VJZCxcbiAgICAgIC4uLihwYXJhbXMudHhQcmVidWlsZC5pc0JhdGNoID8geyBpc0JhdGNoOiBwYXJhbXMudHhQcmVidWlsZC5pc0JhdGNoIH0gOiB7fSksXG4gICAgfTtcblxuICAgIHJldHVybiB7IGhhbGZTaWduZWQ6IHR4UGFyYW1zIH07XG4gIH1cblxuICAvKipcbiAgICogTWV0aG9kIHRvIHZhbGlkYXRlIHJlY292ZXJ5IHBhcmFtc1xuICAgKiBAcGFyYW0ge1JlY292ZXJPcHRpb25zfSBwYXJhbXNcbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICB2YWxpZGF0ZVJlY292ZXJ5UGFyYW1zKHBhcmFtczogUmVjb3Zlck9wdGlvbnMpOiB2b2lkIHtcbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMudXNlcktleSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyB1c2VyS2V5Jyk7XG4gICAgfVxuXG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLmJhY2t1cEtleSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyBiYWNrdXBLZXknKTtcbiAgICB9XG5cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMud2FsbGV0UGFzc3BocmFzZSkgJiYgIXBhcmFtcy51c2VyS2V5LnN0YXJ0c1dpdGgoJ3hwdWInKSAmJiAhcGFyYW1zLmlzVHNzKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3Npbmcgd2FsbGV0IHBhc3NwaHJhc2UnKTtcbiAgICB9XG5cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzKSB8fCAhdGhpcy5pc1ZhbGlkQWRkcmVzcyhwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkIHdhbGxldENvbnRyYWN0QWRkcmVzcycpO1xuICAgIH1cblxuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uKSB8fCAhdGhpcy5pc1ZhbGlkQWRkcmVzcyhwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbikpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCByZWNvdmVyeURlc3RpbmF0aW9uJyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEhlbHBlciB3aGljaCBBZGRzIHNpZ25hdHVyZXMgdG8gdHggb2JqZWN0IGFuZCByZS1zZXJpYWxpemVzIHR4XG4gICAqIEBwYXJhbSB7RXRoTGlrZUNvbW1vbi5kZWZhdWx0fSBldGhDb21tb25cbiAgICogQHBhcmFtIHtFdGhMaWtlVHhMaWIuRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uIHwgRXRoTGlrZVR4TGliLlRyYW5zYWN0aW9ufSB0eFxuICAgKiBAcGFyYW0ge0VDRFNBTWV0aG9kVHlwZXMuU2lnbmF0dXJlfSBzaWduYXR1cmVcbiAgICogQHJldHVybnMge0V0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24gfCBFdGhMaWtlVHhMaWIuVHJhbnNhY3Rpb259XG4gICAqL1xuICBwcml2YXRlIGdldFNpZ25lZFR4RnJvbVNpZ25hdHVyZShcbiAgICBldGhDb21tb246IEV0aExpa2VDb21tb24uZGVmYXVsdCxcbiAgICB0eDogRXRoTGlrZVR4TGliLkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbixcbiAgICBzaWduYXR1cmU6IEVDRFNBTWV0aG9kVHlwZXMuU2lnbmF0dXJlXG4gICkge1xuICAgIC8vIGdldCBzaWduZWQgVHggZnJvbSBzaWduYXR1cmVcbiAgICBjb25zdCB0eERhdGEgPSB0eC50b0pTT04oKTtcbiAgICBjb25zdCB5UGFyaXR5ID0gc2lnbmF0dXJlLnJlY2lkO1xuICAgIGNvbnN0IGJhc2VQYXJhbXMgPSB7XG4gICAgICB0bzogdHhEYXRhLnRvLFxuICAgICAgbm9uY2U6IG5ldyBCTihzdHJpcEhleFByZWZpeCh0eERhdGEubm9uY2UhKSwgJ2hleCcpLFxuICAgICAgdmFsdWU6IG5ldyBCTihzdHJpcEhleFByZWZpeCh0eERhdGEudmFsdWUhKSwgJ2hleCcpLFxuICAgICAgZ2FzTGltaXQ6IG5ldyBCTihzdHJpcEhleFByZWZpeCh0eERhdGEuZ2FzTGltaXQhKSwgJ2hleCcpLFxuICAgICAgZGF0YTogdHhEYXRhLmRhdGEsXG4gICAgICByOiBhZGRIZXhQcmVmaXgoc2lnbmF0dXJlLnIpLFxuICAgICAgczogYWRkSGV4UHJlZml4KHNpZ25hdHVyZS5zKSxcbiAgICB9O1xuXG4gICAgbGV0IGZpbmFsVHg7XG4gICAgaWYgKHR4RGF0YS5tYXhGZWVQZXJHYXMgJiYgdHhEYXRhLm1heFByaW9yaXR5RmVlUGVyR2FzKSB7XG4gICAgICBmaW5hbFR4ID0gRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uLmZyb21UeERhdGEoXG4gICAgICAgIHtcbiAgICAgICAgICAuLi5iYXNlUGFyYW1zLFxuICAgICAgICAgIG1heFByaW9yaXR5RmVlUGVyR2FzOiBuZXcgQk4oc3RyaXBIZXhQcmVmaXgodHhEYXRhLm1heFByaW9yaXR5RmVlUGVyR2FzISksICdoZXgnKSxcbiAgICAgICAgICBtYXhGZWVQZXJHYXM6IG5ldyBCTihzdHJpcEhleFByZWZpeCh0eERhdGEubWF4RmVlUGVyR2FzISksICdoZXgnKSxcbiAgICAgICAgICB2OiBuZXcgQk4oeVBhcml0eS50b1N0cmluZygpKSxcbiAgICAgICAgfSxcbiAgICAgICAgeyBjb21tb246IGV0aENvbW1vbiB9XG4gICAgICApO1xuICAgIH0gZWxzZSBpZiAodHhEYXRhLmdhc1ByaWNlKSB7XG4gICAgICBjb25zdCB2ID0gQmlnSW50KDM1KSArIEJpZ0ludCh5UGFyaXR5KSArIEJpZ0ludChldGhDb21tb24uY2hhaW5JZEJOKCkudG9OdW1iZXIoKSkgKiBCaWdJbnQoMik7XG4gICAgICBmaW5hbFR4ID0gTGVnYWN5VHJhbnNhY3Rpb24uZnJvbVR4RGF0YShcbiAgICAgICAge1xuICAgICAgICAgIC4uLmJhc2VQYXJhbXMsXG4gICAgICAgICAgdjogbmV3IEJOKHYudG9TdHJpbmcoKSksXG4gICAgICAgICAgZ2FzUHJpY2U6IG5ldyBCTihzdHJpcEhleFByZWZpeCh0eERhdGEuZ2FzUHJpY2UhLnRvU3RyaW5nKCkpLCAnaGV4JyksXG4gICAgICAgIH0sXG4gICAgICAgIHsgY29tbW9uOiBldGhDb21tb24gfVxuICAgICAgKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZmluYWxUeDtcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgYSBmdW5kcyByZWNvdmVyeSB0cmFuc2FjdGlvbiB3aXRob3V0IEJpdEdvXG4gICAqIEBwYXJhbSBwYXJhbXNcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy51c2VyS2V5IC0gW2VuY3J5cHRlZF0geHBydlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmJhY2t1cEtleSAtIFtlbmNyeXB0ZWRdIHhwcnYgb3IgeHB1YiBpZiB0aGUgeHBydiBpcyBoZWxkIGJ5IGEgS1JTIHByb3ZpZGVyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMud2FsbGV0UGFzc3BocmFzZSAtIHVzZWQgdG8gZGVjcnlwdCB1c2VyS2V5IGFuZCBiYWNrdXBLZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MgLSB0aGUgRVRIIGFkZHJlc3Mgb2YgdGhlIHdhbGxldCBjb250cmFjdFxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmtyc1Byb3ZpZGVyIC0gbmVjZXNzYXJ5IGlmIGJhY2t1cCBrZXkgaXMgaGVsZCBieSBLUlNcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uIC0gdGFyZ2V0IGFkZHJlc3MgdG8gc2VuZCByZWNvdmVyZWQgZnVuZHMgdG9cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iaXRnb0ZlZUFkZHJlc3MgLSB3cm9uZyBjaGFpbiB3YWxsZXQgZmVlIGFkZHJlc3MgZm9yIGV2bSBiYXNlZCBjcm9zcyBjaGFpbiByZWNvdmVyeSB0eG5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcyAtIHRhcmdldCBiaXRnbyBhZGRyZXNzIHdoZXJlIGZlZSB3aWxsIGJlIHNlbnQgZm9yIGV2bSBiYXNlZCBjcm9zcyBjaGFpbiByZWNvdmVyeSB0eG5cbiAgICovXG4gIGFzeW5jIHJlY292ZXIocGFyYW1zOiBSZWNvdmVyT3B0aW9ucyk6IFByb21pc2U8UmVjb3ZlcnlJbmZvIHwgT2ZmbGluZVZhdWx0VHhJbmZvIHwgVW5zaWduZWRTd2VlcFR4TVBDdjI+IHtcbiAgICBpZiAocGFyYW1zLmlzVHNzID09PSB0cnVlKSB7XG4gICAgICByZXR1cm4gdGhpcy5yZWNvdmVyVFNTKHBhcmFtcyk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnJlY292ZXJFdGhMaWtlKHBhcmFtcyk7XG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIGEgZnVuZHMgcmVjb3ZlcnkgdHJhbnNhY3Rpb24gd2l0aG91dCBCaXRHbyBmb3Igbm9uLVRTUyB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0gcGFyYW1zXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMudXNlcktleSBbZW5jcnlwdGVkXSB4cHJ2IG9yIHhwdWJcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iYWNrdXBLZXkgW2VuY3J5cHRlZF0geHBydiBvciB4cHViIGlmIHRoZSB4cHJ2IGlzIGhlbGQgYnkgYSBLUlMgcHJvdmlkZXJcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlIHVzZWQgdG8gZGVjcnlwdCB1c2VyS2V5IGFuZCBiYWNrdXBLZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MgdGhlIEV0aExpa2UgYWRkcmVzcyBvZiB0aGUgd2FsbGV0IGNvbnRyYWN0XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMua3JzUHJvdmlkZXIgbmVjZXNzYXJ5IGlmIGJhY2t1cCBrZXkgaXMgaGVsZCBieSBLUlNcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uIHRhcmdldCBhZGRyZXNzIHRvIHNlbmQgcmVjb3ZlcmVkIGZ1bmRzIHRvXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuYml0Z29GZWVBZGRyZXNzIHdyb25nIGNoYWluIHdhbGxldCBmZWUgYWRkcmVzcyBmb3IgZXZtIGJhc2VkIGNyb3NzIGNoYWluIHJlY292ZXJ5IHR4blxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmJpdGdvRGVzdGluYXRpb25BZGRyZXNzIHRhcmdldCBiaXRnbyBhZGRyZXNzIHdoZXJlIGZlZSB3aWxsIGJlIHNlbnQgZm9yIGV2bSBiYXNlZCBjcm9zcyBjaGFpbiByZWNvdmVyeSB0eG5cbiAgICogQHJldHVybnMge1Byb21pc2U8UmVjb3ZlcnlJbmZvIHwgT2ZmbGluZVZhdWx0VHhJbmZvPn1cbiAgICovXG4gIHByb3RlY3RlZCBhc3luYyByZWNvdmVyRXRoTGlrZShwYXJhbXM6IFJlY292ZXJPcHRpb25zKTogUHJvbWlzZTxSZWNvdmVyeUluZm8gfCBPZmZsaW5lVmF1bHRUeEluZm8+IHtcbiAgICAvLyBiaXRnb0ZlZUFkZHJlc3MgaXMgb25seSBkZWZpbmVkIHdoZW4gaXQgaXMgYSBldm0gY3Jvc3MgY2hhaW4gcmVjb3ZlcnlcbiAgICAvLyBhcyB3ZSB1c2UgZmVlIGZyb20gdGhpcyB3cm9uZyBjaGFpbiBhZGRyZXNzIGZvciB0aGUgcmVjb3ZlcnkgdHhuIG9uIHRoZSBjb3JyZWN0IGNoYWluLlxuICAgIGlmIChwYXJhbXMuYml0Z29GZWVBZGRyZXNzKSB7XG4gICAgICByZXR1cm4gdGhpcy5yZWNvdmVyRXRoTGlrZWZvckV2bUJhc2VkUmVjb3ZlcnkocGFyYW1zKTtcbiAgICB9XG5cbiAgICB0aGlzLnZhbGlkYXRlUmVjb3ZlcnlQYXJhbXMocGFyYW1zKTtcbiAgICBjb25zdCBpc1Vuc2lnbmVkU3dlZXAgPSBnZXRJc1Vuc2lnbmVkU3dlZXAocGFyYW1zKTtcblxuICAgIC8vIENsZWFuIHVwIHdoaXRlc3BhY2UgZnJvbSBlbnRlcmVkIHZhbHVlc1xuICAgIGxldCB1c2VyS2V5ID0gcGFyYW1zLnVzZXJLZXkucmVwbGFjZSgvXFxzL2csICcnKTtcbiAgICBjb25zdCBiYWNrdXBLZXkgPSBwYXJhbXMuYmFja3VwS2V5LnJlcGxhY2UoL1xccy9nLCAnJyk7XG4gICAgY29uc3QgZ2FzTGltaXQgPSBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4odGhpcy5zZXRHYXNMaW1pdChwYXJhbXMuZ2FzTGltaXQpKTtcbiAgICBjb25zdCBnYXNQcmljZSA9IHBhcmFtcy5laXAxNTU5XG4gICAgICA/IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihwYXJhbXMuZWlwMTU1OS5tYXhGZWVQZXJHYXMpXG4gICAgICA6IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTih0aGlzLnNldEdhc1ByaWNlKHBhcmFtcy5nYXNQcmljZSkpO1xuXG4gICAgaWYgKCF1c2VyS2V5LnN0YXJ0c1dpdGgoJ3hwdWInKSAmJiAhdXNlcktleS5zdGFydHNXaXRoKCd4cHJ2JykpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHVzZXJLZXkgPSB0aGlzLmJpdGdvLmRlY3J5cHQoe1xuICAgICAgICAgIGlucHV0OiB1c2VyS2V5LFxuICAgICAgICAgIHBhc3N3b3JkOiBwYXJhbXMud2FsbGV0UGFzc3BocmFzZSxcbiAgICAgICAgfSk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgZGVjcnlwdGluZyB1c2VyIGtleWNoYWluOiAke2UubWVzc2FnZX1gKTtcbiAgICAgIH1cbiAgICB9XG4gICAgbGV0IGJhY2t1cEtleUFkZHJlc3M7XG4gICAgbGV0IGJhY2t1cFNpZ25pbmdLZXk7XG4gICAgaWYgKGlzVW5zaWduZWRTd2VlcCkge1xuICAgICAgY29uc3QgYmFja3VwSEROb2RlID0gYmlwMzIuZnJvbUJhc2U1OChiYWNrdXBLZXkpO1xuICAgICAgYmFja3VwU2lnbmluZ0tleSA9IGJhY2t1cEhETm9kZS5wdWJsaWNLZXk7XG4gICAgICBiYWNrdXBLZXlBZGRyZXNzID0gYDB4JHtvcHRpb25hbERlcHMuZXRoVXRpbC5wdWJsaWNUb0FkZHJlc3MoYmFja3VwU2lnbmluZ0tleSwgdHJ1ZSkudG9TdHJpbmcoJ2hleCcpfWA7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIERlY3J5cHQgYmFja3VwIHByaXZhdGUga2V5IGFuZCBnZXQgYWRkcmVzc1xuICAgICAgbGV0IGJhY2t1cFBydjtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgYmFja3VwUHJ2ID0gdGhpcy5iaXRnby5kZWNyeXB0KHtcbiAgICAgICAgICBpbnB1dDogYmFja3VwS2V5LFxuICAgICAgICAgIHBhc3N3b3JkOiBwYXJhbXMud2FsbGV0UGFzc3BocmFzZSxcbiAgICAgICAgfSk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgZGVjcnlwdGluZyBiYWNrdXAga2V5Y2hhaW46ICR7ZS5tZXNzYWdlfWApO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBrZXlQYWlyID0gbmV3IEtleVBhaXJMaWIoeyBwcnY6IGJhY2t1cFBydiB9KTtcbiAgICAgIGJhY2t1cFNpZ25pbmdLZXkgPSBrZXlQYWlyLmdldEtleXMoKS5wcnY7XG4gICAgICBpZiAoIWJhY2t1cFNpZ25pbmdLZXkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdubyBwcml2YXRlIGtleScpO1xuICAgICAgfVxuICAgICAgYmFja3VwS2V5QWRkcmVzcyA9IGtleVBhaXIuZ2V0QWRkcmVzcygpO1xuICAgIH1cblxuICAgIGNvbnN0IGJhY2t1cEtleU5vbmNlID0gYXdhaXQgdGhpcy5nZXRBZGRyZXNzTm9uY2UoYmFja3VwS2V5QWRkcmVzcyk7XG4gICAgLy8gZ2V0IGJhbGFuY2Ugb2YgYmFja3VwS2V5IHRvIGVuc3VyZSBmdW5kcyBhcmUgYXZhaWxhYmxlIHRvIHBheSBmZWVzXG4gICAgY29uc3QgYmFja3VwS2V5QmFsYW5jZSA9IGF3YWl0IHRoaXMucXVlcnlBZGRyZXNzQmFsYW5jZShiYWNrdXBLZXlBZGRyZXNzKTtcbiAgICBsZXQgdG90YWxHYXNOZWVkZWQgPSBnYXNQcmljZS5tdWwoZ2FzTGltaXQpO1xuXG4gICAgLy8gT24gb3B0aW1pc20gY2hhaW4sIEwxIGZlZXMgaXMgdG8gYmUgcGFpZCBhcyB3ZWxsIGFwYXJ0IGZyb20gTDIgZmVlc1xuICAgIC8vIFNvIHdlIGFyZSBhZGRpbmcgdGhlIGFtb3VudCB0aGF0IGNhbiBiZSB1c2VkIHVwIGFzIGwxIGZlZXNcbiAgICBpZiAodGhpcy5zdGF0aWNzQ29pbj8uZmFtaWx5ID09PSAnb3BldGgnKSB7XG4gICAgICB0b3RhbEdhc05lZWRlZCA9IHRvdGFsR2FzTmVlZGVkLmFkZChuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4oZXRoR2FzQ29uZmlncy5vcGV0aEdhc0wxRmVlcykpO1xuICAgIH1cblxuICAgIGNvbnN0IHdlaVRvR3dlaSA9IDEwICoqIDk7XG4gICAgaWYgKGJhY2t1cEtleUJhbGFuY2UubHQodG90YWxHYXNOZWVkZWQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBCYWNrdXAga2V5IGFkZHJlc3MgJHtiYWNrdXBLZXlBZGRyZXNzfSBoYXMgYmFsYW5jZSAkeyhiYWNrdXBLZXlCYWxhbmNlIC8gd2VpVG9Hd2VpKS50b1N0cmluZygpfSBHd2VpLmAgK1xuICAgICAgICAgIGBUaGlzIGFkZHJlc3MgbXVzdCBoYXZlIGEgYmFsYW5jZSBvZiBhdCBsZWFzdCAkeyh0b3RhbEdhc05lZWRlZCAvIHdlaVRvR3dlaSkudG9TdHJpbmcoKX1gICtcbiAgICAgICAgICBgIEd3ZWkgdG8gcGVyZm9ybSByZWNvdmVyaWVzLiBUcnkgc2VuZGluZyBzb21lIGZ1bmRzIHRvIHRoaXMgYWRkcmVzcyB0aGVuIHJldHJ5LmBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gZ2V0IGJhbGFuY2Ugb2Ygd2FsbGV0XG4gICAgY29uc3QgdHhBbW91bnQgPSBhd2FpdCB0aGlzLnF1ZXJ5QWRkcmVzc0JhbGFuY2UocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyk7XG4gICAgaWYgKG5ldyBCaWdOdW1iZXIodHhBbW91bnQpLmlzTGVzc1RoYW5PckVxdWFsVG8oMCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignV2FsbGV0IGRvZXMgbm90IGhhdmUgZW5vdWdoIGZ1bmRzIHRvIHJlY292ZXInKTtcbiAgICB9XG5cbiAgICAvLyBidWlsZCByZWNpcGllbnRzIG9iamVjdFxuICAgIGNvbnN0IHJlY2lwaWVudHMgPSBbXG4gICAgICB7XG4gICAgICAgIGFkZHJlc3M6IHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uLFxuICAgICAgICBhbW91bnQ6IHR4QW1vdW50LnRvU3RyaW5nKDEwKSxcbiAgICAgIH0sXG4gICAgXTtcblxuICAgIC8vIEdldCBzZXF1ZW5jZSBJRCB1c2luZyBjb250cmFjdCBjYWxsXG4gICAgLy8gd2UgbmVlZCB0byB3YWl0IGJldHdlZW4gbWFraW5nIHR3byBleHBsb3JlciBhcGkgY2FsbHMgdG8gYXZvaWQgZ2V0dGluZyBiYW5uZWRcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCAxMDAwKSk7XG4gICAgY29uc3Qgc2VxdWVuY2VJZCA9IGF3YWl0IHRoaXMucXVlcnlTZXF1ZW5jZUlkKHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MpO1xuXG4gICAgbGV0IG9wZXJhdGlvbkhhc2gsIHNpZ25hdHVyZTtcbiAgICAvLyBHZXQgb3BlcmF0aW9uIGhhc2ggYW5kIHNpZ24gaXRcbiAgICBpZiAoIWlzVW5zaWduZWRTd2VlcCkge1xuICAgICAgb3BlcmF0aW9uSGFzaCA9IHRoaXMuZ2V0T3BlcmF0aW9uU2hhM0ZvckV4ZWN1dGVBbmRDb25maXJtKHJlY2lwaWVudHMsIHRoaXMuZ2V0RGVmYXVsdEV4cGlyZVRpbWUoKSwgc2VxdWVuY2VJZCk7XG4gICAgICBzaWduYXR1cmUgPSBVdGlsLmV0aFNpZ25Nc2dIYXNoKG9wZXJhdGlvbkhhc2gsIFV0aWwueHBydlRvRXRoUHJpdmF0ZUtleSh1c2VyS2V5KSk7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIFV0aWwuZWNSZWNvdmVyRXRoQWRkcmVzcyhvcGVyYXRpb25IYXNoLCBzaWduYXR1cmUpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgc2lnbmF0dXJlJyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgdHhJbmZvID0ge1xuICAgICAgcmVjaXBpZW50OiByZWNpcGllbnRzWzBdLFxuICAgICAgZXhwaXJlVGltZTogdGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpLFxuICAgICAgY29udHJhY3RTZXF1ZW5jZUlkOiBzZXF1ZW5jZUlkLFxuICAgICAgb3BlcmF0aW9uSGFzaDogb3BlcmF0aW9uSGFzaCxcbiAgICAgIHNpZ25hdHVyZTogc2lnbmF0dXJlLFxuICAgICAgZ2FzTGltaXQ6IGdhc0xpbWl0LnRvU3RyaW5nKDEwKSxcbiAgICB9O1xuXG4gICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIocGFyYW1zLmNvbW1vbikgYXMgVHJhbnNhY3Rpb25CdWlsZGVyO1xuICAgIHR4QnVpbGRlci5jb3VudGVyKGJhY2t1cEtleU5vbmNlKTtcbiAgICB0eEJ1aWxkZXIuY29udHJhY3QocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyk7XG4gICAgbGV0IHR4RmVlO1xuICAgIGlmIChwYXJhbXMuZWlwMTU1OSkge1xuICAgICAgdHhGZWUgPSB7XG4gICAgICAgIGVpcDE1NTk6IHtcbiAgICAgICAgICBtYXhQcmlvcml0eUZlZVBlckdhczogcGFyYW1zLmVpcDE1NTkubWF4UHJpb3JpdHlGZWVQZXJHYXMsXG4gICAgICAgICAgbWF4RmVlUGVyR2FzOiBwYXJhbXMuZWlwMTU1OS5tYXhGZWVQZXJHYXMsXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgIH0gZWxzZSB7XG4gICAgICB0eEZlZSA9IHsgZmVlOiBnYXNQcmljZS50b1N0cmluZygpIH07XG4gICAgfVxuICAgIHR4QnVpbGRlci5mZWUoe1xuICAgICAgLi4udHhGZWUsXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoKSxcbiAgICB9KTtcbiAgICBjb25zdCB0cmFuc2ZlckJ1aWxkZXIgPSB0eEJ1aWxkZXIudHJhbnNmZXIoKSBhcyBUcmFuc2ZlckJ1aWxkZXI7XG4gICAgdHJhbnNmZXJCdWlsZGVyXG4gICAgICAuY29pbih0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZylcbiAgICAgIC5hbW91bnQocmVjaXBpZW50c1swXS5hbW91bnQpXG4gICAgICAuY29udHJhY3RTZXF1ZW5jZUlkKHNlcXVlbmNlSWQpXG4gICAgICAuZXhwaXJhdGlvblRpbWUodGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpKVxuICAgICAgLnRvKHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uKTtcblxuICAgIGNvbnN0IHR4ID0gYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCk7XG4gICAgaWYgKGlzVW5zaWduZWRTd2VlcCkge1xuICAgICAgY29uc3QgcmVzcG9uc2U6IE9mZmxpbmVWYXVsdFR4SW5mbyA9IHtcbiAgICAgICAgdHhIZXg6IHR4LnRvQnJvYWRjYXN0Rm9ybWF0KCksXG4gICAgICAgIHVzZXJLZXksXG4gICAgICAgIGJhY2t1cEtleSxcbiAgICAgICAgY29pbjogdGhpcy5nZXRDaGFpbigpLFxuICAgICAgICBnYXNQcmljZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKSxcbiAgICAgICAgZ2FzTGltaXQsXG4gICAgICAgIHJlY2lwaWVudHM6IFt0eEluZm8ucmVjaXBpZW50XSxcbiAgICAgICAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiB0eC50b0pzb24oKS50byxcbiAgICAgICAgYW1vdW50OiB0eEluZm8ucmVjaXBpZW50LmFtb3VudCxcbiAgICAgICAgYmFja3VwS2V5Tm9uY2UsXG4gICAgICAgIGVpcDE1NTk6IHBhcmFtcy5laXAxNTU5LFxuICAgICAgfTtcbiAgICAgIF8uZXh0ZW5kKHJlc3BvbnNlLCB0eEluZm8pO1xuICAgICAgcmVzcG9uc2UubmV4dENvbnRyYWN0U2VxdWVuY2VJZCA9IHJlc3BvbnNlLmNvbnRyYWN0U2VxdWVuY2VJZDtcbiAgICAgIHJldHVybiByZXNwb25zZTtcbiAgICB9XG5cbiAgICB0eEJ1aWxkZXJcbiAgICAgIC50cmFuc2ZlcigpXG4gICAgICAuY29pbih0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZylcbiAgICAgIC5rZXkobmV3IEtleVBhaXJMaWIoeyBwcnY6IHVzZXJLZXkgfSkuZ2V0S2V5cygpLnBydiBhcyBzdHJpbmcpO1xuICAgIHR4QnVpbGRlci5zaWduKHsga2V5OiBiYWNrdXBTaWduaW5nS2V5IH0pO1xuXG4gICAgY29uc3Qgc2lnbmVkVHggPSBhd2FpdCB0eEJ1aWxkZXIuYnVpbGQoKTtcblxuICAgIHJldHVybiB7XG4gICAgICBpZDogc2lnbmVkVHgudG9Kc29uKCkuaWQsXG4gICAgICB0eDogc2lnbmVkVHgudG9Ccm9hZGNhc3RGb3JtYXQoKSxcbiAgICB9O1xuICB9XG5cbiAgYXN5bmMgc2VuZENyb3NzQ2hhaW5SZWNvdmVyeVRyYW5zYWN0aW9uKFxuICAgIHBhcmFtczogU2VuZENyb3NzQ2hhaW5SZWNvdmVyeU9wdGlvbnNcbiAgKTogUHJvbWlzZTx7IGNvaW46IHN0cmluZzsgdHhIZXg/OiBzdHJpbmc7IHR4aWQ6IHN0cmluZyB9PiB7XG4gICAgY29uc3QgYnVpbGRSZXNwb25zZSA9IGF3YWl0IHRoaXMuYnVpbGRDcm9zc0NoYWluUmVjb3ZlcnlUcmFuc2FjdGlvbihwYXJhbXMucmVjb3ZlcnlJZCk7XG4gICAgaWYgKHBhcmFtcy53YWxsZXRUeXBlID09PSAnY29sZCcpIHtcbiAgICAgIHJldHVybiBidWlsZFJlc3BvbnNlO1xuICAgIH1cbiAgICBpZiAoIXBhcmFtcy5lbmNyeXB0ZWRQcnYpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyBlbmNyeXB0ZWRQcnYnKTtcbiAgICB9XG5cbiAgICBsZXQgdXNlcktleVBydjtcbiAgICB0cnkge1xuICAgICAgdXNlcktleVBydiA9IHRoaXMuYml0Z28uZGVjcnlwdCh7XG4gICAgICAgIGlucHV0OiBwYXJhbXMuZW5jcnlwdGVkUHJ2LFxuICAgICAgICBwYXNzd29yZDogcGFyYW1zLndhbGxldFBhc3NwaHJhc2UsXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEVycm9yIGRlY3J5cHRpbmcgdXNlciBrZXljaGFpbjogJHtlLm1lc3NhZ2V9YCk7XG4gICAgfVxuICAgIGNvbnN0IGtleVBhaXIgPSBuZXcgS2V5UGFpckxpYih7IHBydjogdXNlcktleVBydiB9KTtcbiAgICBjb25zdCB1c2VyU2lnbmluZ0tleSA9IGtleVBhaXIuZ2V0S2V5cygpLnBydjtcbiAgICBpZiAoIXVzZXJTaWduaW5nS2V5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ25vIHByaXZhdGUga2V5Jyk7XG4gICAgfVxuXG4gICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIocGFyYW1zLmNvbW1vbikgYXMgVHJhbnNhY3Rpb25CdWlsZGVyO1xuICAgIGNvbnN0IHR4SGV4ID0gYnVpbGRSZXNwb25zZS50eEhleDtcbiAgICB0eEJ1aWxkZXIuZnJvbSh0eEhleCk7XG4gICAgaWYgKGJ1aWxkUmVzcG9uc2Uud2FsbGV0VmVyc2lvbikge1xuICAgICAgLy8gSWYgd2FsbGV0VmVyc2lvbiBpcyBwcm92aWRlZCwgc2V0IGl0IGluIHRoZSB0eEJ1aWxkZXJcbiAgICAgIHR4QnVpbGRlci53YWxsZXRWZXJzaW9uKGJ1aWxkUmVzcG9uc2Uud2FsbGV0VmVyc2lvbik7XG4gICAgfVxuICAgIHR4QnVpbGRlclxuICAgICAgLnRyYW5zZmVyKClcbiAgICAgIC5jb2luKHRoaXMuc3RhdGljc0NvaW4/Lm5hbWUgYXMgc3RyaW5nKVxuICAgICAgLmtleSh1c2VyU2lnbmluZ0tleSk7XG4gICAgY29uc3QgdHggPSBhd2FpdCB0eEJ1aWxkZXIuYnVpbGQoKTtcbiAgICBjb25zdCByZXMgPSBhd2FpdCB0aGlzLmJpdGdvXG4gICAgICAucG9zdCh0aGlzLmJpdGdvLm1pY3Jvc2VydmljZXNVcmwoYC9hcGkvcmVjb3ZlcnkvdjEvY3Jvc3NjaGFpbi8ke3BhcmFtcy5yZWNvdmVyeUlkfS9zaWduYCkpXG4gICAgICAuc2VuZCh7IHR4SGV4OiB0eC50b0Jyb2FkY2FzdEZvcm1hdCgpIH0pO1xuICAgIHJldHVybiB7XG4gICAgICBjb2luOiB0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZyxcbiAgICAgIHR4aWQ6IHJlcy5ib2R5LnR4aWQsXG4gICAgfTtcbiAgfVxuXG4gIGFzeW5jIGJ1aWxkQ3Jvc3NDaGFpblJlY292ZXJ5VHJhbnNhY3Rpb24oXG4gICAgcmVjb3ZlcnlJZDogc3RyaW5nXG4gICk6IFByb21pc2U8eyBjb2luOiBzdHJpbmc7IHR4SGV4OiBzdHJpbmc7IHR4aWQ6IHN0cmluZzsgd2FsbGV0VmVyc2lvbj86IG51bWJlciB9PiB7XG4gICAgY29uc3QgcmVzID0gYXdhaXQgdGhpcy5iaXRnby5nZXQodGhpcy5iaXRnby5taWNyb3NlcnZpY2VzVXJsKGAvYXBpL3JlY292ZXJ5L3YxL2Nyb3NzY2hhaW4vJHtyZWNvdmVyeUlkfS9idWlsZHR4YCkpO1xuICAgIHJldHVybiB7XG4gICAgICBjb2luOiByZXMuYm9keS5jb2luLFxuICAgICAgdHhIZXg6IHJlcy5ib2R5LnR4SGV4LFxuICAgICAgdHhpZDogcmVzLmJvZHkudHhpZCxcbiAgICAgIHdhbGxldFZlcnNpb246IHJlcy5ib2R5LndhbGxldFZlcnNpb24sXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgYSB1bnNpZ25lZCAoZm9yIGNvbGQsIGN1c3RvZHkgd2FsbGV0KSBvclxuICAgKiBoYWxmLXNpZ25lZCAoZm9yIGhvdCB3YWxsZXQpIGV2bSBjcm9zcyBjaGFpbiByZWNvdmVyeSB0cmFuc2FjdGlvbiB3aXRoXG4gICAqIHNhbWUgZXhwZWN0ZWQgYXJndW1lbnRzIGFzIHJlY292ZXIgbWV0aG9kLlxuICAgKiBUaGlzIGhlbHBzIHJlY292ZXIgZnVuZHMgZnJvbSBldm0gYmFzZWQgd3JvbmcgY2hhaW4uXG4gICAqIEBwYXJhbSB7UmVjb3Zlck9wdGlvbnN9IHBhcmFtc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxSZWNvdmVyeUluZm8gfCBPZmZsaW5lVmF1bHRUeEluZm8+fVxuICAgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIHJlY292ZXJFdGhMaWtlZm9yRXZtQmFzZWRSZWNvdmVyeShcbiAgICBwYXJhbXM6IFJlY292ZXJPcHRpb25zXG4gICk6IFByb21pc2U8UmVjb3ZlcnlJbmZvIHwgT2ZmbGluZVZhdWx0VHhJbmZvPiB7XG4gICAgdGhpcy52YWxpZGF0ZUV2bUJhc2VkUmVjb3ZlcnlQYXJhbXMocGFyYW1zKTtcblxuICAgIC8vIENsZWFuIHVwIHdoaXRlc3BhY2UgZnJvbSBlbnRlcmVkIHZhbHVlc1xuICAgIGNvbnN0IHVzZXJLZXkgPSBwYXJhbXMudXNlcktleS5yZXBsYWNlKC9cXHMvZywgJycpO1xuICAgIGNvbnN0IGJpdGdvRmVlQWRkcmVzcyA9IHBhcmFtcy5iaXRnb0ZlZUFkZHJlc3M/LnJlcGxhY2UoL1xccy9nLCAnJykudG9Mb3dlckNhc2UoKSBhcyBzdHJpbmc7XG4gICAgY29uc3QgYml0Z29EZXN0aW5hdGlvbkFkZHJlc3MgPSBwYXJhbXMuYml0Z29EZXN0aW5hdGlvbkFkZHJlc3M/LnJlcGxhY2UoL1xccy9nLCAnJykudG9Mb3dlckNhc2UoKSBhcyBzdHJpbmc7XG4gICAgY29uc3QgcmVjb3ZlcnlEZXN0aW5hdGlvbiA9IHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uPy5yZXBsYWNlKC9cXHMvZywgJycpLnRvTG93ZXJDYXNlKCkgYXMgc3RyaW5nO1xuICAgIGNvbnN0IHdhbGxldENvbnRyYWN0QWRkcmVzcyA9IHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3M/LnJlcGxhY2UoL1xccy9nLCAnJykudG9Mb3dlckNhc2UoKSBhcyBzdHJpbmc7XG4gICAgY29uc3QgdG9rZW5Db250cmFjdEFkZHJlc3MgPSBwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3M/LnJlcGxhY2UoL1xccy9nLCAnJykudG9Mb3dlckNhc2UoKSBhcyBzdHJpbmc7XG5cbiAgICBsZXQgdXNlclNpZ25pbmdLZXk7XG4gICAgbGV0IHVzZXJLZXlQcnY7XG4gICAgaWYgKHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKSB7XG4gICAgICBpZiAoIXVzZXJLZXkuc3RhcnRzV2l0aCgneHB1YicpICYmICF1c2VyS2V5LnN0YXJ0c1dpdGgoJ3hwcnYnKSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIHVzZXJLZXlQcnYgPSB0aGlzLmJpdGdvLmRlY3J5cHQoe1xuICAgICAgICAgICAgaW5wdXQ6IHVzZXJLZXksXG4gICAgICAgICAgICBwYXNzd29yZDogcGFyYW1zLndhbGxldFBhc3NwaHJhc2UsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEVycm9yIGRlY3J5cHRpbmcgdXNlciBrZXljaGFpbjogJHtlLm1lc3NhZ2V9YCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgY29uc3Qga2V5UGFpciA9IG5ldyBLZXlQYWlyTGliKHsgcHJ2OiB1c2VyS2V5UHJ2IH0pO1xuICAgICAgdXNlclNpZ25pbmdLZXkgPSBrZXlQYWlyLmdldEtleXMoKS5wcnY7XG4gICAgICBpZiAoIXVzZXJTaWduaW5nS2V5KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignbm8gcHJpdmF0ZSBrZXknKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBVc2UgZGVmYXVsdCBnYXNMaW1pdCBmb3IgY29sZCBhbmQgY3VzdG9keSB3YWxsZXRzXG4gICAgbGV0IGdhc0xpbWl0ID1cbiAgICAgIHBhcmFtcy5nYXNMaW1pdCB8fCB1c2VyS2V5LnN0YXJ0c1dpdGgoJ3hwdWInKSB8fCAhdXNlcktleVxuICAgICAgICA/IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTih0aGlzLnNldEdhc0xpbWl0KHBhcmFtcy5nYXNMaW1pdCkpXG4gICAgICAgIDogbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKDApO1xuXG4gICAgY29uc3QgZ2FzUHJpY2UgPSBwYXJhbXMuZWlwMTU1OVxuICAgICAgPyBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocGFyYW1zLmVpcDE1NTkubWF4RmVlUGVyR2FzKVxuICAgICAgOiBwYXJhbXMuZ2FzUHJpY2VcbiAgICAgID8gbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHRoaXMuc2V0R2FzUHJpY2UocGFyYW1zLmdhc1ByaWNlKSlcbiAgICAgIDogYXdhaXQgdGhpcy5nZXRHYXNQcmljZUZyb21FeHRlcm5hbEFQSSh0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZyk7XG5cbiAgICBjb25zdCBiaXRnb0ZlZUFkZHJlc3NOb25jZSA9IGF3YWl0IHRoaXMuZ2V0QWRkcmVzc05vbmNlKGJpdGdvRmVlQWRkcmVzcyk7XG5cbiAgICBpZiAodG9rZW5Db250cmFjdEFkZHJlc3MpIHtcbiAgICAgIHJldHVybiB0aGlzLnJlY292ZXJFdGhMaWtlVG9rZW5mb3JFdm1CYXNlZFJlY292ZXJ5KFxuICAgICAgICBwYXJhbXMsXG4gICAgICAgIGJpdGdvRmVlQWRkcmVzc05vbmNlLFxuICAgICAgICBnYXNMaW1pdCxcbiAgICAgICAgZ2FzUHJpY2UsXG4gICAgICAgIHVzZXJLZXksXG4gICAgICAgIHVzZXJTaWduaW5nS2V5XG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIGdldCBiYWxhbmNlIG9mIHdhbGxldFxuICAgIGNvbnN0IHR4QW1vdW50ID0gYXdhaXQgdGhpcy5xdWVyeUFkZHJlc3NCYWxhbmNlKHdhbGxldENvbnRyYWN0QWRkcmVzcyk7XG5cbiAgICBjb25zdCBiaXRnb0ZlZVBlcmNlbnRhZ2UgPSAwOyAvLyBUT0RPOiBCRy03MTkxMiBjYW4gY2hhbmdlIHRoZSBmZWUlIGhlcmUuXG4gICAgY29uc3QgYml0Z29GZWVBbW91bnQgPSB0eEFtb3VudCAqIChiaXRnb0ZlZVBlcmNlbnRhZ2UgLyAxMDApO1xuXG4gICAgLy8gYnVpbGQgcmVjaXBpZW50cyBvYmplY3RcbiAgICBjb25zdCByZWNpcGllbnRzOiBSZWNpcGllbnRbXSA9IFtcbiAgICAgIHtcbiAgICAgICAgYWRkcmVzczogcmVjb3ZlcnlEZXN0aW5hdGlvbixcbiAgICAgICAgYW1vdW50OiBuZXcgQmlnTnVtYmVyKHR4QW1vdW50KS5taW51cyhiaXRnb0ZlZUFtb3VudCkudG9GaXhlZCgpLFxuICAgICAgfSxcbiAgICBdO1xuXG4gICAgaWYgKGJpdGdvRmVlUGVyY2VudGFnZSA+IDApIHtcbiAgICAgIGlmIChfLmlzVW5kZWZpbmVkKGJpdGdvRGVzdGluYXRpb25BZGRyZXNzKSB8fCAhdGhpcy5pc1ZhbGlkQWRkcmVzcyhiaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcykpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkIGJpdGdvRGVzdGluYXRpb25BZGRyZXNzJyk7XG4gICAgICB9XG5cbiAgICAgIHJlY2lwaWVudHMucHVzaCh7XG4gICAgICAgIGFkZHJlc3M6IGJpdGdvRGVzdGluYXRpb25BZGRyZXNzLFxuICAgICAgICBhbW91bnQ6IGJpdGdvRmVlQW1vdW50LnRvU3RyaW5nKDEwKSxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIGNhbGN1bGF0ZSBiYXRjaCBkYXRhXG4gICAgY29uc3QgQkFUQ0hfTUVUSE9EX05BTUUgPSAnYmF0Y2gnO1xuICAgIGNvbnN0IEJBVENIX01FVEhPRF9UWVBFUyA9IFsnYWRkcmVzc1tdJywgJ3VpbnQyNTZbXSddO1xuICAgIGNvbnN0IGJhdGNoRXhlY3V0aW9uSW5mbyA9IHRoaXMuZ2V0QmF0Y2hFeGVjdXRpb25JbmZvKHJlY2lwaWVudHMpO1xuICAgIGNvbnN0IGJhdGNoRGF0YSA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmFkZEhleFByZWZpeChcbiAgICAgIHRoaXMuZ2V0TWV0aG9kQ2FsbERhdGEoQkFUQ0hfTUVUSE9EX05BTUUsIEJBVENIX01FVEhPRF9UWVBFUywgYmF0Y2hFeGVjdXRpb25JbmZvLnZhbHVlcykudG9TdHJpbmcoJ2hleCcpXG4gICAgKTtcblxuICAgIC8vIEdldCBzZXF1ZW5jZSBJRCB1c2luZyBjb250cmFjdCBjYWxsXG4gICAgLy8gd2UgbmVlZCB0byB3YWl0IGJldHdlZW4gbWFraW5nIHR3byBleHBsb3JlciBhcGkgY2FsbHMgdG8gYXZvaWQgZ2V0dGluZyBiYW5uZWRcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCAxMDAwKSk7XG4gICAgY29uc3Qgc2VxdWVuY2VJZCA9IGF3YWl0IHRoaXMucXVlcnlTZXF1ZW5jZUlkKHdhbGxldENvbnRyYWN0QWRkcmVzcyk7XG5cbiAgICBjb25zdCBuZXR3b3JrID0gdGhpcy5nZXROZXR3b3JrKCk7XG4gICAgY29uc3QgYmF0Y2hlckNvbnRyYWN0QWRkcmVzcyA9IG5ldHdvcms/LmJhdGNoZXJDb250cmFjdEFkZHJlc3MgYXMgc3RyaW5nO1xuXG4gICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIocGFyYW1zLmNvbW1vbikgYXMgVHJhbnNhY3Rpb25CdWlsZGVyO1xuICAgIHR4QnVpbGRlci5jb3VudGVyKGJpdGdvRmVlQWRkcmVzc05vbmNlKTtcbiAgICB0eEJ1aWxkZXIuY29udHJhY3Qod2FsbGV0Q29udHJhY3RBZGRyZXNzKTtcbiAgICBsZXQgdHhGZWU7XG4gICAgaWYgKHBhcmFtcy5laXAxNTU5KSB7XG4gICAgICB0eEZlZSA9IHtcbiAgICAgICAgZWlwMTU1OToge1xuICAgICAgICAgIG1heFByaW9yaXR5RmVlUGVyR2FzOiBwYXJhbXMuZWlwMTU1OS5tYXhQcmlvcml0eUZlZVBlckdhcyxcbiAgICAgICAgICBtYXhGZWVQZXJHYXM6IHBhcmFtcy5laXAxNTU5Lm1heEZlZVBlckdhcyxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIHR4RmVlID0geyBmZWU6IGdhc1ByaWNlLnRvU3RyaW5nKCkgfTtcbiAgICB9XG4gICAgdHhCdWlsZGVyLmZlZSh7XG4gICAgICAuLi50eEZlZSxcbiAgICAgIGdhc0xpbWl0OiBnYXNMaW1pdC50b1N0cmluZygpLFxuICAgIH0pO1xuXG4gICAgY29uc3QgdHJhbnNmZXJCdWlsZGVyID0gdHhCdWlsZGVyLnRyYW5zZmVyKCkgYXMgVHJhbnNmZXJCdWlsZGVyO1xuICAgIGlmICghYmF0Y2hlckNvbnRyYWN0QWRkcmVzcykge1xuICAgICAgdHJhbnNmZXJCdWlsZGVyXG4gICAgICAgIC5jb2luKHRoaXMuc3RhdGljc0NvaW4/Lm5hbWUgYXMgc3RyaW5nKVxuICAgICAgICAuYW1vdW50KGJhdGNoRXhlY3V0aW9uSW5mby50b3RhbEFtb3VudClcbiAgICAgICAgLmNvbnRyYWN0U2VxdWVuY2VJZChzZXF1ZW5jZUlkKVxuICAgICAgICAuZXhwaXJhdGlvblRpbWUodGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpKVxuICAgICAgICAudG8ocmVjb3ZlcnlEZXN0aW5hdGlvbik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRyYW5zZmVyQnVpbGRlclxuICAgICAgICAuY29pbih0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZylcbiAgICAgICAgLmFtb3VudChiYXRjaEV4ZWN1dGlvbkluZm8udG90YWxBbW91bnQpXG4gICAgICAgIC5jb250cmFjdFNlcXVlbmNlSWQoc2VxdWVuY2VJZClcbiAgICAgICAgLmV4cGlyYXRpb25UaW1lKHRoaXMuZ2V0RGVmYXVsdEV4cGlyZVRpbWUoKSlcbiAgICAgICAgLnRvKGJhdGNoZXJDb250cmFjdEFkZHJlc3MpXG4gICAgICAgIC5kYXRhKGJhdGNoRGF0YSk7XG4gICAgfVxuXG4gICAgaWYgKHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKSB7XG4gICAgICB0cmFuc2ZlckJ1aWxkZXIua2V5KHVzZXJTaWduaW5nS2V5KTtcbiAgICB9XG5cbiAgICAvLyBJZiB0aGUgaW50ZW5kZWQgY2hhaW4gaXMgYXJiaXRydW0gb3Igb3B0aW1pc20sIHdlIG5lZWQgdG8gdXNlIHdhbGxldCB2ZXJzaW9uIDRcbiAgICAvLyBzaW5jZSB0aGVzZSBjb250cmFjdHMgY29uc3RydWN0IG9wZXJhdGlvbkhhc2ggZGlmZmVyZW50bHlcbiAgICBpZiAocGFyYW1zLmludGVuZGVkQ2hhaW4gJiYgWydhcmJldGgnLCAnb3BldGgnXS5pbmNsdWRlcyhjb2lucy5nZXQocGFyYW1zLmludGVuZGVkQ2hhaW4pLmZhbWlseSkpIHtcbiAgICAgIHR4QnVpbGRlci53YWxsZXRWZXJzaW9uKDQpO1xuICAgIH1cblxuICAgIC8vIElmIGdhc0xpbWl0IHdhcyBub3QgcGFzc2VkIGFzIGEgcGFyYW0gb3IgaWYgaXQgaXMgbm90IGNvbGQvY3VzdG9keSB3YWxsZXQsIHRoZW4gZmV0Y2ggdGhlIGdhc0xpbWl0IGZyb20gRXhwbG9yZXJcbiAgICBpZiAoIXBhcmFtcy5nYXNMaW1pdCAmJiB1c2VyS2V5ICYmICF1c2VyS2V5LnN0YXJ0c1dpdGgoJ3hwdWInKSkge1xuICAgICAgY29uc3Qgc2VuZERhdGEgPSB0eEJ1aWxkZXIuZ2V0U2VuZERhdGEoKTtcbiAgICAgIGdhc0xpbWl0ID0gYXdhaXQgdGhpcy5nZXRHYXNMaW1pdEZyb21FeHRlcm5hbEFQSShcbiAgICAgICAgcGFyYW1zLmludGVuZGVkQ2hhaW4gYXMgc3RyaW5nLFxuICAgICAgICBwYXJhbXMuYml0Z29GZWVBZGRyZXNzIGFzIHN0cmluZyxcbiAgICAgICAgcGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyxcbiAgICAgICAgc2VuZERhdGFcbiAgICAgICk7XG4gICAgICB0eEJ1aWxkZXIuZmVlKHtcbiAgICAgICAgLi4udHhGZWUsXG4gICAgICAgIGdhc0xpbWl0OiBnYXNMaW1pdC50b1N0cmluZygpLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gR2V0IHRoZSBiYWxhbmNlIG9mIGJpdGdvRmVlQWRkcmVzcyB0byBlbnN1cmUgZnVuZHMgYXJlIGF2YWlsYWJsZSB0byBwYXkgZmVlc1xuICAgIGF3YWl0IHRoaXMuZW5zdXJlU3VmZmljaWVudEJhbGFuY2UoYml0Z29GZWVBZGRyZXNzLCBnYXNQcmljZSwgZ2FzTGltaXQpO1xuXG4gICAgY29uc3QgdHggPSBhd2FpdCB0eEJ1aWxkZXIuYnVpbGQoKTtcblxuICAgIGNvbnN0IHR4SW5mbyA9IHtcbiAgICAgIHJlY2lwaWVudHM6IHJlY2lwaWVudHMsXG4gICAgICBleHBpcmVUaW1lOiB0aGlzLmdldERlZmF1bHRFeHBpcmVUaW1lKCksXG4gICAgICBjb250cmFjdFNlcXVlbmNlSWQ6IHNlcXVlbmNlSWQsXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoMTApLFxuICAgICAgaXNFdm1CYXNlZENyb3NzQ2hhaW5SZWNvdmVyeTogdHJ1ZSxcbiAgICB9O1xuXG4gICAgY29uc3QgcmVzcG9uc2U6IE9mZmxpbmVWYXVsdFR4SW5mbyA9IHtcbiAgICAgIHR4SGV4OiB0eC50b0Jyb2FkY2FzdEZvcm1hdCgpLFxuICAgICAgdXNlcktleSxcbiAgICAgIGNvaW46IHRoaXMuZ2V0Q2hhaW4oKSxcbiAgICAgIGdhc1ByaWNlOiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0ludChnYXNQcmljZSkudG9GaXhlZCgpLFxuICAgICAgZ2FzTGltaXQsXG4gICAgICByZWNpcGllbnRzOiB0eEluZm8ucmVjaXBpZW50cyxcbiAgICAgIHdhbGxldENvbnRyYWN0QWRkcmVzczogdHgudG9Kc29uKCkudG8sXG4gICAgICBhbW91bnQ6IGJhdGNoRXhlY3V0aW9uSW5mby50b3RhbEFtb3VudCxcbiAgICAgIGJhY2t1cEtleU5vbmNlOiBiaXRnb0ZlZUFkZHJlc3NOb25jZSxcbiAgICAgIGVpcDE1NTk6IHBhcmFtcy5laXAxNTU5LFxuICAgICAgLi4uKHR4QnVpbGRlci5nZXRXYWxsZXRWZXJzaW9uKCkgPT09IDQgPyB7IHdhbGxldFZlcnNpb246IHR4QnVpbGRlci5nZXRXYWxsZXRWZXJzaW9uKCkgfSA6IHt9KSxcbiAgICB9O1xuICAgIF8uZXh0ZW5kKHJlc3BvbnNlLCB0eEluZm8pO1xuICAgIHJlc3BvbnNlLm5leHRDb250cmFjdFNlcXVlbmNlSWQgPSByZXNwb25zZS5jb250cmFjdFNlcXVlbmNlSWQ7XG5cbiAgICBpZiAocGFyYW1zLndhbGxldFBhc3NwaHJhc2UpIHtcbiAgICAgIGNvbnN0IGhhbGZTaWduZWRUeG46IEhhbGZTaWduZWRUcmFuc2FjdGlvbiA9IHtcbiAgICAgICAgaGFsZlNpZ25lZDoge1xuICAgICAgICAgIHR4SGV4OiB0eC50b0Jyb2FkY2FzdEZvcm1hdCgpLFxuICAgICAgICAgIHJlY2lwaWVudHM6IHR4SW5mby5yZWNpcGllbnRzLFxuICAgICAgICAgIGV4cGlyZVRpbWU6IHR4SW5mby5leHBpcmVUaW1lLFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICAgIF8uZXh0ZW5kKHJlc3BvbnNlLCBoYWxmU2lnbmVkVHhuKTtcblxuICAgICAgY29uc3QgZmVlc1VzZWQ6IEZlZXNVc2VkID0ge1xuICAgICAgICBnYXNQcmljZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKSxcbiAgICAgICAgZ2FzTGltaXQ6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSW50KGdhc0xpbWl0KS50b0ZpeGVkKCksXG4gICAgICB9O1xuICAgICAgcmVzcG9uc2VbJ2ZlZXNVc2VkJ10gPSBmZWVzVXNlZDtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzcG9uc2U7XG4gIH1cblxuICAvKipcbiAgICogUXVlcnkgZXhwbG9yZXIgZm9yIHRoZSBiYWxhbmNlIG9mIGFuIGFkZHJlc3MgZm9yIGEgdG9rZW5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHRva2VuQ29udHJhY3RBZGRyZXNzIC0gYWRkcmVzcyB3aGVyZSB0aGUgdG9rZW4gc21hcnQgY29udHJhY3QgaXMgaG9zdGVkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB3YWxsZXRDb250cmFjdEFkZHJlc3MgLSBhZGRyZXNzIG9mIHRoZSB3YWxsZXRcbiAgICogQHJldHVybnMge0JpZ051bWJlcn0gdG9rZW4gYmFsYWFuY2UgaW4gYmFzZSB1bml0c1xuICAgKi9cbiAgYXN5bmMgcXVlcnlBZGRyZXNzVG9rZW5CYWxhbmNlKHRva2VuQ29udHJhY3RBZGRyZXNzOiBzdHJpbmcsIHdhbGxldENvbnRyYWN0QWRkcmVzczogc3RyaW5nKTogUHJvbWlzZTxhbnk+IHtcbiAgICBpZiAoIW9wdGlvbmFsRGVwcy5ldGhVdGlsLmlzVmFsaWRBZGRyZXNzKHRva2VuQ29udHJhY3RBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdjYW5ub3QgZ2V0IGJhbGFuY2UgZm9yIGludmFsaWQgdG9rZW4gYWRkcmVzcycpO1xuICAgIH1cbiAgICBpZiAoIW9wdGlvbmFsRGVwcy5ldGhVdGlsLmlzVmFsaWRBZGRyZXNzKHdhbGxldENvbnRyYWN0QWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignY2Fubm90IGdldCB0b2tlbiBiYWxhbmNlIGZvciBpbnZhbGlkIHdhbGxldCBhZGRyZXNzJyk7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5yZWNvdmVyeUJsb2NrY2hhaW5FeHBsb3JlclF1ZXJ5KHtcbiAgICAgIGNoYWluaWQ6IHRoaXMuZ2V0Q2hhaW5JZCgpLnRvU3RyaW5nKCksXG4gICAgICBtb2R1bGU6ICdhY2NvdW50JyxcbiAgICAgIGFjdGlvbjogJ3Rva2VuYmFsYW5jZScsXG4gICAgICBjb250cmFjdGFkZHJlc3M6IHRva2VuQ29udHJhY3RBZGRyZXNzLFxuICAgICAgYWRkcmVzczogd2FsbGV0Q29udHJhY3RBZGRyZXNzLFxuICAgICAgdGFnOiAnbGF0ZXN0JyxcbiAgICB9KTtcbiAgICAvLyB0aHJvdyBpZiB0aGUgcmVzdWx0IGRvZXMgbm90IGV4aXN0IG9yIHRoZSByZXN1bHQgaXMgbm90IGEgdmFsaWQgbnVtYmVyXG4gICAgaWYgKCFyZXN1bHQgfHwgIXJlc3VsdC5yZXN1bHQgfHwgaXNOYU4ocmVzdWx0LnJlc3VsdCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYENvdWxkIG5vdCBvYnRhaW4gdG9rZW4gYWRkcmVzcyBiYWxhbmNlIGZvciAke3Rva2VuQ29udHJhY3RBZGRyZXNzfSBmcm9tIEV0aGVyc2NhbiwgZ290OiAke3Jlc3VsdC5yZXN1bHR9YFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihyZXN1bHQucmVzdWx0LCAxMCk7XG4gIH1cblxuICBhc3luYyByZWNvdmVyRXRoTGlrZVRva2VuZm9yRXZtQmFzZWRSZWNvdmVyeShcbiAgICBwYXJhbXM6IFJlY292ZXJPcHRpb25zLFxuICAgIGJpdGdvRmVlQWRkcmVzc05vbmNlOiBudW1iZXIsXG4gICAgZ2FzTGltaXQsXG4gICAgZ2FzUHJpY2UsXG4gICAgdXNlcktleSxcbiAgICB1c2VyU2lnbmluZ0tleVxuICApIHtcbiAgICAvLyBnZXQgdG9rZW4gYmFsYW5jZSBvZiB3YWxsZXRcbiAgICBjb25zdCB0eEFtb3VudCA9IGF3YWl0IHRoaXMucXVlcnlBZGRyZXNzVG9rZW5CYWxhbmNlKFxuICAgICAgcGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzIGFzIHN0cmluZyxcbiAgICAgIHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3NcbiAgICApO1xuXG4gICAgLy8gYnVpbGQgcmVjaXBpZW50cyBvYmplY3RcbiAgICBjb25zdCByZWNpcGllbnRzOiBSZWNpcGllbnRbXSA9IFtcbiAgICAgIHtcbiAgICAgICAgYWRkcmVzczogcGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24sXG4gICAgICAgIGFtb3VudDogbmV3IEJpZ051bWJlcih0eEFtb3VudCkudG9GaXhlZCgpLFxuICAgICAgfSxcbiAgICBdO1xuXG4gICAgLy8gR2V0IHNlcXVlbmNlIElEIHVzaW5nIGNvbnRyYWN0IGNhbGxcbiAgICAvLyB3ZSBuZWVkIHRvIHdhaXQgYmV0d2VlbiBtYWtpbmcgdHdvIGV4cGxvcmVyIGFwaSBjYWxscyB0byBhdm9pZCBnZXR0aW5nIGJhbm5lZFxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDEwMDApKTtcbiAgICBjb25zdCBzZXF1ZW5jZUlkID0gYXdhaXQgdGhpcy5xdWVyeVNlcXVlbmNlSWQocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyk7XG5cbiAgICBjb25zdCB0eEJ1aWxkZXIgPSB0aGlzLmdldFRyYW5zYWN0aW9uQnVpbGRlcihwYXJhbXMuY29tbW9uKSBhcyBUcmFuc2FjdGlvbkJ1aWxkZXI7XG4gICAgdHhCdWlsZGVyLmNvdW50ZXIoYml0Z29GZWVBZGRyZXNzTm9uY2UpO1xuICAgIHR4QnVpbGRlci5jb250cmFjdChwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzIGFzIHN0cmluZyk7XG4gICAgbGV0IHR4RmVlO1xuICAgIGlmIChwYXJhbXMuZWlwMTU1OSkge1xuICAgICAgdHhGZWUgPSB7XG4gICAgICAgIGVpcDE1NTk6IHtcbiAgICAgICAgICBtYXhQcmlvcml0eUZlZVBlckdhczogcGFyYW1zLmVpcDE1NTkubWF4UHJpb3JpdHlGZWVQZXJHYXMsXG4gICAgICAgICAgbWF4RmVlUGVyR2FzOiBwYXJhbXMuZWlwMTU1OS5tYXhGZWVQZXJHYXMsXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgIH0gZWxzZSB7XG4gICAgICB0eEZlZSA9IHsgZmVlOiBnYXNQcmljZS50b1N0cmluZygpIH07XG4gICAgfVxuICAgIHR4QnVpbGRlci5mZWUoe1xuICAgICAgLi4udHhGZWUsXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoKSxcbiAgICB9KTtcblxuICAgIGNvbnN0IHRyYW5zZmVyQnVpbGRlciA9IHR4QnVpbGRlci50cmFuc2ZlcigpIGFzIFRyYW5zZmVyQnVpbGRlcjtcblxuICAgIGNvbnN0IG5ldHdvcmsgPSB0aGlzLmdldE5ldHdvcmsoKTtcbiAgICBjb25zdCB0b2tlbiA9IGdldFRva2VuKFxuICAgICAgcGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzIGFzIHN0cmluZyxcbiAgICAgIG5ldHdvcmsgYXMgRXRoTGlrZU5ldHdvcmssXG4gICAgICB0aGlzLnN0YXRpY3NDb2luPy5mYW1pbHkgYXMgc3RyaW5nXG4gICAgKT8ubmFtZSBhcyBzdHJpbmc7XG5cbiAgICB0cmFuc2ZlckJ1aWxkZXJcbiAgICAgIC5hbW91bnQodHhBbW91bnQpXG4gICAgICAuY29udHJhY3RTZXF1ZW5jZUlkKHNlcXVlbmNlSWQpXG4gICAgICAuZXhwaXJhdGlvblRpbWUodGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpKVxuICAgICAgLnRvKHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uKTtcblxuICAgIGlmICh0b2tlbikge1xuICAgICAgdHJhbnNmZXJCdWlsZGVyLmNvaW4odG9rZW4pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0cmFuc2ZlckJ1aWxkZXJcbiAgICAgICAgLmNvaW4odGhpcy5zdGF0aWNzQ29pbj8ubmFtZSBhcyBzdHJpbmcpXG4gICAgICAgIC50b2tlbkNvbnRyYWN0QWRkcmVzcyhwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3MgYXMgc3RyaW5nKTtcbiAgICB9XG5cbiAgICBpZiAocGFyYW1zLndhbGxldFBhc3NwaHJhc2UpIHtcbiAgICAgIHR4QnVpbGRlci50cmFuc2ZlcigpLmtleSh1c2VyU2lnbmluZ0tleSk7XG4gICAgfVxuICAgIC8vIElmIHRoZSBpbnRlbmRlZCBjaGFpbiBpcyBhcmJpdHJ1bSBvciBvcHRpbWlzbSwgd2UgbmVlZCB0byB1c2Ugd2FsbGV0IHZlcnNpb24gNFxuICAgIC8vIHNpbmNlIHRoZXNlIGNvbnRyYWN0cyBjb25zdHJ1Y3Qgb3BlcmF0aW9uSGFzaCBkaWZmZXJlbnRseVxuICAgIGlmIChwYXJhbXMuaW50ZW5kZWRDaGFpbiAmJiBbJ2FyYmV0aCcsICdvcGV0aCddLmluY2x1ZGVzKGNvaW5zLmdldChwYXJhbXMuaW50ZW5kZWRDaGFpbikuZmFtaWx5KSkge1xuICAgICAgdHhCdWlsZGVyLndhbGxldFZlcnNpb24oNCk7XG4gICAgfVxuXG4gICAgaWYgKCFwYXJhbXMuZ2FzTGltaXQgJiYgdXNlcktleSAmJiAhdXNlcktleS5zdGFydHNXaXRoKCd4cHViJykpIHtcbiAgICAgIGNvbnN0IHNlbmREYXRhID0gdHhCdWlsZGVyLmdldFNlbmREYXRhKCk7XG4gICAgICBnYXNMaW1pdCA9IGF3YWl0IHRoaXMuZ2V0R2FzTGltaXRGcm9tRXh0ZXJuYWxBUEkoXG4gICAgICAgIHBhcmFtcy5pbnRlbmRlZENoYWluIGFzIHN0cmluZyxcbiAgICAgICAgcGFyYW1zLmJpdGdvRmVlQWRkcmVzcyBhcyBzdHJpbmcsXG4gICAgICAgIHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MsXG4gICAgICAgIHNlbmREYXRhXG4gICAgICApO1xuICAgICAgdHhCdWlsZGVyLmZlZSh7XG4gICAgICAgIC4uLnR4RmVlLFxuICAgICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoKSxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEdldCB0aGUgYmFsYW5jZSBvZiBiaXRnb0ZlZUFkZHJlc3MgdG8gZW5zdXJlIGZ1bmRzIGFyZSBhdmFpbGFibGUgdG8gcGF5IGZlZXNcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVN1ZmZpY2llbnRCYWxhbmNlKHBhcmFtcy5iaXRnb0ZlZUFkZHJlc3MgYXMgc3RyaW5nLCBnYXNQcmljZSwgZ2FzTGltaXQpO1xuXG4gICAgY29uc3QgdHggPSBhd2FpdCB0eEJ1aWxkZXIuYnVpbGQoKTtcblxuICAgIGNvbnN0IHR4SW5mbyA9IHtcbiAgICAgIHJlY2lwaWVudHM6IHJlY2lwaWVudHMsXG4gICAgICBleHBpcmVUaW1lOiB0aGlzLmdldERlZmF1bHRFeHBpcmVUaW1lKCksXG4gICAgICBjb250cmFjdFNlcXVlbmNlSWQ6IHNlcXVlbmNlSWQsXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoMTApLFxuICAgICAgaXNFdm1CYXNlZENyb3NzQ2hhaW5SZWNvdmVyeTogdHJ1ZSxcbiAgICB9O1xuXG4gICAgY29uc3QgcmVzcG9uc2U6IE9mZmxpbmVWYXVsdFR4SW5mbyA9IHtcbiAgICAgIHR4SGV4OiB0eC50b0Jyb2FkY2FzdEZvcm1hdCgpLFxuICAgICAgdXNlcktleSxcbiAgICAgIGNvaW46IHRva2VuID8gdG9rZW4gOiB0aGlzLmdldENoYWluKCksXG4gICAgICBnYXNQcmljZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKSxcbiAgICAgIGdhc0xpbWl0LFxuICAgICAgcmVjaXBpZW50czogdHhJbmZvLnJlY2lwaWVudHMsXG4gICAgICB3YWxsZXRDb250cmFjdEFkZHJlc3M6IHR4LnRvSnNvbigpLnRvLFxuICAgICAgYW1vdW50OiB0eEFtb3VudC50b1N0cmluZygpLFxuICAgICAgYmFja3VwS2V5Tm9uY2U6IGJpdGdvRmVlQWRkcmVzc05vbmNlLFxuICAgICAgZWlwMTU1OTogcGFyYW1zLmVpcDE1NTksXG4gICAgICAuLi4odHhCdWlsZGVyLmdldFdhbGxldFZlcnNpb24oKSA9PT0gNCA/IHsgd2FsbGV0VmVyc2lvbjogdHhCdWlsZGVyLmdldFdhbGxldFZlcnNpb24oKSB9IDoge30pLFxuICAgIH07XG4gICAgXy5leHRlbmQocmVzcG9uc2UsIHR4SW5mbyk7XG4gICAgcmVzcG9uc2UubmV4dENvbnRyYWN0U2VxdWVuY2VJZCA9IHJlc3BvbnNlLmNvbnRyYWN0U2VxdWVuY2VJZDtcblxuICAgIGlmIChwYXJhbXMud2FsbGV0UGFzc3BocmFzZSkge1xuICAgICAgY29uc3QgaGFsZlNpZ25lZFR4bjogSGFsZlNpZ25lZFRyYW5zYWN0aW9uID0ge1xuICAgICAgICBoYWxmU2lnbmVkOiB7XG4gICAgICAgICAgdHhIZXg6IHR4LnRvQnJvYWRjYXN0Rm9ybWF0KCksXG4gICAgICAgICAgcmVjaXBpZW50czogdHhJbmZvLnJlY2lwaWVudHMsXG4gICAgICAgICAgZXhwaXJlVGltZTogdHhJbmZvLmV4cGlyZVRpbWUsXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgICAgXy5leHRlbmQocmVzcG9uc2UsIGhhbGZTaWduZWRUeG4pO1xuXG4gICAgICBjb25zdCBmZWVzVXNlZDogRmVlc1VzZWQgPSB7XG4gICAgICAgIGdhc1ByaWNlOiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0ludChnYXNQcmljZSkudG9GaXhlZCgpLFxuICAgICAgICBnYXNMaW1pdDogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzTGltaXQpLnRvRml4ZWQoKSxcbiAgICAgIH07XG4gICAgICByZXNwb25zZVsnZmVlc1VzZWQnXSA9IGZlZXNVc2VkO1xuICAgIH1cblxuICAgIHJldHVybiByZXNwb25zZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBldm0gYmFzZWQgY3Jvc3MgY2hhaW4gcmVjb3ZlcnkgcGFyYW1zXG4gICAqIEBwYXJhbSBwYXJhbXMge1JlY292ZXJPcHRpb25zfVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIHZhbGlkYXRlRXZtQmFzZWRSZWNvdmVyeVBhcmFtcyhwYXJhbXM6IFJlY292ZXJPcHRpb25zKTogdm9pZCB7XG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLmJpdGdvRmVlQWRkcmVzcykgfHwgIXRoaXMuaXNWYWxpZEFkZHJlc3MocGFyYW1zLmJpdGdvRmVlQWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCBiaXRnb0ZlZUFkZHJlc3MnKTtcbiAgICB9XG5cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzKSB8fCAhdGhpcy5pc1ZhbGlkQWRkcmVzcyhwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkIHdhbGxldENvbnRyYWN0QWRkcmVzcycpO1xuICAgIH1cblxuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uKSB8fCAhdGhpcy5pc1ZhbGlkQWRkcmVzcyhwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbikpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCByZWNvdmVyeURlc3RpbmF0aW9uJyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiB0eXBlcywgdmFsdWVzLCBhbmQgdG90YWwgYW1vdW50IGluIHdlaSB0byBzZW5kIGluIGEgYmF0Y2ggdHJhbnNhY3Rpb24sIHVzaW5nIHRoZSBtZXRob2Qgc2lnbmF0dXJlXG4gICAqIGBkaXN0cmlidXRlQmF0Y2goYWRkcmVzc1tdLCB1aW50MjU2W10pYFxuICAgKiBAcGFyYW0ge1JlY2lwaWVudFtdfSByZWNpcGllbnRzIC0gdHJhbnNhY3Rpb24gcmVjaXBpZW50c1xuICAgKiBAcmV0dXJucyB7R2V0QmF0Y2hFeGVjdXRpb25JbmZvUlR9IGluZm9ybWF0aW9uIG5lZWRlZCB0byBleGVjdXRlIHRoZSBiYXRjaCB0cmFuc2FjdGlvblxuICAgKi9cbiAgZ2V0QmF0Y2hFeGVjdXRpb25JbmZvKHJlY2lwaWVudHM6IFJlY2lwaWVudFtdKTogR2V0QmF0Y2hFeGVjdXRpb25JbmZvUlQge1xuICAgIGNvbnN0IGFkZHJlc3Nlczogc3RyaW5nW10gPSBbXTtcbiAgICBjb25zdCBhbW91bnRzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGxldCBzdW0gPSBuZXcgQmlnTnVtYmVyKCcwJyk7XG4gICAgXy5mb3JFYWNoKHJlY2lwaWVudHMsICh7IGFkZHJlc3MsIGFtb3VudCB9KSA9PiB7XG4gICAgICBhZGRyZXNzZXMucHVzaChhZGRyZXNzKTtcbiAgICAgIGFtb3VudHMucHVzaChhbW91bnQgYXMgc3RyaW5nKTtcbiAgICAgIHN1bSA9IHN1bS5wbHVzKGFtb3VudCk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgdmFsdWVzOiBbYWRkcmVzc2VzLCBhbW91bnRzXSxcbiAgICAgIHRvdGFsQW1vdW50OiBzdW0udG9GaXhlZCgpLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBkYXRhIHJlcXVpcmVkIHRvIG1ha2UgYW4gRVRIIGZ1bmN0aW9uIGNhbGwgZGVmaW5lZCBieSB0aGUgZ2l2ZW4gdHlwZXMgYW5kIHZhbHVlc1xuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gZnVuY3Rpb25OYW1lIC0gVGhlIG5hbWUgb2YgdGhlIGZ1bmN0aW9uIGJlaW5nIGNhbGxlZCwgZS5nLiB0cmFuc2ZlclxuICAgKiBAcGFyYW0gdHlwZXMgVGhlIHR5cGVzIG9mIHRoZSBmdW5jdGlvbiBjYWxsIGluIG9yZGVyXG4gICAqIEBwYXJhbSB2YWx1ZXMgVGhlIHZhbHVlcyBvZiB0aGUgZnVuY3Rpb24gY2FsbCBpbiBvcmRlclxuICAgKiBAcmV0dXJuIHtCdWZmZXJ9IFRoZSBjb21iaW5lZCBkYXRhIGZvciB0aGUgZnVuY3Rpb24gY2FsbFxuICAgKi9cbiAgZ2V0TWV0aG9kQ2FsbERhdGEgPSAoZnVuY3Rpb25OYW1lLCB0eXBlcywgdmFsdWVzKSA9PiB7XG4gICAgcmV0dXJuIEJ1ZmZlci5jb25jYXQoW1xuICAgICAgLy8gZnVuY3Rpb24gc2lnbmF0dXJlXG4gICAgICBvcHRpb25hbERlcHMuZXRoQWJpLm1ldGhvZElEKGZ1bmN0aW9uTmFtZSwgdHlwZXMpLFxuICAgICAgLy8gZnVuY3Rpb24gYXJndW1lbnRzXG4gICAgICBvcHRpb25hbERlcHMuZXRoQWJpLnJhd0VuY29kZSh0eXBlcywgdmFsdWVzKSxcbiAgICBdKTtcbiAgfTtcblxuICAvKipcbiAgICogQnVpbGQgYXJndW1lbnRzIHRvIGNhbGwgdGhlIHNlbmQgbWV0aG9kIG9uIHRoZSB3YWxsZXQgY29udHJhY3RcbiAgICogQHBhcmFtIHR4SW5mb1xuICAgKi9cbiAgZ2V0U2VuZE1ldGhvZEFyZ3ModHhJbmZvOiBHZXRTZW5kTWV0aG9kQXJnc09wdGlvbnMpOiBTZW5kTWV0aG9kQXJnc1tdIHtcbiAgICAvLyBNZXRob2Qgc2lnbmF0dXJlIGlzXG4gICAgLy8gc2VuZE11bHRpU2lnKGFkZHJlc3MgdG9BZGRyZXNzLCB1aW50IHZhbHVlLCBieXRlcyBkYXRhLCB1aW50IGV4cGlyZVRpbWUsIHVpbnQgc2VxdWVuY2VJZCwgYnl0ZXMgc2lnbmF0dXJlKVxuICAgIHJldHVybiBbXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICd0b0FkZHJlc3MnLFxuICAgICAgICB0eXBlOiAnYWRkcmVzcycsXG4gICAgICAgIHZhbHVlOiB0eEluZm8ucmVjaXBpZW50LmFkZHJlc3MsXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBuYW1lOiAndmFsdWUnLFxuICAgICAgICB0eXBlOiAndWludCcsXG4gICAgICAgIHZhbHVlOiB0eEluZm8ucmVjaXBpZW50LmFtb3VudCxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdkYXRhJyxcbiAgICAgICAgdHlwZTogJ2J5dGVzJyxcbiAgICAgICAgdmFsdWU6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLnRvQnVmZmVyKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmFkZEhleFByZWZpeCh0eEluZm8ucmVjaXBpZW50LmRhdGEgfHwgJycpKSxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdleHBpcmVUaW1lJyxcbiAgICAgICAgdHlwZTogJ3VpbnQnLFxuICAgICAgICB2YWx1ZTogdHhJbmZvLmV4cGlyZVRpbWUsXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBuYW1lOiAnc2VxdWVuY2VJZCcsXG4gICAgICAgIHR5cGU6ICd1aW50JyxcbiAgICAgICAgdmFsdWU6IHR4SW5mby5jb250cmFjdFNlcXVlbmNlSWQsXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBuYW1lOiAnc2lnbmF0dXJlJyxcbiAgICAgICAgdHlwZTogJ2J5dGVzJyxcbiAgICAgICAgdmFsdWU6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLnRvQnVmZmVyKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmFkZEhleFByZWZpeCh0eEluZm8uc2lnbmF0dXJlKSksXG4gICAgICB9LFxuICAgIF07XG4gIH1cblxuICAvKipcbiAgICogUmVjb3ZlcnMgYSB0eCB3aXRoIFRTUyBrZXkgc2hhcmVzXG4gICAqIHNhbWUgZXhwZWN0ZWQgYXJndW1lbnRzIGFzIHJlY292ZXIgbWV0aG9kLCBidXQgd2l0aCBUU1Mga2V5IHNoYXJlc1xuICAgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIHJlY292ZXJUU1MoXG4gICAgcGFyYW1zOiBSZWNvdmVyT3B0aW9uc1xuICApOiBQcm9taXNlPFJlY292ZXJ5SW5mbyB8IE9mZmxpbmVWYXVsdFR4SW5mbyB8IFVuc2lnbmVkU3dlZXBUeE1QQ3YyPiB7XG4gICAgdGhpcy52YWxpZGF0ZVJlY292ZXJ5UGFyYW1zKHBhcmFtcyk7XG5cbiAgICBpZiAoIXRoaXMuc3RhdGljc0NvaW4/LmZlYXR1cmVzLmluY2x1ZGVzKENvaW5GZWF0dXJlLkVJUDE1NTkpKSB7XG4gICAgICBkZWxldGUgcGFyYW1zLmVpcDE1NTk7XG4gICAgfVxuXG4gICAgLy8gQ2xlYW4gdXAgd2hpdGVzcGFjZSBmcm9tIGVudGVyZWQgdmFsdWVzXG4gICAgY29uc3QgdXNlclB1YmxpY09yUHJpdmF0ZUtleVNoYXJlID0gcGFyYW1zLnVzZXJLZXkucmVwbGFjZSgvXFxzL2csICcnKTtcbiAgICBjb25zdCBiYWNrdXBQcml2YXRlT3JQdWJsaWNLZXlTaGFyZSA9IHBhcmFtcy5iYWNrdXBLZXkucmVwbGFjZSgvXFxzL2csICcnKTtcblxuICAgIGlmIChcbiAgICAgIGdldElzVW5zaWduZWRTd2VlcCh7XG4gICAgICAgIHVzZXJLZXk6IHVzZXJQdWJsaWNPclByaXZhdGVLZXlTaGFyZSxcbiAgICAgICAgYmFja3VwS2V5OiBiYWNrdXBQcml2YXRlT3JQdWJsaWNLZXlTaGFyZSxcbiAgICAgICAgaXNUc3M6IHBhcmFtcy5pc1RzcyxcbiAgICAgIH0pXG4gICAgKSB7XG4gICAgICByZXR1cm4gdGhpcy5idWlsZFVuc2lnbmVkU3dlZXBUeG5UU1MocGFyYW1zKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgeyB1c2VyS2V5U2hhcmUsIGJhY2t1cEtleVNoYXJlLCBjb21tb25LZXlDaGFpbiB9ID0gYXdhaXQgRUNEU0FVdGlscy5nZXRNcGNWMlJlY292ZXJ5S2V5U2hhcmVzKFxuICAgICAgICB1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUsXG4gICAgICAgIGJhY2t1cFByaXZhdGVPclB1YmxpY0tleVNoYXJlLFxuICAgICAgICBwYXJhbXMud2FsbGV0UGFzc3BocmFzZVxuICAgICAgKTtcblxuICAgICAgY29uc3QgeyBnYXNMaW1pdCwgZ2FzUHJpY2UgfSA9IGF3YWl0IHRoaXMuZ2V0R2FzVmFsdWVzKHBhcmFtcyk7XG5cbiAgICAgIGNvbnN0IE1QQyA9IG5ldyBFY2RzYSgpO1xuICAgICAgY29uc3QgZGVyaXZlZENvbW1vbktleUNoYWluID0gTVBDLmRlcml2ZVVuaGFyZGVuZWQoY29tbW9uS2V5Q2hhaW4sICdtLzAnKTtcbiAgICAgIGNvbnN0IGJhY2t1cEtleVBhaXIgPSBuZXcgS2V5UGFpckxpYih7IHB1YjogZGVyaXZlZENvbW1vbktleUNoYWluLnNsaWNlKDAsIDY2KSB9KTtcbiAgICAgIGNvbnN0IGJhc2VBZGRyZXNzID0gYmFja3VwS2V5UGFpci5nZXRBZGRyZXNzKCk7XG4gICAgICBjb25zdCB1bnNpZ25lZFR4ID0gKGF3YWl0IHRoaXMuYnVpbGRUc3NSZWNvdmVyeVR4bihiYXNlQWRkcmVzcywgZ2FzUHJpY2UsIGdhc0xpbWl0LCBwYXJhbXMpKS50eDtcbiAgICAgIGNvbnN0IG1lc3NhZ2VIYXNoID0gdW5zaWduZWRUeC5nZXRNZXNzYWdlVG9TaWduKHRydWUpO1xuICAgICAgY29uc3Qgc2lnbmF0dXJlID0gYXdhaXQgRUNEU0FVdGlscy5zaWduUmVjb3ZlcnlNcGNWMihtZXNzYWdlSGFzaCwgdXNlcktleVNoYXJlLCBiYWNrdXBLZXlTaGFyZSwgY29tbW9uS2V5Q2hhaW4pO1xuICAgICAgY29uc3QgZXRoQ29tbW1vbiA9IHRoaXMuZ2V0RXRoTGlrZUNvbW1vbihwYXJhbXMuZWlwMTU1OSwgcGFyYW1zLnJlcGxheVByb3RlY3Rpb25PcHRpb25zKTtcbiAgICAgIGNvbnN0IHNpZ25lZFR4ID0gdGhpcy5nZXRTaWduZWRUeEZyb21TaWduYXR1cmUoZXRoQ29tbW1vbiwgdW5zaWduZWRUeCwgc2lnbmF0dXJlKTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaWQ6IGFkZEhleFByZWZpeChzaWduZWRUeC5oYXNoKCkudG9TdHJpbmcoJ2hleCcpKSxcbiAgICAgICAgdHg6IGFkZEhleFByZWZpeChzaWduZWRUeC5zZXJpYWxpemUoKS50b1N0cmluZygnaGV4JykpLFxuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGdldEdhc1ZhbHVlcyhwYXJhbXM6IFJlY292ZXJPcHRpb25zKTogUHJvbWlzZTx7IGdhc0xpbWl0OiBudW1iZXI7IGdhc1ByaWNlOiBCdWZmZXIgfT4ge1xuICAgIGNvbnN0IGdhc0xpbWl0ID0gbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHRoaXMuc2V0R2FzTGltaXQocGFyYW1zLmdhc0xpbWl0KSk7XG4gICAgY29uc3QgZ2FzUHJpY2UgPSBwYXJhbXMuZWlwMTU1OVxuICAgICAgPyBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocGFyYW1zLmVpcDE1NTkubWF4RmVlUGVyR2FzKVxuICAgICAgOiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4odGhpcy5zZXRHYXNQcmljZShwYXJhbXMuZ2FzUHJpY2UpKTtcbiAgICByZXR1cm4geyBnYXNMaW1pdCwgZ2FzUHJpY2UgfTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhc3luYyBidWlsZFVuc2lnbmVkU3dlZXBUeG5UU1MocGFyYW1zOiBSZWNvdmVyT3B0aW9ucyk6IFByb21pc2U8T2ZmbGluZVZhdWx0VHhJbmZvIHwgVW5zaWduZWRTd2VlcFR4TVBDdjI+IHtcbiAgICBjb25zdCB1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUgPSBwYXJhbXMudXNlcktleS5yZXBsYWNlKC9cXHMvZywgJycpO1xuICAgIGNvbnN0IGJhY2t1cFByaXZhdGVPclB1YmxpY0tleVNoYXJlID0gcGFyYW1zLmJhY2t1cEtleS5yZXBsYWNlKC9cXHMvZywgJycpO1xuXG4gICAgY29uc3QgeyBnYXNMaW1pdCwgZ2FzUHJpY2UgfSA9IGF3YWl0IHRoaXMuZ2V0R2FzVmFsdWVzKHBhcmFtcyk7XG5cbiAgICBjb25zdCBiYWNrdXBLZXlQYWlyID0gbmV3IEtleVBhaXJMaWIoeyBwdWI6IGJhY2t1cFByaXZhdGVPclB1YmxpY0tleVNoYXJlIH0pO1xuICAgIGNvbnN0IGJhc2VBZGRyZXNzID0gYmFja3VwS2V5UGFpci5nZXRBZGRyZXNzKCk7XG4gICAgY29uc3QgeyB0eEluZm8sIHR4LCBub25jZSB9ID0gYXdhaXQgdGhpcy5idWlsZFRzc1JlY292ZXJ5VHhuKGJhc2VBZGRyZXNzLCBnYXNQcmljZSwgZ2FzTGltaXQsIHBhcmFtcyk7XG4gICAgcmV0dXJuIHRoaXMuZm9ybWF0Rm9yT2ZmbGluZVZhdWx0VFNTKFxuICAgICAgdHhJbmZvLFxuICAgICAgdHgsXG4gICAgICB1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUsXG4gICAgICBiYWNrdXBQcml2YXRlT3JQdWJsaWNLZXlTaGFyZSxcbiAgICAgIGdhc1ByaWNlLFxuICAgICAgZ2FzTGltaXQsXG4gICAgICBub25jZSxcbiAgICAgIHBhcmFtcy5laXAxNTU5LFxuICAgICAgcGFyYW1zLnJlcGxheVByb3RlY3Rpb25PcHRpb25zXG4gICAgKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhc3luYyBidWlsZFVuc2lnbmVkU3dlZXBUeG5NUEN2MihwYXJhbXM6IFJlY292ZXJPcHRpb25zKTogUHJvbWlzZTxVbnNpZ25lZFN3ZWVwVHhNUEN2Mj4ge1xuICAgIGNvbnN0IHsgZ2FzTGltaXQsIGdhc1ByaWNlIH0gPSBhd2FpdCB0aGlzLmdldEdhc1ZhbHVlcyhwYXJhbXMpO1xuXG4gICAgY29uc3QgcmVjb3ZlclBhcmFtcyA9IHBhcmFtcyBhcyBSZWNvdmVyT3B0aW9ucztcbiAgICB0aGlzLnZhbGlkYXRlVW5zaWduZWRTd2VlcFRTU1BhcmFtcyhyZWNvdmVyUGFyYW1zKTtcblxuICAgIGNvbnN0IGRlcml2YXRpb25QYXRoID0gcmVjb3ZlclBhcmFtcy5kZXJpdmF0aW9uU2VlZCA/IGdldERlcml2YXRpb25QYXRoKHJlY292ZXJQYXJhbXMuZGVyaXZhdGlvblNlZWQpIDogJ20vMCc7XG4gICAgY29uc3QgTVBDID0gbmV3IEVjZHNhKCk7XG4gICAgY29uc3QgZGVyaXZlZENvbW1vbktleUNoYWluID0gTVBDLmRlcml2ZVVuaGFyZGVuZWQocmVjb3ZlclBhcmFtcy5iYWNrdXBLZXkgYXMgc3RyaW5nLCBkZXJpdmF0aW9uUGF0aCk7XG4gICAgY29uc3QgYmFja3VwS2V5UGFpciA9IG5ldyBLZXlQYWlyTGliKHsgcHViOiBkZXJpdmVkQ29tbW9uS2V5Q2hhaW4uc2xpY2UoMCwgNjYpIH0pO1xuICAgIGNvbnN0IGJhc2VBZGRyZXNzID0gYmFja3VwS2V5UGFpci5nZXRBZGRyZXNzKCk7XG4gICAgY29uc3QgeyB0eEluZm8sIHR4LCBub25jZSB9ID0gYXdhaXQgdGhpcy5idWlsZFRzc1JlY292ZXJ5VHhuKGJhc2VBZGRyZXNzLCBnYXNQcmljZSwgZ2FzTGltaXQsIHBhcmFtcyk7XG4gICAgcmV0dXJuIHRoaXMuYnVpbGRUeFJlcXVlc3RGb3JPZmZsaW5lVmF1bHRNUEN2MihcbiAgICAgIHR4SW5mbyxcbiAgICAgIHR4LFxuICAgICAgZGVyaXZhdGlvblBhdGgsXG4gICAgICBub25jZSxcbiAgICAgIGdhc1ByaWNlLFxuICAgICAgZ2FzTGltaXQsXG4gICAgICBwYXJhbXMuZWlwMTU1OSxcbiAgICAgIHBhcmFtcy5yZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyxcbiAgICAgIHJlY292ZXJQYXJhbXMuYmFja3VwS2V5IGFzIHN0cmluZ1xuICAgICk7XG4gIH1cblxuICBhc3luYyBjcmVhdGVCcm9hZGNhc3RhYmxlU3dlZXBUcmFuc2FjdGlvbihwYXJhbXM6IE1QQ1N3ZWVwUmVjb3ZlcnlPcHRpb25zKTogUHJvbWlzZTxNUENUeHM+IHtcbiAgICBjb25zdCByZXEgPSBwYXJhbXMuc2lnbmF0dXJlU2hhcmVzO1xuICAgIGNvbnN0IGJyb2FkY2FzdGFibGVUcmFuc2FjdGlvbnM6IE1QQ1R4W10gPSBbXTtcbiAgICBsZXQgbGFzdFNjYW5JbmRleCA9IDA7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJlcS5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgTVBDID0gbmV3IEVjZHNhKCk7XG4gICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHJlcVtpXT8udHhSZXF1ZXN0Py50cmFuc2FjdGlvbnM/LlswXT8udW5zaWduZWRUeCBhcyB1bmtub3duIGFzIFVuc2lnbmVkVHJhbnNhY3Rpb25Uc3M7XG4gICAgICBpZiAoIXJlcVtpXS5vdmMgfHwgIXJlcVtpXS5vdmNbMF0uZWNkc2FTaWduYXR1cmUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNaXNzaW5nIHNpZ25hdHVyZShzKScpO1xuICAgICAgfVxuICAgICAgaWYgKCF0cmFuc2FjdGlvbi5zaWduYWJsZUhleCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01pc3Npbmcgc2lnbmFibGUgaGV4Jyk7XG4gICAgICB9XG4gICAgICBjb25zdCBzaWduYXR1cmUgPSByZXFbaV0ub3ZjWzBdLmVjZHNhU2lnbmF0dXJlO1xuICAgICAgaWYgKCFzaWduYXR1cmUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdTaWduYXR1cmUgaXMgdW5kZWZpbmVkJyk7XG4gICAgICB9XG4gICAgICBjb25zdCBzaGFyZXM6IHN0cmluZ1tdID0gc2lnbmF0dXJlLnRvU3RyaW5nKCkuc3BsaXQoJzonKTtcbiAgICAgIGlmIChzaGFyZXMubGVuZ3RoICE9PSA0KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBzaWduYXR1cmUnKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGZpbmFsU2lnbmF0dXJlOiBFQ0RTQU1ldGhvZFR5cGVzLlNpZ25hdHVyZSA9IHtcbiAgICAgICAgcmVjaWQ6IE51bWJlcihzaGFyZXNbMF0pLFxuICAgICAgICByOiBzaGFyZXNbMV0sXG4gICAgICAgIHM6IHNoYXJlc1syXSxcbiAgICAgICAgeTogc2hhcmVzWzNdLFxuICAgICAgfSBhcyB1bmtub3duIGFzIEVDRFNBTWV0aG9kVHlwZXMuU2lnbmF0dXJlO1xuICAgICAgY29uc3Qgc2lnbmF0dXJlSGV4ID0gQnVmZmVyLmZyb20oc2lnbmF0dXJlLnRvU3RyaW5nKCksICdoZXgnKTtcbiAgICAgIGNvbnN0IHR4QnVpbGRlciA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25CdWlsZGVyKGdldENvbW1vbih0aGlzLmdldE5ldHdvcmsoKSBhcyBFdGhMaWtlTmV0d29yaykpO1xuICAgICAgdHhCdWlsZGVyLmZyb20odHJhbnNhY3Rpb24uc2VyaWFsaXplZFR4SGV4IGFzIHN0cmluZyk7XG5cbiAgICAgIGlmICghdHJhbnNhY3Rpb24uY29pblNwZWNpZmljPy5jb21tb25LZXlDaGFpbikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE1pc3NpbmcgY29tbW9uIGtleWNoYWluIGZvciB0cmFuc2FjdGlvbiBhdCBpbmRleCAke2l9YCk7XG4gICAgICB9XG4gICAgICBjb25zdCBjb21tb25LZXlDaGFpbiA9IHRyYW5zYWN0aW9uLmNvaW5TcGVjaWZpYy5jb21tb25LZXlDaGFpbjtcbiAgICAgIGlmICghdHJhbnNhY3Rpb24uZGVyaXZhdGlvblBhdGgpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBNaXNzaW5nIGRlcml2YXRpb24gcGF0aCBmb3IgdHJhbnNhY3Rpb24gYXQgaW5kZXggJHtpfWApO1xuICAgICAgfVxuICAgICAgaWYgKCFjb21tb25LZXlDaGFpbikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE1pc3NpbmcgY29tbW9uIGtleSBjaGFpbiBmb3IgdHJhbnNhY3Rpb24gYXQgaW5kZXggJHtpfWApO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBkZXJpdmF0aW9uUGF0aCA9IHRyYW5zYWN0aW9uLmRlcml2YXRpb25QYXRoID8/ICdtLzAnO1xuICAgICAgY29uc3QgZGVyaXZlZENvbW1vbktleUNoYWluID0gTVBDLmRlcml2ZVVuaGFyZGVuZWQoU3RyaW5nKGNvbW1vbktleUNoYWluKSwgU3RyaW5nKGRlcml2YXRpb25QYXRoKSk7XG4gICAgICBjb25zdCBkZXJpdmVkUHVibGljS2V5ID0gbmV3IEtleVBhaXJMaWIoeyBwdWI6IGRlcml2ZWRDb21tb25LZXlDaGFpbi5zbGljZSgwLCA2NikgfSk7XG4gICAgICB0eEJ1aWxkZXIuYWRkU2lnbmF0dXJlKHsgcHViOiBkZXJpdmVkUHVibGljS2V5LmdldEtleXMoKS5wdWIgfSwgc2lnbmF0dXJlSGV4KTtcbiAgICAgIGNvbnN0IGV0aENvbW1tb24gPSB0aGlzLmdldEV0aExpa2VDb21tb24odHJhbnNhY3Rpb24uZWlwMTU1OSwgdHJhbnNhY3Rpb24ucmVwbGF5UHJvdGVjdGlvbk9wdGlvbnMpO1xuICAgICAgbGV0IHVuc2lnbmVkVHg7XG4gICAgICBpZiAodHJhbnNhY3Rpb24uZWlwMTU1OSkge1xuICAgICAgICB1bnNpZ25lZFR4ID0gYXdhaXQgRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uLmZyb21TZXJpYWxpemVkVHgoXG4gICAgICAgICAgQnVmZmVyLmZyb20odHJhbnNhY3Rpb24uc2VyaWFsaXplZFR4SGV4LCAnaGV4JylcbiAgICAgICAgKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHVuc2lnbmVkVHggPSBhd2FpdCBMZWdhY3lUcmFuc2FjdGlvbi5mcm9tU2VyaWFsaXplZFR4KEJ1ZmZlci5mcm9tKHRyYW5zYWN0aW9uLnNlcmlhbGl6ZWRUeEhleCwgJ2hleCcpKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHNpZ25lZFR4ID0gdGhpcy5nZXRTaWduZWRUeEZyb21TaWduYXR1cmUoZXRoQ29tbW1vbiwgdW5zaWduZWRUeCwgZmluYWxTaWduYXR1cmUpO1xuICAgICAgYnJvYWRjYXN0YWJsZVRyYW5zYWN0aW9ucy5wdXNoKHtcbiAgICAgICAgc2VyaWFsaXplZFR4OiBhZGRIZXhQcmVmaXgoc2lnbmVkVHguc2VyaWFsaXplKCkudG9TdHJpbmcoJ2hleCcpKSxcbiAgICAgIH0pO1xuXG4gICAgICBpZiAoaSA9PT0gcmVxLmxlbmd0aCAtIDEgJiYgdHJhbnNhY3Rpb24uY29pblNwZWNpZmljIS5sYXN0U2NhbkluZGV4KSB7XG4gICAgICAgIGxhc3RTY2FuSW5kZXggPSB0cmFuc2FjdGlvbi5jb2luU3BlY2lmaWMhLmxhc3RTY2FuSW5kZXggYXMgbnVtYmVyO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7IHRyYW5zYWN0aW9uczogYnJvYWRjYXN0YWJsZVRyYW5zYWN0aW9ucywgbGFzdFNjYW5JbmRleCB9O1xuICB9XG5cbiAgLyoqXG4gICAqIE1ldGhvZCB0byB2YWxpZGF0ZSByZWNvdmVyeSBwYXJhbXNcbiAgICogQHBhcmFtIHtSZWNvdmVyT3B0aW9uc30gcGFyYW1zXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB2YWxpZGF0ZVVuc2lnbmVkU3dlZXBUU1NQYXJhbXMocGFyYW1zOiBSZWNvdmVyT3B0aW9ucyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy5iYWNrdXBLZXkpICYmIHBhcmFtcy5iYWNrdXBLZXkgPT09ICcnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3NpbmcgY29tbW9uS2V5Q2hhaW4nKTtcbiAgICB9XG4gICAgaWYgKCFfLmlzVW5kZWZpbmVkKHBhcmFtcy5kZXJpdmF0aW9uU2VlZCkgJiYgdHlwZW9mIHBhcmFtcy5kZXJpdmF0aW9uU2VlZCAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCBkZXJpdmF0aW9uU2VlZCcpO1xuICAgIH1cbiAgICBpZiAoXG4gICAgICBfLmlzVW5kZWZpbmVkKHBhcmFtcy5iaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcykgfHxcbiAgICAgIHR5cGVvZiBwYXJhbXMuYml0Z29EZXN0aW5hdGlvbkFkZHJlc3MgIT09ICdzdHJpbmcnIHx8XG4gICAgICAhdGhpcy5pc1ZhbGlkQWRkcmVzcyhwYXJhbXMuYml0Z29EZXN0aW5hdGlvbkFkZHJlc3MpXG4gICAgKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3Npbmcgb3IgaW52YWxpZCBkZXN0aW5hdGlvbkFkZHJlc3MnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSGVscGVyIGZ1bmN0aW9uIGZvciByZWNvdmVyKClcbiAgICogVGhpcyB0cmFuc2Zvcm1zIHRoZSB1bnNpZ25lZCB0cmFuc2FjdGlvbiBpbmZvcm1hdGlvbiBpbnRvIGEgZm9ybWF0IHRoZSBCaXRHbyBvZmZsaW5lIHZhdWx0IGV4cGVjdHNcbiAgICogQHBhcmFtIHtVbmZvcm1hdHRlZFR4SW5mb30gdHhJbmZvIC0gdHggaW5mb1xuICAgKiBAcGFyYW0ge0V0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb259IGV0aFR4IC0gdGhlIGV0aGVyZXVtanMgdHggb2JqZWN0XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBkZXJpdmF0aW9uUGF0aCAtIHRoZSBkZXJpdmF0aW9uUGF0aFxuICAgKiBAcGFyYW0ge251bWJlcn0gbm9uY2UgLSB0aGUgbm9uY2Ugb2YgdGhlIGJhY2t1cCBrZXkgYWRkcmVzc1xuICAgKiBAcGFyYW0ge0J1ZmZlcn0gZ2FzUHJpY2UgLSBnYXMgcHJpY2UgZm9yIHRoZSB0eFxuICAgKiBAcGFyYW0ge251bWJlcn0gZ2FzTGltaXQgLSBnYXMgbGltaXQgZm9yIHRoZSB0eFxuICAgKiBAcGFyYW0ge0VJUDE1NTl9IGVpcDE1NTkgLSBlaXAxNTU5IHBhcmFtc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPZmZsaW5lVmF1bHRUeEluZm8+fVxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZFR4UmVxdWVzdEZvck9mZmxpbmVWYXVsdE1QQ3YyKFxuICAgIHR4SW5mbzogVW5mb3JtYXR0ZWRUeEluZm8sXG4gICAgZXRoVHg6IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24sXG4gICAgZGVyaXZhdGlvblBhdGg6IHN0cmluZyxcbiAgICBub25jZTogbnVtYmVyLFxuICAgIGdhc1ByaWNlOiBCdWZmZXIsXG4gICAgZ2FzTGltaXQ6IG51bWJlcixcbiAgICBlaXAxNTU5PzogRUlQMTU1OSxcbiAgICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zLFxuICAgIGNvbW1vbktleUNoYWluPzogc3RyaW5nXG4gICk6IFVuc2lnbmVkU3dlZXBUeE1QQ3YyIHtcbiAgICBpZiAoIWV0aFR4LnRvKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0V0aCB0eCBtdXN0IGhhdmUgYSBgdG9gIGFkZHJlc3MnKTtcbiAgICB9XG5cbiAgICBjb25zdCBmZWUgPSBlaXAxNTU5XG4gICAgICA/IGdhc0xpbWl0ICogZWlwMTU1OS5tYXhGZWVQZXJHYXNcbiAgICAgIDogZ2FzTGltaXQgKiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0ludChnYXNQcmljZSkudG9GaXhlZCgpO1xuXG4gICAgY29uc3QgdW5zaWduZWRUeDogVW5zaWduZWRUcmFuc2FjdGlvblRzcyA9IHtcbiAgICAgIHNlcmlhbGl6ZWRUeEhleDogZXRoVHguc2VyaWFsaXplKCkudG9TdHJpbmcoJ2hleCcpLFxuICAgICAgc2lnbmFibGVIZXg6IGV0aFR4LmdldE1lc3NhZ2VUb1NpZ24oZmFsc2UpLnRvU3RyaW5nKCdoZXgnKSxcbiAgICAgIGRlcml2YXRpb25QYXRoOiBkZXJpdmF0aW9uUGF0aCxcbiAgICAgIGZlZUluZm86IHtcbiAgICAgICAgZmVlOiBmZWUsXG4gICAgICAgIGZlZVN0cmluZzogZmVlLnRvU3RyaW5nKCksXG4gICAgICB9LFxuICAgICAgcGFyc2VkVHg6IHtcbiAgICAgICAgc3BlbmRBbW91bnQ6IHR4SW5mby5yZWNpcGllbnQuYW1vdW50LFxuICAgICAgICBvdXRwdXRzOiBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgY29pbk5hbWU6IHRoaXMuZ2V0Q2hhaW4oKSxcbiAgICAgICAgICAgIGFkZHJlc3M6IHR4SW5mby5yZWNpcGllbnQuYWRkcmVzcyxcbiAgICAgICAgICAgIHZhbHVlU3RyaW5nOiB0eEluZm8ucmVjaXBpZW50LmFtb3VudCxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgfSxcbiAgICAgIGNvaW5TcGVjaWZpYzoge1xuICAgICAgICBjb21tb25LZXlDaGFpbjogY29tbW9uS2V5Q2hhaW4sXG4gICAgICB9LFxuICAgICAgZWlwMTU1OTogZWlwMTU1OSxcbiAgICAgIHJlcGxheVByb3RlY3Rpb25PcHRpb25zOiByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyxcbiAgICB9O1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHR4UmVxdWVzdHM6IFtcbiAgICAgICAge1xuICAgICAgICAgIHdhbGxldENvaW46IHRoaXMuZ2V0Q2hhaW4oKSxcbiAgICAgICAgICB0cmFuc2FjdGlvbnM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgdW5zaWduZWRUeDogdW5zaWduZWRUeCxcbiAgICAgICAgICAgICAgbm9uY2U6IG5vbmNlLFxuICAgICAgICAgICAgICBzaWduYXR1cmVTaGFyZXM6IFtdLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBidWlsZFRzc1JlY292ZXJ5VHhuKGJhc2VBZGRyZXNzOiBzdHJpbmcsIGdhc1ByaWNlOiBhbnksIGdhc0xpbWl0OiBhbnksIHBhcmFtczogUmVjb3Zlck9wdGlvbnMpIHtcbiAgICBjb25zdCBub25jZSA9IGF3YWl0IHRoaXMuZ2V0QWRkcmVzc05vbmNlKGJhc2VBZGRyZXNzKTtcbiAgICBjb25zdCB0eEFtb3VudCA9IGF3YWl0IHRoaXMudmFsaWRhdGVCYWxhbmNlQW5kR2V0VHhBbW91bnQoYmFzZUFkZHJlc3MsIGdhc1ByaWNlLCBnYXNMaW1pdCk7XG4gICAgY29uc3QgcmVjaXBpZW50cyA9IFtcbiAgICAgIHtcbiAgICAgICAgYWRkcmVzczogcGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24sXG4gICAgICAgIGFtb3VudDogdHhBbW91bnQudG9TdHJpbmcoMTApLFxuICAgICAgfSxcbiAgICBdO1xuXG4gICAgY29uc3QgdHhJbmZvID0ge1xuICAgICAgcmVjaXBpZW50OiByZWNpcGllbnRzWzBdLFxuICAgICAgZXhwaXJlVGltZTogdGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpLFxuICAgICAgZ2FzTGltaXQ6IGdhc0xpbWl0LnRvU3RyaW5nKDEwKSxcbiAgICB9O1xuXG4gICAgY29uc3QgdHhQYXJhbXMgPSB7XG4gICAgICB0bzogcGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24sXG4gICAgICBub25jZTogbm9uY2UsXG4gICAgICB2YWx1ZTogdHhBbW91bnQsXG4gICAgICBnYXNQcmljZTogZ2FzUHJpY2UsXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQsXG4gICAgICBkYXRhOiBCdWZmZXIuZnJvbSgnMHgnKSxcbiAgICAgIGVpcDE1NTk6IHBhcmFtcy5laXAxNTU5LFxuICAgICAgcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM6IHBhcmFtcy5yZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyxcbiAgICB9O1xuXG4gICAgY29uc3QgdHggPSB0aGlzLmJ1aWxkVHJhbnNhY3Rpb24odHhQYXJhbXMpO1xuICAgIHJldHVybiB7IHR4SW5mbywgdHgsIG5vbmNlIH07XG4gIH1cblxuICBhc3luYyB2YWxpZGF0ZUJhbGFuY2VBbmRHZXRUeEFtb3VudChiYXNlQWRkcmVzczogc3RyaW5nLCBnYXNQcmljZTogQk4sIGdhc0xpbWl0OiBCTikge1xuICAgIGNvbnN0IGJhc2VBZGRyZXNzQmFsYW5jZSA9IGF3YWl0IHRoaXMucXVlcnlBZGRyZXNzQmFsYW5jZShiYXNlQWRkcmVzcyk7XG4gICAgY29uc3QgdG90YWxHYXNOZWVkZWQgPSBnYXNQcmljZS5tdWwoZ2FzTGltaXQpO1xuICAgIGNvbnN0IHdlaVRvR3dlaSA9IG5ldyBCTigxMCAqKiA5KTtcbiAgICBpZiAoYmFzZUFkZHJlc3NCYWxhbmNlLmx0KHRvdGFsR2FzTmVlZGVkKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgQmFja3VwIGtleSBhZGRyZXNzICR7YmFzZUFkZHJlc3N9IGhhcyBiYWxhbmNlICR7YmFzZUFkZHJlc3NCYWxhbmNlLmRpdih3ZWlUb0d3ZWkpLnRvU3RyaW5nKCl9IEd3ZWkuYCArXG4gICAgICAgICAgYFRoaXMgYWRkcmVzcyBtdXN0IGhhdmUgYSBiYWxhbmNlIG9mIGF0IGxlYXN0ICR7dG90YWxHYXNOZWVkZWQuZGl2KHdlaVRvR3dlaSkudG9TdHJpbmcoKX1gICtcbiAgICAgICAgICBgIEd3ZWkgdG8gcGVyZm9ybSByZWNvdmVyaWVzLiBUcnkgc2VuZGluZyBzb21lIEVUSCB0byB0aGlzIGFkZHJlc3MgdGhlbiByZXRyeS5gXG4gICAgICApO1xuICAgIH1cbiAgICBjb25zdCB0eEFtb3VudCA9IGJhc2VBZGRyZXNzQmFsYW5jZS5zdWIodG90YWxHYXNOZWVkZWQpO1xuICAgIHJldHVybiB0eEFtb3VudDtcbiAgfVxuXG4gIGFzeW5jIHJlY292ZXJ5QmxvY2tjaGFpbkV4cGxvcmVyUXVlcnkocXVlcnk6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4pOiBQcm9taXNlPGFueT4ge1xuICAgIHRocm93IG5ldyBFcnJvcignbWV0aG9kIG5vdCBpbXBsZW1lbnRlZCcpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgdGhlIGV4dHJhIHBhcmFtZXRlcnMgbmVlZGVkIHRvIGJ1aWxkIGEgaG9wIHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSBidWlsZFBhcmFtcyBUaGUgb3JpZ2luYWwgYnVpbGQgcGFyYW1ldGVyc1xuICAgKiBAcmV0dXJucyBleHRyYSBwYXJhbWV0ZXJzIG9iamVjdCB0byBtZXJnZSB3aXRoIHRoZSBvcmlnaW5hbCBidWlsZCBwYXJhbWV0ZXJzIG9iamVjdCBhbmQgc2VuZCB0byB0aGUgcGxhdGZvcm1cbiAgICovXG4gIGFzeW5jIGNyZWF0ZUhvcFRyYW5zYWN0aW9uUGFyYW1zKGJ1aWxkUGFyYW1zOiBIb3BUcmFuc2FjdGlvbkJ1aWxkT3B0aW9ucyk6IFByb21pc2U8SG9wUGFyYW1zPiB7XG4gICAgY29uc3Qgd2FsbGV0ID0gYnVpbGRQYXJhbXMud2FsbGV0O1xuICAgIGNvbnN0IHJlY2lwaWVudHMgPSBidWlsZFBhcmFtcy5yZWNpcGllbnRzO1xuICAgIGNvbnN0IHdhbGxldFBhc3NwaHJhc2UgPSBidWlsZFBhcmFtcy53YWxsZXRQYXNzcGhyYXNlO1xuXG4gICAgY29uc3QgdXNlcktleWNoYWluID0gYXdhaXQgdGhpcy5rZXljaGFpbnMoKS5nZXQoeyBpZDogd2FsbGV0LmtleUlkcygpWzBdIH0pO1xuICAgIGNvbnN0IHVzZXJQcnYgPSB3YWxsZXQuZ2V0VXNlclBydih7IGtleWNoYWluOiB1c2VyS2V5Y2hhaW4sIHdhbGxldFBhc3NwaHJhc2UgfSk7XG4gICAgY29uc3QgdXNlclBydkJ1ZmZlciA9IGJpcDMyLmZyb21CYXNlNTgodXNlclBydikucHJpdmF0ZUtleTtcbiAgICBpZiAoIXVzZXJQcnZCdWZmZXIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCB1c2VyUHJ2Jyk7XG4gICAgfVxuICAgIGlmICghcmVjaXBpZW50cyB8fCAhQXJyYXkuaXNBcnJheShyZWNpcGllbnRzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdleHBlY3RpbmcgYXJyYXkgb2YgcmVjaXBpZW50cycpO1xuICAgIH1cblxuICAgIC8vIFJpZ2h0IG5vdyB3ZSBvbmx5IHN1cHBvcnQgMSByZWNpcGllbnRcbiAgICBpZiAocmVjaXBpZW50cy5sZW5ndGggIT09IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbXVzdCBzZW5kIHRvIGV4YWN0bHkgMSByZWNpcGllbnQnKTtcbiAgICB9XG4gICAgY29uc3QgcmVjaXBpZW50QWRkcmVzcyA9IHJlY2lwaWVudHNbMF0uYWRkcmVzcztcbiAgICBjb25zdCByZWNpcGllbnRBbW91bnQgPSByZWNpcGllbnRzWzBdLmFtb3VudCBhcyBzdHJpbmc7XG4gICAgY29uc3QgZmVlRXN0aW1hdGVQYXJhbXMgPSB7XG4gICAgICByZWNpcGllbnQ6IHJlY2lwaWVudEFkZHJlc3MsXG4gICAgICBhbW91bnQ6IHJlY2lwaWVudEFtb3VudCxcbiAgICAgIGhvcDogdHJ1ZSxcbiAgICB9O1xuICAgIGNvbnN0IGZlZUVzdGltYXRlOiBGZWVFc3RpbWF0ZSA9IGF3YWl0IHRoaXMuZmVlRXN0aW1hdGUoZmVlRXN0aW1hdGVQYXJhbXMpO1xuXG4gICAgY29uc3QgZ2FzTGltaXQgPSBmZWVFc3RpbWF0ZS5nYXNMaW1pdEVzdGltYXRlO1xuICAgIGNvbnN0IGdhc1ByaWNlID0gTWF0aC5yb3VuZChmZWVFc3RpbWF0ZS5mZWVFc3RpbWF0ZSAvIGdhc0xpbWl0KTtcbiAgICBjb25zdCBnYXNQcmljZU1heCA9IGdhc1ByaWNlICogNTtcbiAgICAvLyBQYXltZW50IGlkIGEgcmFuZG9tIG51bWJlciBzbyBpdHMgZGlmZmVyZW50IGZvciBldmVyeSB0eFxuICAgIGNvbnN0IHBheW1lbnRJZCA9IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIDEwMDAwMDAwMDAwKS50b1N0cmluZygpO1xuICAgIGNvbnN0IGhvcERpZ2VzdDogQnVmZmVyID0gQWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuZ2V0SG9wRGlnZXN0KFtcbiAgICAgIHJlY2lwaWVudEFkZHJlc3MsXG4gICAgICByZWNpcGllbnRBbW91bnQsXG4gICAgICBnYXNQcmljZU1heC50b1N0cmluZygpLFxuICAgICAgZ2FzTGltaXQudG9TdHJpbmcoKSxcbiAgICAgIHBheW1lbnRJZCxcbiAgICBdKTtcblxuICAgIGNvbnN0IHVzZXJSZXFTaWcgPSBvcHRpb25hbERlcHMuZXRoVXRpbC5hZGRIZXhQcmVmaXgoXG4gICAgICBCdWZmZXIuZnJvbShzZWNwMjU2azEuZWNkc2FTaWduKGhvcERpZ2VzdCwgdXNlclBydkJ1ZmZlcikuc2lnbmF0dXJlKS50b1N0cmluZygnaGV4JylcbiAgICApO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIGhvcFBhcmFtczoge1xuICAgICAgICBnYXNQcmljZU1heCxcbiAgICAgICAgdXNlclJlcVNpZyxcbiAgICAgICAgcGF5bWVudElkLFxuICAgICAgICBnYXNMaW1pdCxcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZXMgdGhhdCB0aGUgaG9wIHByZWJ1aWxkIGZyb20gdGhlIEhTTSBpcyB2YWxpZCBhbmQgY29ycmVjdFxuICAgKiBAcGFyYW0ge0lXYWxsZXR9IHdhbGxldCAtIFRoZSB3YWxsZXQgdGhhdCB0aGUgcHJlYnVpbGQgaXMgZm9yXG4gICAqIEBwYXJhbSB7SG9wUHJlYnVpbGR9IGhvcFByZWJ1aWxkIC0gVGhlIHByZWJ1aWxkIHRvIHZhbGlkYXRlXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBvcmlnaW5hbFBhcmFtcyAtIFRoZSBvcmlnaW5hbCBwYXJhbWV0ZXJzIHBhc3NlZCB0byBwcmVidWlsZFRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSB7UmVjaXBpZW50W119IG9yaWdpbmFsUGFyYW1zLnJlY2lwaWVudHMgLSBUaGUgb3JpZ2luYWwgcmVjaXBpZW50cyBhcnJheVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICogQHRocm93cyBFcnJvciBpZiBUaGUgcHJlYnVpbGQgaXMgaW52YWxpZFxuICAgKi9cbiAgYXN5bmMgdmFsaWRhdGVIb3BQcmVidWlsZChcbiAgICB3YWxsZXQ6IElXYWxsZXQsXG4gICAgaG9wUHJlYnVpbGQ6IEhvcFByZWJ1aWxkLFxuICAgIG9yaWdpbmFsUGFyYW1zPzogeyByZWNpcGllbnRzOiBSZWNpcGllbnRbXSB9XG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgdHgsIGlkLCBzaWduYXR1cmUgfSA9IGhvcFByZWJ1aWxkO1xuXG4gICAgLy8gZmlyc3QsIHZhbGlkYXRlIHRoZSBIU00gc2lnbmF0dXJlXG4gICAgY29uc3Qgc2VydmVyWHB1YiA9IGNvbW1vbi5FbnZpcm9ubWVudHNbdGhpcy5iaXRnby5nZXRFbnYoKV0uaHNtWHB1YjtcbiAgICBjb25zdCBzZXJ2ZXJQdWJrZXlCdWZmZXI6IEJ1ZmZlciA9IGJpcDMyLmZyb21CYXNlNTgoc2VydmVyWHB1YikucHVibGljS2V5O1xuICAgIGNvbnN0IHNpZ25hdHVyZUJ1ZmZlcjogQnVmZmVyID0gQnVmZmVyLmZyb20ob3B0aW9uYWxEZXBzLmV0aFV0aWwuc3RyaXBIZXhQcmVmaXgoc2lnbmF0dXJlKSwgJ2hleCcpO1xuICAgIGNvbnN0IG1lc3NhZ2VCdWZmZXI6IEJ1ZmZlciA9IEJ1ZmZlci5mcm9tKFxuICAgICAgb3B0aW9uYWxEZXBzLmV0aFV0aWwucGFkVG9FdmVuKG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KGlkKSksXG4gICAgICAnaGV4J1xuICAgICk7XG5cbiAgICBjb25zdCBzaWcgPSBuZXcgVWludDhBcnJheShzaWduYXR1cmVCdWZmZXIuc2xpY2UoMSkpO1xuICAgIGNvbnN0IGlzVmFsaWRTaWduYXR1cmU6IGJvb2xlYW4gPSBzZWNwMjU2azEuZWNkc2FWZXJpZnkoc2lnLCBtZXNzYWdlQnVmZmVyLCBzZXJ2ZXJQdWJrZXlCdWZmZXIpO1xuICAgIGlmICghaXNWYWxpZFNpZ25hdHVyZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgSG9wIHR4aWQgc2lnbmF0dXJlIGludmFsaWQgLSBwdWI6ICR7c2VydmVyWHB1Yn0sIG1zZzogJHttZXNzYWdlQnVmZmVyPy50b1N0cmluZygpfSwgc2lnOiAke3NpZ25hdHVyZUJ1ZmZlcj8udG9TdHJpbmcoKX1gXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IGJ1aWx0SG9wVHggPSBvcHRpb25hbERlcHMuRXRoVHguVHJhbnNhY3Rpb25GYWN0b3J5LmZyb21TZXJpYWxpemVkRGF0YShvcHRpb25hbERlcHMuZXRoVXRpbC50b0J1ZmZlcih0eCkpO1xuICAgIC8vIElmIG9yaWdpbmFsIHBhcmFtcyBhcmUgZ2l2ZW4sIHdlIGNhbiBjaGVjayB0aGVtIGFnYWluc3QgdGhlIHRyYW5zYWN0aW9uIHByZWJ1aWxkIHBhcmFtc1xuICAgIGlmICghXy5pc05pbChvcmlnaW5hbFBhcmFtcykpIHtcbiAgICAgIGNvbnN0IHsgcmVjaXBpZW50cyB9ID0gb3JpZ2luYWxQYXJhbXM7XG5cbiAgICAgIC8vIFRoZW4gdmFsaWRhdGUgdGhhdCB0aGUgdHggcGFyYW1zIGFjdHVhbGx5IGVxdWFsIHRoZSByZXF1ZXN0ZWQgcGFyYW1zXG4gICAgICBjb25zdCBvcmlnaW5hbEFtb3VudCA9IG5ldyBCaWdOdW1iZXIocmVjaXBpZW50c1swXS5hbW91bnQpO1xuICAgICAgY29uc3Qgb3JpZ2luYWxEZXN0aW5hdGlvbjogc3RyaW5nID0gcmVjaXBpZW50c1swXS5hZGRyZXNzO1xuXG4gICAgICBjb25zdCBob3BBbW91bnQgPSBuZXcgQmlnTnVtYmVyKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSGV4KGJ1aWx0SG9wVHgudmFsdWUpKTtcbiAgICAgIGlmICghYnVpbHRIb3BUeC50bykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFRyYW5zYWN0aW9uIGRvZXMgbm90IGhhdmUgYSBkZXN0aW5hdGlvbiBhZGRyZXNzYCk7XG4gICAgICB9XG4gICAgICBjb25zdCBob3BEZXN0aW5hdGlvbiA9IGJ1aWx0SG9wVHgudG8udG9TdHJpbmcoKTtcbiAgICAgIGlmICghaG9wQW1vdW50LmVxKG9yaWdpbmFsQW1vdW50KSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEhvcCBhbW91bnQ6ICR7aG9wQW1vdW50fSBkb2VzIG5vdCBlcXVhbCBvcmlnaW5hbCBhbW91bnQ6ICR7b3JpZ2luYWxBbW91bnR9YCk7XG4gICAgICB9XG4gICAgICBpZiAoaG9wRGVzdGluYXRpb24udG9Mb3dlckNhc2UoKSAhPT0gb3JpZ2luYWxEZXN0aW5hdGlvbi50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgSG9wIGRlc3RpbmF0aW9uOiAke2hvcERlc3RpbmF0aW9ufSBkb2VzIG5vdCBlcXVhbCBvcmlnaW5hbCByZWNpcGllbnQ6ICR7aG9wRGVzdGluYXRpb259YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFidWlsdEhvcFR4LnZlcmlmeVNpZ25hdHVyZSgpKSB7XG4gICAgICAvLyBXZSBkb250IHdhbnQgdG8gY29udGludWUgYXQgYWxsIGluIHRoaXMgY2FzZSwgYXQgcmlzayBvZiBFVEggYmVpbmcgc3R1Y2sgb24gdGhlIGhvcCBhZGRyZXNzXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgaG9wIHRyYW5zYWN0aW9uIHNpZ25hdHVyZSwgdHhpZDogJHtpZH1gKTtcbiAgICB9XG4gICAgaWYgKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmFkZEhleFByZWZpeChidWlsdEhvcFR4Lmhhc2goKS50b1N0cmluZygnaGV4JykpICE9PSBpZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBTaWduZWQgaG9wIHR4aWQgZG9lcyBub3QgZXF1YWwgYWN0dWFsIHR4aWRgKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0cyB0aGUgaG9wIGRpZ2VzdCBmb3IgdGhlIHVzZXIgdG8gc2lnbi4gVGhpcyBpcyB2YWxpZGF0ZWQgaW4gdGhlIEhTTSB0byBwcm92ZSB0aGF0IHRoZSB1c2VyIHJlcXVlc3RlZCB0aGlzIHR4XG4gICAqIEBwYXJhbSB7c3RyaW5nW119IHBhcmFtc0FyciAtIFRoZSBwYXJhbWV0ZXJzIHRvIGhhc2ggdG9nZXRoZXIgZm9yIHRoZSBkaWdlc3RcbiAgICogQHJldHVybnMge0J1ZmZlcn1cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgZ2V0SG9wRGlnZXN0KHBhcmFtc0Fycjogc3RyaW5nW10pOiBCdWZmZXIge1xuICAgIGNvbnN0IGhhc2ggPSBLZWNjYWsoJ2tlY2NhazI1NicpO1xuICAgIGhhc2gudXBkYXRlKFtBYnN0cmFjdEV0aExpa2VOZXdDb2lucy5ob3BUcmFuc2FjdGlvblNhbHQsIC4uLnBhcmFtc0Fycl0uam9pbignJCcpKTtcbiAgICByZXR1cm4gaGFzaC5kaWdlc3QoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNb2RpZnkgcHJlYnVpbGQgYmVmb3JlIHNlbmRpbmcgaXQgdG8gdGhlIHNlcnZlci4gQWRkIHRoaW5ncyBsaWtlIGhvcCB0cmFuc2FjdGlvbiBwYXJhbXNcbiAgICogQHBhcmFtIHtCdWlsZE9wdGlvbnN9IGJ1aWxkUGFyYW1zIC0gVGhlIHdoaXRlbGlzdGVkIHBhcmFtZXRlcnMgZm9yIHRoaXMgcHJlYnVpbGRcbiAgICogQHBhcmFtIHtib29sZWFufSBidWlsZFBhcmFtcy5ob3AgLSBUcnVlIGlmIHRoaXMgc2hvdWxkIHByZWJ1aWxkIGEgaG9wIHR4LCBlbHNlIGZhbHNlXG4gICAqIEBwYXJhbSB7UmVjaXBpZW50W119IGJ1aWxkUGFyYW1zLnJlY2lwaWVudHMgLSBUaGUgcmVjaXBpZW50cyBhcnJheSBvZiB0aGlzIHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSB7V2FsbGV0fSBidWlsZFBhcmFtcy53YWxsZXQgLSBUaGUgd2FsbGV0IHNlbmRpbmcgdGhpcyB0eFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYnVpbGRQYXJhbXMud2FsbGV0UGFzc3BocmFzZSAtIHRoZSBwYXNzcGhyYXNlIGZvciB0aGlzIHdhbGxldFxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxCdWlsZE9wdGlvbnM+fVxuICAgKi9cbiAgYXN5bmMgZ2V0RXh0cmFQcmVidWlsZFBhcmFtcyhidWlsZFBhcmFtczogQnVpbGRPcHRpb25zKTogUHJvbWlzZTxCdWlsZE9wdGlvbnM+IHtcbiAgICBpZiAoXG4gICAgICAhXy5pc1VuZGVmaW5lZChidWlsZFBhcmFtcy5ob3ApICYmXG4gICAgICBidWlsZFBhcmFtcy5ob3AgJiZcbiAgICAgICFfLmlzVW5kZWZpbmVkKGJ1aWxkUGFyYW1zLndhbGxldCkgJiZcbiAgICAgICFfLmlzVW5kZWZpbmVkKGJ1aWxkUGFyYW1zLnJlY2lwaWVudHMpICYmXG4gICAgICAhXy5pc1VuZGVmaW5lZChidWlsZFBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKVxuICAgICkge1xuICAgICAgaWYgKHRoaXMgaW5zdGFuY2VvZiBFdGhMaWtlVG9rZW4pIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBIb3AgdHJhbnNhY3Rpb25zIGFyZSBub3QgZW5hYmxlZCBmb3IgRVJDLTIwIHRva2Vucywgbm9yIGFyZSB0aGV5IG5lY2Vzc2FyeS4gUGxlYXNlIHJlbW92ZSB0aGUgJ2hvcCcgcGFyYW1ldGVyIGFuZCB0cnkgYWdhaW4uYFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIChhd2FpdCB0aGlzLmNyZWF0ZUhvcFRyYW5zYWN0aW9uUGFyYW1zKHtcbiAgICAgICAgd2FsbGV0OiBidWlsZFBhcmFtcy53YWxsZXQsXG4gICAgICAgIHJlY2lwaWVudHM6IGJ1aWxkUGFyYW1zLnJlY2lwaWVudHMsXG4gICAgICAgIHdhbGxldFBhc3NwaHJhc2U6IGJ1aWxkUGFyYW1zLndhbGxldFBhc3NwaHJhc2UsXG4gICAgICB9KSkgYXMgYW55O1xuICAgIH1cbiAgICByZXR1cm4ge307XG4gIH1cblxuICAvKipcbiAgICogTW9kaWZ5IHByZWJ1aWxkIGFmdGVyIHJlY2VpdmluZyBpdCBmcm9tIHRoZSBzZXJ2ZXIuIEFkZCB0aGluZ3MgbGlrZSBubG9ja3RpbWVcbiAgICogQHBhcmFtIHtUcmFuc2FjdGlvblByZWJ1aWxkfSBwYXJhbXMgLSBUaGUgcHJlYnVpbGQgdG8gbW9kaWZ5XG4gICAqIEByZXR1cm5zIHtUcmFuc2FjdGlvblByZWJ1aWxkfSBUaGUgbW9kaWZpZWQgcHJlYnVpbGRcbiAgICovXG4gIGFzeW5jIHBvc3RQcm9jZXNzUHJlYnVpbGQocGFyYW1zOiBUcmFuc2FjdGlvblByZWJ1aWxkKTogUHJvbWlzZTxUcmFuc2FjdGlvblByZWJ1aWxkPiB7XG4gICAgaWYgKCFfLmlzVW5kZWZpbmVkKHBhcmFtcy5ob3BUcmFuc2FjdGlvbikgJiYgIV8uaXNVbmRlZmluZWQocGFyYW1zLndhbGxldCkgJiYgIV8uaXNVbmRlZmluZWQocGFyYW1zLmJ1aWxkUGFyYW1zKSkge1xuICAgICAgYXdhaXQgdGhpcy52YWxpZGF0ZUhvcFByZWJ1aWxkKHBhcmFtcy53YWxsZXQsIHBhcmFtcy5ob3BUcmFuc2FjdGlvbiwgcGFyYW1zLmJ1aWxkUGFyYW1zKTtcbiAgICB9XG4gICAgcmV0dXJuIHBhcmFtcztcbiAgfVxuXG4gIC8qKlxuICAgKiBDb2luLXNwZWNpZmljIHRoaW5ncyBkb25lIGJlZm9yZSBzaWduaW5nIGEgdHJhbnNhY3Rpb24sIGkuZS4gdmVyaWZpY2F0aW9uXG4gICAqIEBwYXJhbSB7UHJlc2lnblRyYW5zYWN0aW9uT3B0aW9uc30gcGFyYW1zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFByZXNpZ25UcmFuc2FjdGlvbk9wdGlvbnM+fVxuICAgKi9cbiAgYXN5bmMgcHJlc2lnblRyYW5zYWN0aW9uKHBhcmFtczogUHJlc2lnblRyYW5zYWN0aW9uT3B0aW9ucyk6IFByb21pc2U8UHJlc2lnblRyYW5zYWN0aW9uT3B0aW9ucz4ge1xuICAgIGlmICghXy5pc1VuZGVmaW5lZChwYXJhbXMuaG9wVHJhbnNhY3Rpb24pICYmICFfLmlzVW5kZWZpbmVkKHBhcmFtcy53YWxsZXQpICYmICFfLmlzVW5kZWZpbmVkKHBhcmFtcy5idWlsZFBhcmFtcykpIHtcbiAgICAgIGF3YWl0IHRoaXMudmFsaWRhdGVIb3BQcmVidWlsZChwYXJhbXMud2FsbGV0LCBwYXJhbXMuaG9wVHJhbnNhY3Rpb24pO1xuICAgIH1cbiAgICByZXR1cm4gcGFyYW1zO1xuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoIGZlZSBlc3RpbWF0ZSBpbmZvcm1hdGlvbiBmcm9tIHRoZSBzZXJ2ZXJcbiAgICogQHBhcmFtIHtPYmplY3R9IHBhcmFtcyAtIFRoZSBwYXJhbXMgcGFzc2VkIGludG8gdGhlIGZ1bmN0aW9uXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW3BhcmFtcy5ob3BdIC0gVHJ1ZSBpZiB3ZSBzaG91bGQgZXN0aW1hdGUgZmVlIGZvciBhIGhvcCB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0ge3N0cmluZ30gW3BhcmFtcy5yZWNpcGllbnRdIC0gVGhlIHJlY2lwaWVudCBvZiB0aGUgdHJhbnNhY3Rpb24gdG8gZXN0aW1hdGUgYSBzZW5kIHRvXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbcGFyYW1zLmRhdGFdIC0gVGhlIEVUSCB0eCBkYXRhIHRvIGVzdGltYXRlIGEgc2VuZCBmb3JcbiAgICogQHJldHVybnMge09iamVjdH0gVGhlIGZlZSBpbmZvIHJldHVybmVkIGZyb20gdGhlIHNlcnZlclxuICAgKi9cbiAgYXN5bmMgZmVlRXN0aW1hdGUocGFyYW1zOiBGZWVFc3RpbWF0ZU9wdGlvbnMpOiBQcm9taXNlPEZlZUVzdGltYXRlPiB7XG4gICAgY29uc3QgcXVlcnk6IEZlZUVzdGltYXRlT3B0aW9ucyA9IHt9O1xuICAgIGlmIChwYXJhbXMgJiYgcGFyYW1zLmhvcCkge1xuICAgICAgcXVlcnkuaG9wID0gcGFyYW1zLmhvcDtcbiAgICB9XG4gICAgaWYgKHBhcmFtcyAmJiBwYXJhbXMucmVjaXBpZW50KSB7XG4gICAgICBxdWVyeS5yZWNpcGllbnQgPSBwYXJhbXMucmVjaXBpZW50O1xuICAgIH1cbiAgICBpZiAocGFyYW1zICYmIHBhcmFtcy5kYXRhKSB7XG4gICAgICBxdWVyeS5kYXRhID0gcGFyYW1zLmRhdGE7XG4gICAgfVxuICAgIGlmIChwYXJhbXMgJiYgcGFyYW1zLmFtb3VudCkge1xuICAgICAgcXVlcnkuYW1vdW50ID0gcGFyYW1zLmFtb3VudDtcbiAgICB9XG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5iaXRnby5nZXQodGhpcy51cmwoJy90eC9mZWUnKSkucXVlcnkocXVlcnkpLnJlc3VsdCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlIHNlY3AyNTZrMSBrZXkgcGFpclxuICAgKlxuICAgKiBAcGFyYW0ge0J1ZmZlcn0gc2VlZFxuICAgKiBAcmV0dXJucyB7S2V5UGFpcn0gb2JqZWN0IHdpdGggZ2VuZXJhdGVkIHB1YiBhbmQgcHJ2XG4gICAqL1xuICBnZW5lcmF0ZUtleVBhaXIoc2VlZDogQnVmZmVyKTogS2V5UGFpciB7XG4gICAgaWYgKCFzZWVkKSB7XG4gICAgICAvLyBBbiBleHRlbmRlZCBwcml2YXRlIGtleSBoYXMgYm90aCBhIG5vcm1hbCAyNTYgYml0IHByaXZhdGUga2V5IGFuZCBhIDI1NlxuICAgICAgLy8gYml0IGNoYWluIGNvZGUsIGJvdGggb2Ygd2hpY2ggbXVzdCBiZSByYW5kb20uIDUxMiBiaXRzIGlzIHRoZXJlZm9yZSB0aGVcbiAgICAgIC8vIG1heGltdW0gZW50cm9weSBhbmQgZ2l2ZXMgdXMgbWF4aW11bSBzZWN1cml0eSBhZ2FpbnN0IGNyYWNraW5nLlxuICAgICAgc2VlZCA9IHJhbmRvbUJ5dGVzKDUxMiAvIDgpO1xuICAgIH1cbiAgICBjb25zdCBleHRlbmRlZEtleSA9IGJpcDMyLmZyb21TZWVkKHNlZWQpO1xuICAgIGNvbnN0IHhwdWIgPSBleHRlbmRlZEtleS5uZXV0ZXJlZCgpLnRvQmFzZTU4KCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHB1YjogeHB1YixcbiAgICAgIHBydjogZXh0ZW5kZWRLZXkudG9CYXNlNTgoKSxcbiAgICB9O1xuICB9XG5cbiAgYXN5bmMgcGFyc2VUcmFuc2FjdGlvbihwYXJhbXM6IFBhcnNlVHJhbnNhY3Rpb25PcHRpb25zKTogUHJvbWlzZTxQYXJzZWRUcmFuc2FjdGlvbj4ge1xuICAgIHJldHVybiB7fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNYWtlIHN1cmUgYW4gYWRkcmVzcyBpcyBhIHdhbGxldCBhZGRyZXNzIGFuZCB0aHJvdyBhbiBlcnJvciBpZiBpdCdzIG5vdC5cbiAgICogQHBhcmFtIHtPYmplY3R9IHBhcmFtc1xuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmFkZHJlc3MgLSBUaGUgZGVyaXZlZCBhZGRyZXNzIHN0cmluZyBvbiB0aGUgbmV0d29ya1xuICAgKiBAcGFyYW0ge09iamVjdH0gcGFyYW1zLmNvaW5TcGVjaWZpYyAtIENvaW4tc3BlY2lmaWMgZGV0YWlscyBmb3IgdGhlIGFkZHJlc3Mgc3VjaCBhcyBhIGZvcndhcmRlclZlcnNpb25cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iYXNlQWRkcmVzcyAtIFRoZSBiYXNlIGFkZHJlc3Mgb2YgdGhlIHdhbGxldCBvbiB0aGUgbmV0d29ya1xuICAgKiBAdGhyb3dzIHtJbnZhbGlkQWRkcmVzc0Vycm9yfVxuICAgKiBAdGhyb3dzIHtJbnZhbGlkQWRkcmVzc1ZlcmlmaWNhdGlvbk9iamVjdFByb3BlcnR5RXJyb3J9XG4gICAqIEB0aHJvd3Mge1VuZXhwZWN0ZWRBZGRyZXNzRXJyb3J9XG4gICAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmZiBhZGRyZXNzIGlzIGEgd2FsbGV0IGFkZHJlc3NcbiAgICovXG4gIGFzeW5jIGlzV2FsbGV0QWRkcmVzcyhwYXJhbXM6IFZlcmlmeUV0aEFkZHJlc3NPcHRpb25zKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3QgZXRoVXRpbCA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsO1xuXG4gICAgbGV0IGV4cGVjdGVkQWRkcmVzcztcbiAgICBsZXQgYWN0dWFsQWRkcmVzcztcblxuICAgIGNvbnN0IHsgYWRkcmVzcywgY29pblNwZWNpZmljLCBiYXNlQWRkcmVzcywgaW1wbGllZEZvcndhcmRlclZlcnNpb24gPSBjb2luU3BlY2lmaWM/LmZvcndhcmRlclZlcnNpb24gfSA9IHBhcmFtcztcblxuICAgIGlmIChhZGRyZXNzICYmICF0aGlzLmlzVmFsaWRBZGRyZXNzKGFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgSW52YWxpZEFkZHJlc3NFcnJvcihgaW52YWxpZCBhZGRyZXNzOiAke2FkZHJlc3N9YCk7XG4gICAgfVxuXG4gICAgLy8gYmFzZSBhZGRyZXNzIGlzIHJlcXVpcmVkIHRvIGNhbGN1bGF0ZSB0aGUgc2FsdCB3aGljaCBpcyB1c2VkIGluIGNhbGN1bGF0ZUZvcndhcmRlclYxQWRkcmVzcyBtZXRob2RcbiAgICBpZiAoXy5pc1VuZGVmaW5lZChiYXNlQWRkcmVzcykgfHwgIXRoaXMuaXNWYWxpZEFkZHJlc3MoYmFzZUFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgSW52YWxpZEFkZHJlc3NFcnJvcignaW52YWxpZCBiYXNlIGFkZHJlc3MnKTtcbiAgICB9XG5cbiAgICBpZiAoIV8uaXNPYmplY3QoY29pblNwZWNpZmljKSkge1xuICAgICAgdGhyb3cgbmV3IEludmFsaWRBZGRyZXNzVmVyaWZpY2F0aW9uT2JqZWN0UHJvcGVydHlFcnJvcihcbiAgICAgICAgJ2FkZHJlc3MgdmFsaWRhdGlvbiBmYWlsdXJlOiBjb2luU3BlY2lmaWMgZmllbGQgbXVzdCBiZSBhbiBvYmplY3QnXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChpbXBsaWVkRm9yd2FyZGVyVmVyc2lvbiA9PT0gMCB8fCBpbXBsaWVkRm9yd2FyZGVyVmVyc2lvbiA9PT0gMyB8fCBpbXBsaWVkRm9yd2FyZGVyVmVyc2lvbiA9PT0gNSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGV0aE5ldHdvcmsgPSB0aGlzLmdldE5ldHdvcmsoKTtcbiAgICAgIGNvbnN0IGZvcndhcmRlckZhY3RvcnlBZGRyZXNzID0gZXRoTmV0d29yaz8uZm9yd2FyZGVyRmFjdG9yeUFkZHJlc3MgYXMgc3RyaW5nO1xuICAgICAgY29uc3QgZm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzID0gZXRoTmV0d29yaz8uZm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzIGFzIHN0cmluZztcblxuICAgICAgY29uc3QgaW5pdGNvZGUgPSBnZXRQcm94eUluaXRjb2RlKGZvcndhcmRlckltcGxlbWVudGF0aW9uQWRkcmVzcyk7XG4gICAgICBjb25zdCBzYWx0QnVmZmVyID0gZXRoVXRpbC5zZXRMZW5ndGhMZWZ0KFxuICAgICAgICBCdWZmZXIuZnJvbShldGhVdGlsLnBhZFRvRXZlbihldGhVdGlsLnN0cmlwSGV4UHJlZml4KGNvaW5TcGVjaWZpYy5zYWx0IHx8ICcnKSksICdoZXgnKSxcbiAgICAgICAgMzJcbiAgICAgICk7XG5cbiAgICAgIC8vIEhhc2ggdGhlIHdhbGxldCBiYXNlIGFkZHJlc3Mgd2l0aCB0aGUgZ2l2ZW4gc2FsdCwgc28gdGhlIGFkZHJlc3MgZGlyZWN0bHkgcmVsaWVzIG9uIHRoZSBiYXNlIGFkZHJlc3NcbiAgICAgIGNvbnN0IGNhbGN1bGF0aW9uU2FsdCA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSGV4KFxuICAgICAgICBvcHRpb25hbERlcHMuZXRoQWJpLnNvbGlkaXR5U0hBMyhbJ2FkZHJlc3MnLCAnYnl0ZXMzMiddLCBbYmFzZUFkZHJlc3MsIHNhbHRCdWZmZXJdKVxuICAgICAgKTtcblxuICAgICAgZXhwZWN0ZWRBZGRyZXNzID0gY2FsY3VsYXRlRm9yd2FyZGVyVjFBZGRyZXNzKGZvcndhcmRlckZhY3RvcnlBZGRyZXNzLCBjYWxjdWxhdGlvblNhbHQsIGluaXRjb2RlKTtcbiAgICAgIGFjdHVhbEFkZHJlc3MgPSBhZGRyZXNzO1xuICAgIH1cblxuICAgIGlmIChleHBlY3RlZEFkZHJlc3MgIT09IGFjdHVhbEFkZHJlc3MpIHtcbiAgICAgIHRocm93IG5ldyBVbmV4cGVjdGVkQWRkcmVzc0Vycm9yKGBhZGRyZXNzIHZhbGlkYXRpb24gZmFpbHVyZTogZXhwZWN0ZWQgJHtleHBlY3RlZEFkZHJlc3N9IGJ1dCBnb3QgJHthZGRyZXNzfWApO1xuICAgIH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqXG4gICAqIEBwYXJhbSB7VHJhbnNhY3Rpb25QcmVidWlsZH0gdHhQcmVidWlsZFxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIHZlcmlmeUNvaW4odHhQcmVidWlsZDogVHJhbnNhY3Rpb25QcmVidWlsZCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IG5hdGl2ZUNvaW4gPSB0aGlzLmdldENoYWluKCkuc3BsaXQoJzonKVswXTtcbiAgICByZXR1cm4gdHhQcmVidWlsZC5jb2luID09PSBuYXRpdmVDb2luO1xuICB9XG5cbiAgLyoqXG4gICAqIFZlcmlmeSBpZiBhIHRzcyB0cmFuc2FjdGlvbiBpcyB2YWxpZFxuICAgKlxuICAgKiBAcGFyYW0ge1ZlcmlmeUV0aFRyYW5zYWN0aW9uT3B0aW9uc30gcGFyYW1zXG4gICAqIEBwYXJhbSB7VHJhbnNhY3Rpb25QYXJhbXN9IHBhcmFtcy50eFBhcmFtcyAtIHBhcmFtcyBvYmplY3QgcGFzc2VkIHRvIHNlbmRcbiAgICogQHBhcmFtIHtUcmFuc2FjdGlvblByZWJ1aWxkfSBwYXJhbXMudHhQcmVidWlsZCAtIHByZWJ1aWxkIG9iamVjdCByZXR1cm5lZCBieSBzZXJ2ZXJcbiAgICogQHBhcmFtIHtXYWxsZXR9IHBhcmFtcy53YWxsZXQgLSBXYWxsZXQgb2JqZWN0IHRvIG9idGFpbiBrZXlzIHRvIHZlcmlmeSBhZ2FpbnN0XG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgYXN5bmMgdmVyaWZ5VHNzVHJhbnNhY3Rpb24ocGFyYW1zOiBWZXJpZnlFdGhUcmFuc2FjdGlvbk9wdGlvbnMpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCB7IHR4UGFyYW1zLCB0eFByZWJ1aWxkLCB3YWxsZXQgfSA9IHBhcmFtcztcbiAgICBpZiAoXG4gICAgICAhdHhQYXJhbXM/LnJlY2lwaWVudHMgJiZcbiAgICAgICEoXG4gICAgICAgIHR4UGFyYW1zLnByZWJ1aWxkVHg/LmNvbnNvbGlkYXRlSWQgfHxcbiAgICAgICAgKHR4UGFyYW1zLnR5cGUgJiYgWydhY2NlbGVyYXRpb24nLCAnZmlsbE5vbmNlJywgJ3RyYW5zZmVyVG9rZW4nLCAndG9rZW5BcHByb3ZhbCddLmluY2x1ZGVzKHR4UGFyYW1zLnR5cGUpKVxuICAgICAgKVxuICAgICkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBtaXNzaW5nIHR4UGFyYW1zYCk7XG4gICAgfVxuICAgIGlmICghd2FsbGV0IHx8ICF0eFByZWJ1aWxkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYG1pc3NpbmcgcGFyYW1zYCk7XG4gICAgfVxuICAgIGlmICh0eFBhcmFtcy5ob3AgJiYgdHhQYXJhbXMucmVjaXBpZW50cyAmJiB0eFBhcmFtcy5yZWNpcGllbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgdHggY2Fubm90IGJlIGJvdGggYSBiYXRjaCBhbmQgaG9wIHRyYW5zYWN0aW9uYCk7XG4gICAgfVxuXG4gICAgaWYgKHR4UGFyYW1zLnR5cGUgJiYgWyd0cmFuc2ZlciddLmluY2x1ZGVzKHR4UGFyYW1zLnR5cGUpKSB7XG4gICAgICBpZiAodHhQYXJhbXMucmVjaXBpZW50cyAmJiB0eFBhcmFtcy5yZWNpcGllbnRzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICBjb25zdCByZWNpcGllbnRzID0gdHhQYXJhbXMucmVjaXBpZW50cztcbiAgICAgICAgY29uc3QgZXhwZWN0ZWRBbW91bnQgPSByZWNpcGllbnRzWzBdLmFtb3VudC50b1N0cmluZygpO1xuICAgICAgICBjb25zdCBleHBlY3RlZERlc3RpbmF0aW9uID0gcmVjaXBpZW50c1swXS5hZGRyZXNzO1xuXG4gICAgICAgIGNvbnN0IHR4QnVpbGRlciA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25CdWlsZGVyKCk7XG4gICAgICAgIHR4QnVpbGRlci5mcm9tKHR4UHJlYnVpbGQudHhIZXgpO1xuICAgICAgICBjb25zdCB0eCA9IGF3YWl0IHR4QnVpbGRlci5idWlsZCgpO1xuICAgICAgICBjb25zdCB0eEpzb24gPSB0eC50b0pzb24oKTtcbiAgICAgICAgaWYgKHR4SnNvbi5kYXRhID09PSAnMHgnKSB7XG4gICAgICAgICAgaWYgKGV4cGVjdGVkQW1vdW50ICE9PSB0eEpzb24udmFsdWUpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcigndGhlIHRyYW5zYWN0aW9uIGFtb3VudCBpbiB0eFByZWJ1aWxkIGRvZXMgbm90IG1hdGNoIHRoZSB2YWx1ZSBnaXZlbiBieSBjbGllbnQnKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGV4cGVjdGVkRGVzdGluYXRpb24udG9Mb3dlckNhc2UoKSAhPT0gdHhKc29uLnRvLnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignZGVzdGluYXRpb24gYWRkcmVzcyBkb2VzIG5vdCBtYXRjaCB3aXRoIHRoZSByZWNpcGllbnQgYWRkcmVzcycpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmICh0eEpzb24uZGF0YS5zdGFydHNXaXRoKCcweGE5MDU5Y2JiJykpIHtcbiAgICAgICAgICBjb25zdCBbcmVjaXBpZW50QWRkcmVzcywgYW1vdW50XSA9IGdldFJhd0RlY29kZWQoXG4gICAgICAgICAgICBbJ2FkZHJlc3MnLCAndWludDI1NiddLFxuICAgICAgICAgICAgZ2V0QnVmZmVyZWRCeXRlQ29kZSgnMHhhOTA1OWNiYicsIHR4SnNvbi5kYXRhKVxuICAgICAgICAgICk7XG4gICAgICAgICAgaWYgKGV4cGVjdGVkQW1vdW50ICE9PSBhbW91bnQudG9TdHJpbmcoKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCd0aGUgdHJhbnNhY3Rpb24gYW1vdW50IGluIHR4UHJlYnVpbGQgZG9lcyBub3QgbWF0Y2ggdGhlIHZhbHVlIGdpdmVuIGJ5IGNsaWVudCcpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZXhwZWN0ZWREZXN0aW5hdGlvbi50b0xvd2VyQ2FzZSgpICE9PSBhZGRIZXhQcmVmaXgocmVjaXBpZW50QWRkcmVzcy50b1N0cmluZygpKS50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2Rlc3RpbmF0aW9uIGFkZHJlc3MgZG9lcyBub3QgbWF0Y2ggd2l0aCB0aGUgcmVjaXBpZW50IGFkZHJlc3MnKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZnkgdGhhdCBhIHRyYW5zYWN0aW9uIHByZWJ1aWxkIGNvbXBsaWVzIHdpdGggdGhlIG9yaWdpbmFsIGludGVudGlvblxuICAgKlxuICAgKiBAcGFyYW0ge1ZlcmlmeUV0aFRyYW5zYWN0aW9uT3B0aW9uc30gcGFyYW1zXG4gICAqIEBwYXJhbSB7VHJhbnNhY3Rpb25QYXJhbXN9IHBhcmFtcy50eFBhcmFtcyAtIHBhcmFtcyBvYmplY3QgcGFzc2VkIHRvIHNlbmRcbiAgICogQHBhcmFtIHtUcmFuc2FjdGlvblByZWJ1aWxkfSBwYXJhbXMudHhQcmVidWlsZCAtIHByZWJ1aWxkIG9iamVjdCByZXR1cm5lZCBieSBzZXJ2ZXJcbiAgICogQHBhcmFtIHtXYWxsZXR9IHBhcmFtcy53YWxsZXQgLSBXYWxsZXQgb2JqZWN0IHRvIG9idGFpbiBrZXlzIHRvIHZlcmlmeSBhZ2FpbnN0XG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgYXN5bmMgdmVyaWZ5VHJhbnNhY3Rpb24ocGFyYW1zOiBWZXJpZnlFdGhUcmFuc2FjdGlvbk9wdGlvbnMpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBldGhOZXR3b3JrID0gdGhpcy5nZXROZXR3b3JrKCk7XG4gICAgY29uc3QgeyB0eFBhcmFtcywgdHhQcmVidWlsZCwgd2FsbGV0LCB3YWxsZXRUeXBlIH0gPSBwYXJhbXM7XG5cbiAgICBpZiAod2FsbGV0VHlwZSA9PT0gJ3RzcycpIHtcbiAgICAgIHJldHVybiB0aGlzLnZlcmlmeVRzc1RyYW5zYWN0aW9uKHBhcmFtcyk7XG4gICAgfVxuXG4gICAgaWYgKCF0eFBhcmFtcz8ucmVjaXBpZW50cyB8fCAhdHhQcmVidWlsZD8ucmVjaXBpZW50cyB8fCAhd2FsbGV0KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYG1pc3NpbmcgcGFyYW1zYCk7XG4gICAgfVxuICAgIGlmICh0eFBhcmFtcy5ob3AgJiYgdHhQYXJhbXMucmVjaXBpZW50cy5sZW5ndGggPiAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYHR4IGNhbm5vdCBiZSBib3RoIGEgYmF0Y2ggYW5kIGhvcCB0cmFuc2FjdGlvbmApO1xuICAgIH1cbiAgICBpZiAodHhQcmVidWlsZC5yZWNpcGllbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYCR7dGhpcy5nZXRDaGFpbigpfSBkb2Vzbid0IHN1cHBvcnQgc2VuZGluZyB0byBtb3JlIHRoYW4gMSBkZXN0aW5hdGlvbiBhZGRyZXNzIHdpdGhpbiBhIHNpbmdsZSB0cmFuc2FjdGlvbi4gVHJ5IGFnYWluLCB1c2luZyBvbmx5IGEgc2luZ2xlIHJlY2lwaWVudC5gXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAodHhQYXJhbXMuaG9wICYmIHR4UHJlYnVpbGQuaG9wVHJhbnNhY3Rpb24pIHtcbiAgICAgIC8vIENoZWNrIHJlY2lwaWVudCBhbW91bnQgZm9yIGhvcCB0cmFuc2FjdGlvblxuICAgICAgaWYgKHR4UGFyYW1zLnJlY2lwaWVudHMubGVuZ3RoICE9PSAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgaG9wIHRyYW5zYWN0aW9uIG9ubHkgc3VwcG9ydHMgMSByZWNpcGllbnQgYnV0ICR7dHhQYXJhbXMucmVjaXBpZW50cy5sZW5ndGh9IGZvdW5kYCk7XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIHR4IHNlbmRzIHRvIGhvcCBhZGRyZXNzXG4gICAgICBjb25zdCBkZWNvZGVkSG9wVHggPSBvcHRpb25hbERlcHMuRXRoVHguVHJhbnNhY3Rpb25GYWN0b3J5LmZyb21TZXJpYWxpemVkRGF0YShcbiAgICAgICAgb3B0aW9uYWxEZXBzLmV0aFV0aWwudG9CdWZmZXIodHhQcmVidWlsZC5ob3BUcmFuc2FjdGlvbi50eClcbiAgICAgICk7XG4gICAgICBjb25zdCBleHBlY3RlZEhvcEFkZHJlc3MgPSBvcHRpb25hbERlcHMuZXRoVXRpbC5zdHJpcEhleFByZWZpeChkZWNvZGVkSG9wVHguZ2V0U2VuZGVyQWRkcmVzcygpLnRvU3RyaW5nKCkpO1xuICAgICAgY29uc3QgYWN0dWFsSG9wQWRkcmVzcyA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hZGRyZXNzKTtcbiAgICAgIGlmIChleHBlY3RlZEhvcEFkZHJlc3MudG9Mb3dlckNhc2UoKSAhPT0gYWN0dWFsSG9wQWRkcmVzcy50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcigncmVjaXBpZW50IGFkZHJlc3Mgb2YgdHhQcmVidWlsZCBkb2VzIG5vdCBtYXRjaCBob3AgYWRkcmVzcycpO1xuICAgICAgfVxuXG4gICAgICAvLyBDb252ZXJ0IFRyYW5zYWN0aW9uUmVjaXBpZW50IGFycmF5IHRvIFJlY2lwaWVudCBhcnJheVxuICAgICAgY29uc3QgcmVjaXBpZW50czogUmVjaXBpZW50W10gPSB0eFBhcmFtcy5yZWNpcGllbnRzLm1hcCgocikgPT4ge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGFkZHJlc3M6IHIuYWRkcmVzcyxcbiAgICAgICAgICBhbW91bnQ6IHR5cGVvZiByLmFtb3VudCA9PT0gJ251bWJlcicgPyByLmFtb3VudC50b1N0cmluZygpIDogci5hbW91bnQsXG4gICAgICAgIH07XG4gICAgICB9KTtcblxuICAgICAgLy8gQ2hlY2sgZGVzdGluYXRpb24gYWRkcmVzcyBhbmQgYW1vdW50XG4gICAgICBhd2FpdCB0aGlzLnZhbGlkYXRlSG9wUHJlYnVpbGQod2FsbGV0LCB0eFByZWJ1aWxkLmhvcFRyYW5zYWN0aW9uLCB7IHJlY2lwaWVudHMgfSk7XG4gICAgfSBlbHNlIGlmICh0eFBhcmFtcy5yZWNpcGllbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgIC8vIENoZWNrIHRvdGFsIGFtb3VudCBmb3IgYmF0Y2ggdHJhbnNhY3Rpb25cbiAgICAgIGlmICh0eFBhcmFtcy50b2tlbk5hbWUpIHtcbiAgICAgICAgY29uc3QgZXhwZWN0ZWRUb3RhbEFtb3VudCA9IG5ldyBCaWdOdW1iZXIoMCk7XG4gICAgICAgIGlmICghZXhwZWN0ZWRUb3RhbEFtb3VudC5pc0VxdWFsVG8odHhQcmVidWlsZC5yZWNpcGllbnRzWzBdLmFtb3VudCkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2JhdGNoIHRva2VuIHRyYW5zYWN0aW9uIGFtb3VudCBpbiB0eFByZWJ1aWxkIHNob3VsZCBiZSB6ZXJvIGZvciB0b2tlbiB0cmFuc2ZlcnMnKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbGV0IGV4cGVjdGVkVG90YWxBbW91bnQgPSBuZXcgQmlnTnVtYmVyKDApO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHR4UGFyYW1zLnJlY2lwaWVudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICBleHBlY3RlZFRvdGFsQW1vdW50ID0gZXhwZWN0ZWRUb3RhbEFtb3VudC5wbHVzKHR4UGFyYW1zLnJlY2lwaWVudHNbaV0uYW1vdW50KTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWV4cGVjdGVkVG90YWxBbW91bnQuaXNFcXVhbFRvKHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hbW91bnQpKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgJ2JhdGNoIHRyYW5zYWN0aW9uIGFtb3VudCBpbiB0eFByZWJ1aWxkIHJlY2VpdmVkIGZyb20gQml0R28gc2VydmVycyBkb2VzIG5vdCBtYXRjaCB0eFBhcmFtcyBzdXBwbGllZCBieSBjbGllbnQnXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBDaGVjayBiYXRjaCB0cmFuc2FjdGlvbiBpcyBzZW50IHRvIHRoZSBiYXRjaGVyIGNvbnRyYWN0IGFkZHJlc3MgZm9yIHRoZSBjaGFpblxuICAgICAgY29uc3QgYmF0Y2hlckNvbnRyYWN0QWRkcmVzcyA9IGV0aE5ldHdvcms/LmJhdGNoZXJDb250cmFjdEFkZHJlc3M7XG4gICAgICBpZiAoXG4gICAgICAgICFiYXRjaGVyQ29udHJhY3RBZGRyZXNzIHx8XG4gICAgICAgIGJhdGNoZXJDb250cmFjdEFkZHJlc3MudG9Mb3dlckNhc2UoKSAhPT0gdHhQcmVidWlsZC5yZWNpcGllbnRzWzBdLmFkZHJlc3MudG9Mb3dlckNhc2UoKVxuICAgICAgKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcigncmVjaXBpZW50IGFkZHJlc3Mgb2YgdHhQcmVidWlsZCBkb2VzIG5vdCBtYXRjaCBiYXRjaGVyIGFkZHJlc3MnKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gQ2hlY2sgcmVjaXBpZW50IGFkZHJlc3MgYW5kIGFtb3VudCBmb3Igbm9ybWFsIHRyYW5zYWN0aW9uXG4gICAgICBpZiAodHhQYXJhbXMucmVjaXBpZW50cy5sZW5ndGggIT09IDEpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBub3JtYWwgdHJhbnNhY3Rpb24gb25seSBzdXBwb3J0cyAxIHJlY2lwaWVudCBidXQgJHt0eFBhcmFtcy5yZWNpcGllbnRzLmxlbmd0aH0gZm91bmRgKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGV4cGVjdGVkQW1vdW50ID0gbmV3IEJpZ051bWJlcih0eFBhcmFtcy5yZWNpcGllbnRzWzBdLmFtb3VudCk7XG4gICAgICBpZiAoIWV4cGVjdGVkQW1vdW50LmlzRXF1YWxUbyh0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYW1vdW50KSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ25vcm1hbCB0cmFuc2FjdGlvbiBhbW91bnQgaW4gdHhQcmVidWlsZCByZWNlaXZlZCBmcm9tIEJpdEdvIHNlcnZlcnMgZG9lcyBub3QgbWF0Y2ggdHhQYXJhbXMgc3VwcGxpZWQgYnkgY2xpZW50J1xuICAgICAgICApO1xuICAgICAgfVxuICAgICAgaWYgKFxuICAgICAgICB0aGlzLmlzRVRIQWRkcmVzcyh0eFBhcmFtcy5yZWNpcGllbnRzWzBdLmFkZHJlc3MpICYmXG4gICAgICAgIHR4UGFyYW1zLnJlY2lwaWVudHNbMF0uYWRkcmVzcyAhPT0gdHhQcmVidWlsZC5yZWNpcGllbnRzWzBdLmFkZHJlc3NcbiAgICAgICkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2Rlc3RpbmF0aW9uIGFkZHJlc3MgaW4gbm9ybWFsIHR4UHJlYnVpbGQgZG9lcyBub3QgbWF0Y2ggdGhhdCBpbiB0eFBhcmFtcyBzdXBwbGllZCBieSBjbGllbnQnKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gQ2hlY2sgY29pbiBpcyBjb3JyZWN0IGZvciBhbGwgdHJhbnNhY3Rpb24gdHlwZXNcbiAgICBpZiAoIXRoaXMudmVyaWZ5Q29pbih0eFByZWJ1aWxkKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBjb2luIGluIHR4UHJlYnVpbGQgZGlkIG5vdCBtYXRjaCB0aGF0IGluIHR4UGFyYW1zIHN1cHBsaWVkIGJ5IGNsaWVudGApO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBhZGRyZXNzIGlzIHZhbGlkIGV0aCBhZGRyZXNzXG4gICAqIEBwYXJhbSBhZGRyZXNzXG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgcHJpdmF0ZSBpc0VUSEFkZHJlc3MoYWRkcmVzczogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICEhYWRkcmVzcy5tYXRjaCgvMHhbYS1mQS1GMC05XXs0MH0vKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUcmFuc2Zvcm0gbWVzc2FnZSB0byBhY2NvbW1vZGF0ZSBzcGVjaWZpYyBibG9ja2NoYWluIHJlcXVpcmVtZW50cy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2UgLSB0aGUgbWVzc2FnZSB0byBwcmVwYXJlXG4gICAqIEByZXR1cm4ge3N0cmluZ30gdGhlIHByZXBhcmVkIG1lc3NhZ2UgYXMgYSBoZXggZW5jb2RlZCBzdHJpbmcuXG4gICAqL1xuICBlbmNvZGVNZXNzYWdlKG1lc3NhZ2U6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgcHJlZml4ID0gYFxcdTAwMTlFdGhlcmV1bSBTaWduZWQgTWVzc2FnZTpcXG4ke21lc3NhZ2UubGVuZ3RofWA7XG4gICAgcmV0dXJuIEJ1ZmZlci5mcm9tKHByZWZpeC5jb25jYXQobWVzc2FnZSkpLnRvU3RyaW5nKCdoZXgnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUcmFuc2Zvcm0gdGhlIFR5cGVkIGRhdGEgdG8gYWNjb21vZGF0ZSB0aGUgYmxvY2tjaGFpbiByZXF1aXJlbWVudHMgKEVJUC03MTIpXG4gICAqIEBwYXJhbSB7VHlwZWREYXRhfSB0eXBlZERhdGEgLSB0aGUgdHlwZWQgZGF0YSB0byBwcmVwYXJlXG4gICAqIEByZXR1cm4ge0J1ZmZlcn0gYSBidWZmZXIgb2YgdGhlIHJlc3VsdFxuICAgKi9cbiAgZW5jb2RlVHlwZWREYXRhKHR5cGVkRGF0YTogVHlwZWREYXRhKTogQnVmZmVyIHtcbiAgICBjb25zdCB2ZXJzaW9uID0gdHlwZWREYXRhLnZlcnNpb247XG4gICAgaWYgKHZlcnNpb24gPT09IFNpZ25UeXBlZERhdGFWZXJzaW9uLlYxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NpZ25UeXBlZERhdGEgdjEgaXMgbm90IHN1cHBvcnRlZCBkdWUgdG8gc2VjdXJpdHkgY29uY2VybnMnKTtcbiAgICB9XG4gICAgY29uc3QgdHlwZWREYXRhUmF3ID0gSlNPTi5wYXJzZSh0eXBlZERhdGEudHlwZWREYXRhUmF3KTtcbiAgICBjb25zdCBzYW5pdGl6ZWREYXRhID0gVHlwZWREYXRhVXRpbHMuc2FuaXRpemVEYXRhKHR5cGVkRGF0YVJhdyBhcyB1bmtub3duIGFzIFR5cGVkTWVzc2FnZTxhbnk+KTtcbiAgICBjb25zdCBwYXJ0cyA9IFtCdWZmZXIuZnJvbSgnMTkwMScsICdoZXgnKV07XG4gICAgY29uc3QgZWlwNzEyRG9tYWluID0gJ0VJUDcxMkRvbWFpbic7XG4gICAgcGFydHMucHVzaChUeXBlZERhdGFVdGlscy5oYXNoU3RydWN0KGVpcDcxMkRvbWFpbiwgc2FuaXRpemVkRGF0YS5kb21haW4sIHNhbml0aXplZERhdGEudHlwZXMsIHZlcnNpb24pKTtcblxuICAgIGlmIChzYW5pdGl6ZWREYXRhLnByaW1hcnlUeXBlICE9PSBlaXA3MTJEb21haW4pIHtcbiAgICAgIHBhcnRzLnB1c2goXG4gICAgICAgIFR5cGVkRGF0YVV0aWxzLmhhc2hTdHJ1Y3QoXG4gICAgICAgICAgc2FuaXRpemVkRGF0YS5wcmltYXJ5VHlwZSBhcyBzdHJpbmcsXG4gICAgICAgICAgc2FuaXRpemVkRGF0YS5tZXNzYWdlLFxuICAgICAgICAgIHNhbml0aXplZERhdGEudHlwZXMsXG4gICAgICAgICAgdmVyc2lvblxuICAgICAgICApXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gQnVmZmVyLmNvbmNhdChwYXJ0cyk7XG4gIH1cblxuICAvKipcbiAgICogQnVpbGQgdGhlIGRhdGEgdG8gdHJhbnNmZXIgYW4gRVJDLTcyMSBvciBFUkMtMTE1NSB0b2tlbiB0byBhbm90aGVyIGFkZHJlc3NcbiAgICogQHBhcmFtIHBhcmFtc1xuICAgKi9cbiAgYnVpbGROZnRUcmFuc2ZlckRhdGEocGFyYW1zOiBCdWlsZE5mdFRyYW5zZmVyRGF0YU9wdGlvbnMpOiBzdHJpbmcge1xuICAgIGNvbnN0IHsgdG9rZW5Db250cmFjdEFkZHJlc3MsIHJlY2lwaWVudEFkZHJlc3MsIGZyb21BZGRyZXNzIH0gPSBwYXJhbXM7XG4gICAgc3dpdGNoIChwYXJhbXMudHlwZSkge1xuICAgICAgY2FzZSAnRVJDNzIxJzoge1xuICAgICAgICBjb25zdCB0b2tlbklkID0gcGFyYW1zLnRva2VuSWQ7XG4gICAgICAgIGNvbnN0IGNvbnRyYWN0RGF0YSA9IG5ldyBFUkM3MjFUcmFuc2ZlckJ1aWxkZXIoKVxuICAgICAgICAgIC50b2tlbkNvbnRyYWN0QWRkcmVzcyh0b2tlbkNvbnRyYWN0QWRkcmVzcylcbiAgICAgICAgICAudG8ocmVjaXBpZW50QWRkcmVzcylcbiAgICAgICAgICAuZnJvbShmcm9tQWRkcmVzcylcbiAgICAgICAgICAudG9rZW5JZCh0b2tlbklkKVxuICAgICAgICAgIC5idWlsZCgpO1xuICAgICAgICByZXR1cm4gY29udHJhY3REYXRhO1xuICAgICAgfVxuXG4gICAgICBjYXNlICdFUkMxMTU1Jzoge1xuICAgICAgICBjb25zdCBlbnRyaWVzID0gcGFyYW1zLmVudHJpZXM7XG4gICAgICAgIGNvbnN0IHRyYW5zZmVyQnVpbGRlciA9IG5ldyBFUkMxMTU1VHJhbnNmZXJCdWlsZGVyKClcbiAgICAgICAgICAudG9rZW5Db250cmFjdEFkZHJlc3ModG9rZW5Db250cmFjdEFkZHJlc3MpXG4gICAgICAgICAgLnRvKHJlY2lwaWVudEFkZHJlc3MpXG4gICAgICAgICAgLmZyb20oZnJvbUFkZHJlc3MpO1xuXG4gICAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgICAgIHRyYW5zZmVyQnVpbGRlci5lbnRyeShwYXJzZUludChlbnRyeS50b2tlbklkLCAxMCksIGVudHJ5LmFtb3VudCk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdHJhbnNmZXJCdWlsZGVyLmJ1aWxkKCk7XG4gICAgICB9XG5cbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgTkZUIHR5cGU6ICR7cGFyYW1zLnR5cGV9YCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoIHRoZSBnYXMgcHJpY2UgZnJvbSB0aGUgZXhwbG9yZXJcbiAgICovXG4gIGFzeW5jIGdldEdhc1ByaWNlRnJvbUV4dGVybmFsQVBJKHdyb25nQ2hhaW5Db2luOiBzdHJpbmcpOiBQcm9taXNlPEJOPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IHRoaXMucmVjb3ZlcnlCbG9ja2NoYWluRXhwbG9yZXJRdWVyeSh7XG4gICAgICAgIGNoYWluaWQ6IHRoaXMuZ2V0Q2hhaW5JZCgpLnRvU3RyaW5nKCksXG4gICAgICAgIG1vZHVsZTogJ3Byb3h5JyxcbiAgICAgICAgYWN0aW9uOiAnZXRoX2dhc1ByaWNlJyxcbiAgICAgIH0pO1xuICAgICAgY29uc3QgZ2FzUHJpY2UgPSBuZXcgQk4ocmVzLnJlc3VsdC5zbGljZSgyKSwgMTYpO1xuICAgICAgY29uc29sZS5sb2coYCBHb3QgZ2FzIHByaWNlOiAke2dhc1ByaWNlfWApO1xuICAgICAgcmV0dXJuIGdhc1ByaWNlO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGdldCBnYXMgcHJpY2UuIFBsZWFzZSBtYWtlIHN1cmUgdG8gdXNlIHRoZSBhcGkga2V5IG9mICR7d3JvbmdDaGFpbkNvaW59YCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoIHRoZSBnYXMgbGltaXQgZnJvbSB0aGUgZXhwbG9yZXJcbiAgICogQHBhcmFtIGludGVuZGVkQ2hhaW5cbiAgICogQHBhcmFtIGZyb21cbiAgICogQHBhcmFtIHRvXG4gICAqIEBwYXJhbSBkYXRhXG4gICAqL1xuICBhc3luYyBnZXRHYXNMaW1pdEZyb21FeHRlcm5hbEFQSShpbnRlbmRlZENoYWluOiBzdHJpbmcsIGZyb206IHN0cmluZywgdG86IHN0cmluZywgZGF0YTogc3RyaW5nKTogUHJvbWlzZTxCTj4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXMgPSBhd2FpdCB0aGlzLnJlY292ZXJ5QmxvY2tjaGFpbkV4cGxvcmVyUXVlcnkoe1xuICAgICAgICBjaGFpbmlkOiB0aGlzLmdldENoYWluSWQoKS50b1N0cmluZygpLFxuICAgICAgICBtb2R1bGU6ICdwcm94eScsXG4gICAgICAgIGFjdGlvbjogJ2V0aF9lc3RpbWF0ZUdhcycsXG4gICAgICAgIGZyb20sXG4gICAgICAgIHRvLFxuICAgICAgICBkYXRhLFxuICAgICAgfSk7XG4gICAgICBjb25zdCBnYXNMaW1pdCA9IG5ldyBCTihyZXMucmVzdWx0LnNsaWNlKDIpLCAxNik7XG4gICAgICBjb25zb2xlLmxvZyhgR290IGdhcyBsaW1pdDogJHtnYXNMaW1pdH1gKTtcbiAgICAgIHJldHVybiBnYXNMaW1pdDtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBGYWlsZWQgdG8gZ2V0IGdhcyBsaW1pdC4gUGxlYXNlIG1ha2Ugc3VyZSB0byB1c2UgdGhlIHByaXZhdGVLZXkgYWthIHVzZXJLZXkgb2YgJHtpbnRlbmRlZENoYWlufSB3YWxsZXQgJHt0b31gXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIGJhbGFuY2Ugb2YgYml0Z29GZWVBZGRyZXNzIHRvIGVuc3VyZSBmdW5kcyBhcmUgYXZhaWxhYmxlIHRvIHBheSBmZWVzXG4gICAqIEBwYXJhbSBiaXRnb0ZlZUFkZHJlc3NcbiAgICogQHBhcmFtIGdhc1ByaWNlXG4gICAqIEBwYXJhbSBnYXNMaW1pdFxuICAgKi9cbiAgYXN5bmMgZW5zdXJlU3VmZmljaWVudEJhbGFuY2UoYml0Z29GZWVBZGRyZXNzOiBzdHJpbmcsIGdhc1ByaWNlOiBCTiwgZ2FzTGltaXQ6IEJOKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgYml0Z29GZWVBZGRyZXNzQmFsYW5jZSA9IGF3YWl0IHRoaXMucXVlcnlBZGRyZXNzQmFsYW5jZShiaXRnb0ZlZUFkZHJlc3MpO1xuICAgIGNvbnN0IHRvdGFsR2FzTmVlZGVkID0gTnVtYmVyKGdhc1ByaWNlLm11bChnYXNMaW1pdCkpO1xuICAgIGNvbnN0IHdlaVRvR3dlaSA9IDEwICoqIDk7XG4gICAgaWYgKGJpdGdvRmVlQWRkcmVzc0JhbGFuY2UubHQodG90YWxHYXNOZWVkZWQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBGZWUgYWRkcmVzcyAke2JpdGdvRmVlQWRkcmVzc30gaGFzIGJhbGFuY2UgJHsoYml0Z29GZWVBZGRyZXNzQmFsYW5jZSAvIHdlaVRvR3dlaSkudG9TdHJpbmcoKX0gR3dlaS5gICtcbiAgICAgICAgICBgVGhpcyBhZGRyZXNzIG11c3QgaGF2ZSBhIGJhbGFuY2Ugb2YgYXQgbGVhc3QgJHsodG90YWxHYXNOZWVkZWQgLyB3ZWlUb0d3ZWkpLnRvU3RyaW5nKCl9YCArXG4gICAgICAgICAgYCBHd2VpIHRvIHBlcmZvcm0gcmVjb3Zlcmllcy4gVHJ5IHNlbmRpbmcgc29tZSAke3RoaXMuZ2V0Q2hhaW4oKX0gdG8gdGhpcyBhZGRyZXNzIHRoZW4gcmV0cnkuYFxuICAgICAgKTtcbiAgICB9XG4gIH1cbn1cbiJdfQ==
|