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