@bitgo-beta/abstract-eth 1.0.2-beta.235 → 1.0.2-beta.2350
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/dist/src/abstractEthLikeCoin.d.ts +20 -9
- package/dist/src/abstractEthLikeCoin.d.ts.map +1 -1
- package/dist/src/abstractEthLikeCoin.js +46 -15
- package/dist/src/abstractEthLikeNewCoins.d.ts +978 -0
- package/dist/src/abstractEthLikeNewCoins.d.ts.map +1 -0
- package/dist/src/abstractEthLikeNewCoins.js +3072 -0
- package/dist/src/ethLikeToken.d.ts +38 -6
- package/dist/src/ethLikeToken.d.ts.map +1 -1
- package/dist/src/ethLikeToken.js +293 -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/constants.d.ts +4 -0
- package/dist/src/lib/constants.d.ts.map +1 -0
- package/dist/src/lib/constants.js +11 -0
- 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/decryptionDelegationBuilder.d.ts +90 -0
- package/dist/src/lib/decryptionDelegationBuilder.d.ts.map +1 -0
- package/dist/src/lib/decryptionDelegationBuilder.js +59 -0
- package/dist/src/lib/iface.d.ts +173 -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 +19 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +60 -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 +69 -0
- package/dist/src/lib/transaction.d.ts.map +1 -0
- package/dist/src/lib/transaction.js +149 -0
- package/dist/src/lib/transactionBuilder.d.ts +388 -0
- package/dist/src/lib/transactionBuilder.d.ts.map +1 -0
- package/dist/src/lib/transactionBuilder.js +1120 -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 +5 -0
- package/dist/src/lib/transferBuilders/index.d.ts.map +1 -0
- package/dist/src/lib/transferBuilders/index.js +21 -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 +21 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC721.d.ts.map +1 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC721.js +91 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC7984.d.ts +35 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC7984.d.ts.map +1 -0
- package/dist/src/lib/transferBuilders/transferBuilderERC7984.js +113 -0
- package/dist/src/lib/types.d.ts +40 -0
- package/dist/src/lib/types.d.ts.map +1 -0
- package/dist/src/lib/types.js +140 -0
- package/dist/src/lib/utils.d.ts +386 -0
- package/dist/src/lib/utils.d.ts.map +1 -0
- package/dist/src/lib/utils.js +1026 -0
- package/dist/src/lib/walletUtil.d.ts +46 -0
- package/dist/src/lib/walletUtil.d.ts.map +1 -0
- package/dist/src/lib/walletUtil.js +53 -0
- package/dist/src/lib/zamaUtils.d.ts +257 -0
- package/dist/src/lib/zamaUtils.d.ts.map +1 -0
- package/dist/src/lib/zamaUtils.js +527 -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/decryptionDelegationBuilder.d.ts +2 -0
- package/dist/test/unit/decryptionDelegationBuilder.d.ts.map +1 -0
- package/dist/test/unit/decryptionDelegationBuilder.js +265 -0
- package/dist/test/unit/index.d.ts +8 -0
- package/dist/test/unit/index.d.ts.map +1 -0
- package/dist/test/unit/index.js +24 -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 +73 -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/decryptionDelegation.d.ts +3 -0
- package/dist/test/unit/transactionBuilder/decryptionDelegation.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/decryptionDelegation.js +161 -0
- package/dist/test/unit/transactionBuilder/finalizeUnwrapERC7984.d.ts +3 -0
- package/dist/test/unit/transactionBuilder/finalizeUnwrapERC7984.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/finalizeUnwrapERC7984.js +127 -0
- package/dist/test/unit/transactionBuilder/flushERC7984.d.ts +3 -0
- package/dist/test/unit/transactionBuilder/flushERC7984.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/flushERC7984.js +292 -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 +10 -0
- package/dist/test/unit/transactionBuilder/index.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/index.js +26 -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/unwrapERC7984.d.ts +3 -0
- package/dist/test/unit/transactionBuilder/unwrapERC7984.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/unwrapERC7984.js +134 -0
- package/dist/test/unit/transactionBuilder/walletInitialization.d.ts +11 -0
- package/dist/test/unit/transactionBuilder/walletInitialization.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/walletInitialization.js +137 -0
- package/dist/test/unit/transactionBuilder/wrapERC7984.d.ts +3 -0
- package/dist/test/unit/transactionBuilder/wrapERC7984.d.ts.map +1 -0
- package/dist/test/unit/transactionBuilder/wrapERC7984.js +140 -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 +214 -0
- package/dist/test/unit/zamaUtils.d.ts +2 -0
- package/dist/test/unit/zamaUtils.d.ts.map +1 -0
- package/dist/test/unit/zamaUtils.js +616 -0
- package/dist/tsconfig.tsbuildinfo +1 -8442
- package/package.json +34 -10
- package/.eslintignore +0 -5
- package/CHANGELOG.md +0 -178
- package/LICENSE +0 -191
|
@@ -0,0 +1,3072 @@
|
|
|
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
|
+
exports.isVerifyContractBaseAddressOptions = isVerifyContractBaseAddressOptions;
|
|
8
|
+
/**
|
|
9
|
+
* @prettier
|
|
10
|
+
*/
|
|
11
|
+
const sdk_core_1 = require("@bitgo-beta/sdk-core");
|
|
12
|
+
const sdk_lib_mpc_1 = require("@bitgo-beta/sdk-lib-mpc");
|
|
13
|
+
const secp256k1_1 = require("@bitgo-beta/secp256k1");
|
|
14
|
+
const statics_1 = require("@bitgo-beta/statics");
|
|
15
|
+
const tx_1 = require("@ethereumjs/tx");
|
|
16
|
+
const rlp_1 = require("@ethereumjs/rlp");
|
|
17
|
+
const eth_sig_util_1 = require("@metamask/eth-sig-util");
|
|
18
|
+
const bignumber_js_1 = require("bignumber.js");
|
|
19
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
20
|
+
const crypto_1 = require("crypto");
|
|
21
|
+
const debug_1 = __importDefault(require("debug"));
|
|
22
|
+
const ethereumjs_util_1 = require("ethereumjs-util");
|
|
23
|
+
const keccak_1 = __importDefault(require("keccak"));
|
|
24
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
25
|
+
const secp256k1_2 = __importDefault(require("secp256k1"));
|
|
26
|
+
const abstractEthLikeCoin_1 = require("./abstractEthLikeCoin");
|
|
27
|
+
const ethLikeToken_1 = require("./ethLikeToken");
|
|
28
|
+
const lib_1 = require("./lib");
|
|
29
|
+
exports.DEFAULT_SCAN_FACTOR = 20;
|
|
30
|
+
/**
|
|
31
|
+
* Type guard to check if params are for BIP32 base address verification (V1, V2, V4)
|
|
32
|
+
* These wallet versions use ethAddress for address derivation
|
|
33
|
+
*/
|
|
34
|
+
function isVerifyContractBaseAddressOptions(params) {
|
|
35
|
+
return ((params.walletVersion === 1 || params.walletVersion === 2 || params.walletVersion === 4) &&
|
|
36
|
+
'keychains' in params &&
|
|
37
|
+
Array.isArray(params.keychains) &&
|
|
38
|
+
params.keychains.length === 3 &&
|
|
39
|
+
params.keychains.every((kc) => 'ethAddress' in kc && typeof kc.ethAddress === 'string'));
|
|
40
|
+
}
|
|
41
|
+
const debug = (0, debug_1.default)('bitgo:v2:ethlike');
|
|
42
|
+
let erc7984WrappersByUnderlying;
|
|
43
|
+
/**
|
|
44
|
+
* Lowercased wrapper (spender) addresses of every registered ERC-7984 shield pair, keyed by the lowercased
|
|
45
|
+
* address of the pair's underlying ERC-20. Statics is static for the lifetime of the process, so this is
|
|
46
|
+
* computed once.
|
|
47
|
+
*/
|
|
48
|
+
function getErc7984WrappersByUnderlying() {
|
|
49
|
+
if (!erc7984WrappersByUnderlying) {
|
|
50
|
+
const byUnderlying = new Map();
|
|
51
|
+
statics_1.coins.forEach((coin) => {
|
|
52
|
+
if (coin instanceof statics_1.Erc7984Coin && coin.underlyingErc20Address && coin.contractAddress) {
|
|
53
|
+
const underlying = coin.underlyingErc20Address.toLowerCase();
|
|
54
|
+
const wrappers = byUnderlying.get(underlying) ?? new Set();
|
|
55
|
+
wrappers.add(coin.contractAddress.toLowerCase());
|
|
56
|
+
byUnderlying.set(underlying, wrappers);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
erc7984WrappersByUnderlying = byUnderlying;
|
|
60
|
+
}
|
|
61
|
+
return erc7984WrappersByUnderlying;
|
|
62
|
+
}
|
|
63
|
+
exports.optionalDeps = {
|
|
64
|
+
get ethAbi() {
|
|
65
|
+
try {
|
|
66
|
+
return require('ethereumjs-abi');
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
debug('unable to load ethereumjs-abi:');
|
|
70
|
+
debug(e.stack);
|
|
71
|
+
throw new sdk_core_1.EthereumLibraryUnavailableError(`ethereumjs-abi`);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
get ethUtil() {
|
|
75
|
+
try {
|
|
76
|
+
return require('ethereumjs-util');
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
debug('unable to load ethereumjs-util:');
|
|
80
|
+
debug(e.stack);
|
|
81
|
+
throw new sdk_core_1.EthereumLibraryUnavailableError(`ethereumjs-util`);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
get EthTx() {
|
|
85
|
+
try {
|
|
86
|
+
return require('@ethereumjs/tx');
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
debug('unable to load @ethereumjs/tx');
|
|
90
|
+
debug(e.stack);
|
|
91
|
+
throw new sdk_core_1.EthereumLibraryUnavailableError(`@ethereumjs/tx`);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
get EthCommon() {
|
|
95
|
+
try {
|
|
96
|
+
return require('@ethereumjs/common');
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
debug('unable to load @ethereumjs/common:');
|
|
100
|
+
debug(e.stack);
|
|
101
|
+
throw new sdk_core_1.EthereumLibraryUnavailableError(`@ethereumjs/common`);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
class AbstractEthLikeNewCoins extends abstractEthLikeCoin_1.AbstractEthLikeCoin {
|
|
106
|
+
constructor(bitgo, staticsCoin) {
|
|
107
|
+
super(bitgo, staticsCoin);
|
|
108
|
+
/**
|
|
109
|
+
* Get the data required to make an ETH function call defined by the given types and values
|
|
110
|
+
*
|
|
111
|
+
* @param {string} functionName - The name of the function being called, e.g. transfer
|
|
112
|
+
* @param types The types of the function call in order
|
|
113
|
+
* @param values The values of the function call in order
|
|
114
|
+
* @return {Buffer} The combined data for the function call
|
|
115
|
+
*/
|
|
116
|
+
this.getMethodCallData = (functionName, types, values) => {
|
|
117
|
+
return Buffer.concat([
|
|
118
|
+
// function signature
|
|
119
|
+
exports.optionalDeps.ethAbi.methodID(functionName, types),
|
|
120
|
+
// function arguments
|
|
121
|
+
exports.optionalDeps.ethAbi.rawEncode(types, values),
|
|
122
|
+
]);
|
|
123
|
+
};
|
|
124
|
+
if (!staticsCoin) {
|
|
125
|
+
throw new Error('missing required constructor parameter staticsCoin');
|
|
126
|
+
}
|
|
127
|
+
this.staticsCoin = staticsCoin;
|
|
128
|
+
this.sendMethodName = 'sendMultiSig';
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Method to return the coin's network object
|
|
132
|
+
* @returns {EthLikeNetwork | undefined}
|
|
133
|
+
*/
|
|
134
|
+
getNetwork() {
|
|
135
|
+
return this.staticsCoin?.network;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Evaluates whether an address string is valid for this coin
|
|
139
|
+
* @param {string} address
|
|
140
|
+
* @returns {boolean} True if address is the valid ethlike adderss
|
|
141
|
+
*/
|
|
142
|
+
isValidAddress(address) {
|
|
143
|
+
return exports.optionalDeps.ethUtil.isValidAddress(exports.optionalDeps.ethUtil.addHexPrefix(address));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Flag for sending data along with transactions
|
|
147
|
+
* @returns {boolean} True if okay to send tx data (ETH), false otherwise
|
|
148
|
+
*/
|
|
149
|
+
transactionDataAllowed() {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
/** @inheritDoc */
|
|
153
|
+
supportsMessageSigning() {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
/** @inheritDoc */
|
|
157
|
+
supportsSigningTypedData() {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Default expire time for a contract call (1 week)
|
|
162
|
+
* @returns {number} Time in seconds
|
|
163
|
+
*/
|
|
164
|
+
getDefaultExpireTime() {
|
|
165
|
+
return Math.floor(new Date().getTime() / 1000) + 60 * 60 * 24 * 7;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Method to get the custom chain common object based on params from recovery
|
|
169
|
+
* @param {number} chainId - the chain id of the custom chain
|
|
170
|
+
* @returns {EthLikeCommon.default}
|
|
171
|
+
*/
|
|
172
|
+
static getCustomChainCommon(chainId) {
|
|
173
|
+
const coinName = statics_1.coins.coinNameFromChainId(chainId);
|
|
174
|
+
if (!coinName) {
|
|
175
|
+
throw new statics_1.ChainIdNotFoundError(chainId);
|
|
176
|
+
}
|
|
177
|
+
const coin = statics_1.coins.get(coinName);
|
|
178
|
+
const ethLikeCommon = (0, lib_1.getCommon)(coin.network);
|
|
179
|
+
return ethLikeCommon;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Gets correct Eth Common object based on params from either recovery or tx building
|
|
183
|
+
* @param {EIP1559} eip1559 - configs that specify whether we should construct an eip1559 tx
|
|
184
|
+
* @param {ReplayProtectionOptions} replayProtectionOptions - check if chain id supports replay protection
|
|
185
|
+
* @returns {EthLikeCommon.default}
|
|
186
|
+
*/
|
|
187
|
+
static getEthLikeCommon(eip1559, replayProtectionOptions) {
|
|
188
|
+
// if eip1559 params are specified, default to london hardfork, otherwise,
|
|
189
|
+
// default to petersburg to avoid replay protection issues
|
|
190
|
+
const defaultHardfork = !!eip1559 ? 'london' : exports.optionalDeps.EthCommon.Hardfork.Petersburg;
|
|
191
|
+
const ethLikeCommon = AbstractEthLikeNewCoins.getCustomChainCommon(replayProtectionOptions?.chain);
|
|
192
|
+
ethLikeCommon.setHardfork(replayProtectionOptions?.hardfork ?? defaultHardfork);
|
|
193
|
+
return ethLikeCommon;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Method to build the tx object
|
|
197
|
+
* @param {BuildTransactionParams} params - params to build transaction
|
|
198
|
+
* @returns {EthLikeTxLib.FeeMarketEIP1559Transaction | EthLikeTxLib.Transaction}
|
|
199
|
+
*/
|
|
200
|
+
static buildTransaction(params) {
|
|
201
|
+
// if eip1559 params are specified, default to london hardfork, otherwise,
|
|
202
|
+
// default to tangerine whistle to avoid replay protection issues
|
|
203
|
+
const ethLikeCommon = AbstractEthLikeNewCoins.getEthLikeCommon(params.eip1559, params.replayProtectionOptions);
|
|
204
|
+
const baseParams = {
|
|
205
|
+
to: params.to,
|
|
206
|
+
nonce: params.nonce,
|
|
207
|
+
value: params.value,
|
|
208
|
+
data: params.data,
|
|
209
|
+
gasLimit: new exports.optionalDeps.ethUtil.BN(params.gasLimit),
|
|
210
|
+
};
|
|
211
|
+
const unsignedEthTx = !!params.eip1559
|
|
212
|
+
? exports.optionalDeps.EthTx.FeeMarketEIP1559Transaction.fromTxData({
|
|
213
|
+
...baseParams,
|
|
214
|
+
maxFeePerGas: new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas),
|
|
215
|
+
maxPriorityFeePerGas: new exports.optionalDeps.ethUtil.BN(params.eip1559.maxPriorityFeePerGas),
|
|
216
|
+
}, { common: ethLikeCommon })
|
|
217
|
+
: exports.optionalDeps.EthTx.Transaction.fromTxData({
|
|
218
|
+
...baseParams,
|
|
219
|
+
gasPrice: new exports.optionalDeps.ethUtil.BN(params.gasPrice),
|
|
220
|
+
}, { common: ethLikeCommon });
|
|
221
|
+
return unsignedEthTx;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Query explorer for the balance of an address
|
|
225
|
+
* @param {String} address - the ETHLike address
|
|
226
|
+
* @param {String} apiKey - optional API key to use instead of the one from the environment
|
|
227
|
+
* @returns {BigNumber} address balance
|
|
228
|
+
*/
|
|
229
|
+
async queryAddressBalance(address, apiKey) {
|
|
230
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
231
|
+
chainid: this.getChainId().toString(),
|
|
232
|
+
module: 'account',
|
|
233
|
+
action: 'balance',
|
|
234
|
+
address: address,
|
|
235
|
+
}, apiKey);
|
|
236
|
+
// throw if the result does not exist or the result is not a valid number
|
|
237
|
+
if (!result || !result.result || isNaN(result.result)) {
|
|
238
|
+
throw new sdk_core_1.RecoveryProviderError(`Could not obtain address balance for ${address} from the explorer, got: ${result.result}`);
|
|
239
|
+
}
|
|
240
|
+
return new exports.optionalDeps.ethUtil.BN(result.result, 10);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* @param {Recipient[]} recipients - the recipients of the transaction
|
|
244
|
+
* @param {number} expireTime - the expire time of the transaction
|
|
245
|
+
* @param {number} contractSequenceId - the contract sequence id of the transaction
|
|
246
|
+
* @returns {string}
|
|
247
|
+
*/
|
|
248
|
+
getOperationSha3ForExecuteAndConfirm(recipients, expireTime, contractSequenceId) {
|
|
249
|
+
if (!recipients || !Array.isArray(recipients)) {
|
|
250
|
+
throw new Error('expecting array of recipients');
|
|
251
|
+
}
|
|
252
|
+
// Right now we only support 1 recipient
|
|
253
|
+
if (recipients.length !== 1) {
|
|
254
|
+
throw new Error('must send to exactly 1 recipient');
|
|
255
|
+
}
|
|
256
|
+
if (!lodash_1.default.isNumber(expireTime)) {
|
|
257
|
+
throw new Error('expireTime must be number of seconds since epoch');
|
|
258
|
+
}
|
|
259
|
+
if (!lodash_1.default.isNumber(contractSequenceId)) {
|
|
260
|
+
throw new Error('contractSequenceId must be number');
|
|
261
|
+
}
|
|
262
|
+
// Check inputs
|
|
263
|
+
recipients.forEach(function (recipient) {
|
|
264
|
+
if (!lodash_1.default.isString(recipient.address) ||
|
|
265
|
+
!exports.optionalDeps.ethUtil.isValidAddress(exports.optionalDeps.ethUtil.addHexPrefix(recipient.address))) {
|
|
266
|
+
throw new Error('Invalid address: ' + recipient.address);
|
|
267
|
+
}
|
|
268
|
+
let amount;
|
|
269
|
+
try {
|
|
270
|
+
amount = new bignumber_js_1.BigNumber(recipient.amount);
|
|
271
|
+
}
|
|
272
|
+
catch (e) {
|
|
273
|
+
throw new Error('Invalid amount for: ' + recipient.address + ' - should be numeric');
|
|
274
|
+
}
|
|
275
|
+
recipient.amount = amount.toFixed(0);
|
|
276
|
+
if (recipient.data && !lodash_1.default.isString(recipient.data)) {
|
|
277
|
+
throw new Error('Data for recipient ' + recipient.address + ' - should be of type hex string');
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
const recipient = recipients[0];
|
|
281
|
+
return exports.optionalDeps.ethUtil.bufferToHex(exports.optionalDeps.ethAbi.soliditySHA3(...this.getOperation(recipient, expireTime, contractSequenceId)));
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get transfer operation for coin
|
|
285
|
+
* @param {Recipient} recipient - recipient info
|
|
286
|
+
* @param {number} expireTime - expiry time
|
|
287
|
+
* @param {number} contractSequenceId - sequence id
|
|
288
|
+
* @returns {Array} operation array
|
|
289
|
+
*/
|
|
290
|
+
getOperation(recipient, expireTime, contractSequenceId) {
|
|
291
|
+
const network = this.getNetwork();
|
|
292
|
+
return [
|
|
293
|
+
['string', 'address', 'uint', 'bytes', 'uint', 'uint'],
|
|
294
|
+
[
|
|
295
|
+
network.nativeCoinOperationHashPrefix,
|
|
296
|
+
new exports.optionalDeps.ethUtil.BN(exports.optionalDeps.ethUtil.stripHexPrefix(recipient.address), 16),
|
|
297
|
+
recipient.amount,
|
|
298
|
+
Buffer.from(exports.optionalDeps.ethUtil.stripHexPrefix(exports.optionalDeps.ethUtil.padToEven(recipient.data || '')), 'hex'),
|
|
299
|
+
expireTime,
|
|
300
|
+
contractSequenceId,
|
|
301
|
+
],
|
|
302
|
+
];
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Queries the contract (via explorer API) for the next sequence ID
|
|
306
|
+
* @param {String} address - address of the contract
|
|
307
|
+
* @param {String} apiKey - optional API key to use instead of the one from the environment
|
|
308
|
+
* @returns {Promise<Number>} sequence ID
|
|
309
|
+
*/
|
|
310
|
+
async querySequenceId(address, apiKey) {
|
|
311
|
+
// Get sequence ID using contract call
|
|
312
|
+
const sequenceIdMethodSignature = exports.optionalDeps.ethAbi.methodID('getNextSequenceId', []);
|
|
313
|
+
const sequenceIdArgs = exports.optionalDeps.ethAbi.rawEncode([], []);
|
|
314
|
+
const sequenceIdData = Buffer.concat([sequenceIdMethodSignature, sequenceIdArgs]).toString('hex');
|
|
315
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
316
|
+
chainid: this.getChainId().toString(),
|
|
317
|
+
module: 'proxy',
|
|
318
|
+
action: 'eth_call',
|
|
319
|
+
to: address,
|
|
320
|
+
data: sequenceIdData,
|
|
321
|
+
tag: 'latest',
|
|
322
|
+
}, apiKey);
|
|
323
|
+
if (!result || !result.result) {
|
|
324
|
+
throw new sdk_core_1.RecoveryProviderError('Could not obtain sequence ID from explorer, got: ' + result.result);
|
|
325
|
+
}
|
|
326
|
+
const sequenceIdHex = result.result;
|
|
327
|
+
return new exports.optionalDeps.ethUtil.BN(sequenceIdHex.slice(2), 16).toNumber();
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Queries the wallet contract (via explorer API) whether an address is a signer on it.
|
|
331
|
+
* Probes the explicit `isSigner(address)` function because it exists on every deployed
|
|
332
|
+
* WalletSimple generation: the newer generation declares it beside its
|
|
333
|
+
* `mapping(address => bool) public signers`, while eth-multisig-v2 (ETH wallets) only
|
|
334
|
+
* has `address[] public signers` whose getter takes an index. The contracts'
|
|
335
|
+
* non-reverting fallback would return empty data for a missing selector, so probing a
|
|
336
|
+
* generation-specific getter must never happen.
|
|
337
|
+
* @param walletContractAddress - address of the wallet contract
|
|
338
|
+
* @param signerAddress - address to test for membership
|
|
339
|
+
* @param apiKey - optional API key to use instead of the one from the environment
|
|
340
|
+
* @returns true if the address is a signer on the wallet contract
|
|
341
|
+
*/
|
|
342
|
+
async queryIsWalletSigner(walletContractAddress, signerAddress, apiKey) {
|
|
343
|
+
const signerData = this.getMethodCallData('isSigner', ['address'], [signerAddress]).toString('hex');
|
|
344
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
345
|
+
chainid: this.getChainId().toString(),
|
|
346
|
+
module: 'proxy',
|
|
347
|
+
action: 'eth_call',
|
|
348
|
+
to: walletContractAddress,
|
|
349
|
+
data: signerData,
|
|
350
|
+
tag: 'latest',
|
|
351
|
+
}, apiKey);
|
|
352
|
+
if (!result || !result.result || exports.optionalDeps.ethUtil.stripHexPrefix(result.result).length === 0) {
|
|
353
|
+
throw new Error(`Could not read the signer set of wallet contract ${walletContractAddress} from the explorer, got: ${result?.result}`);
|
|
354
|
+
}
|
|
355
|
+
return exports.optionalDeps.ethAbi.rawDecode(['bool'], exports.optionalDeps.ethUtil.toBuffer(result.result))[0] === true;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Asserts the user and backup recovery keys are signers on the wallet contract.
|
|
359
|
+
* Runs before any balance/fee check so wrong pairings fail closed with RecoveryKeyMismatchError.
|
|
360
|
+
*/
|
|
361
|
+
async assertRecoveryKeysAreWalletSigners(userKey, backupKeyAddress, walletContractAddress, apiKey) {
|
|
362
|
+
const userKeyAddress = `0x${exports.optionalDeps.ethUtil
|
|
363
|
+
.publicToAddress(secp256k1_1.bip32.fromBase58(userKey).publicKey, true)
|
|
364
|
+
.toString('hex')}`;
|
|
365
|
+
const isUserSigner = await this.queryIsWalletSigner(walletContractAddress, userKeyAddress, apiKey);
|
|
366
|
+
// we need to wait between making two explorer api calls to avoid getting banned
|
|
367
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
368
|
+
const isBackupSigner = await this.queryIsWalletSigner(walletContractAddress, backupKeyAddress, apiKey);
|
|
369
|
+
const missingSigners = [
|
|
370
|
+
...(isUserSigner ? [] : [`user key ${userKeyAddress}`]),
|
|
371
|
+
...(isBackupSigner ? [] : [`backup key ${backupKeyAddress}`]),
|
|
372
|
+
];
|
|
373
|
+
if (missingSigners.length > 0) {
|
|
374
|
+
throw new sdk_core_1.RecoveryKeyMismatchError(`${missingSigners.join(' and ')} ${missingSigners.length === 1 ? 'is not a signer' : 'are not signers'} of wallet contract ${walletContractAddress}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Recover an unsupported token from a BitGo multisig wallet
|
|
379
|
+
* This builds a half-signed transaction, for which there will be an admin route to co-sign and broadcast. Optionally
|
|
380
|
+
* the user can set params.broadcast = true and the half-signed tx will be sent to BitGo for cosigning and broadcasting
|
|
381
|
+
* @param {RecoverTokenOptions} params
|
|
382
|
+
* @param {Wallet} params.wallet - the wallet to recover the token from
|
|
383
|
+
* @param {string} params.tokenContractAddress - the contract address of the unsupported token
|
|
384
|
+
* @param {string} params.recipient - the destination address recovered tokens should be sent to
|
|
385
|
+
* @param {string} params.walletPassphrase - the wallet passphrase
|
|
386
|
+
* @param {string} params.prv - the xprv
|
|
387
|
+
* @param {boolean} params.broadcast - if true, we will automatically submit the half-signed tx to BitGo for cosigning and broadcasting
|
|
388
|
+
* @returns {Promise<RecoverTokenTransaction>}
|
|
389
|
+
*/
|
|
390
|
+
async recoverToken(params) {
|
|
391
|
+
const network = this.getNetwork();
|
|
392
|
+
if (!lodash_1.default.isObject(params)) {
|
|
393
|
+
throw new Error(`recoverToken must be passed a params object. Got ${params} (type ${typeof params})`);
|
|
394
|
+
}
|
|
395
|
+
if (lodash_1.default.isUndefined(params.tokenContractAddress) || !lodash_1.default.isString(params.tokenContractAddress)) {
|
|
396
|
+
throw new Error(`tokenContractAddress must be a string, got ${params.tokenContractAddress} (type ${typeof params.tokenContractAddress})`);
|
|
397
|
+
}
|
|
398
|
+
if (!this.isValidAddress(params.tokenContractAddress)) {
|
|
399
|
+
throw new Error('tokenContractAddress not a valid address');
|
|
400
|
+
}
|
|
401
|
+
if (lodash_1.default.isUndefined(params.wallet) || !(params.wallet instanceof sdk_core_1.Wallet)) {
|
|
402
|
+
throw new Error(`wallet must be a wallet instance, got ${params.wallet} (type ${typeof params.wallet})`);
|
|
403
|
+
}
|
|
404
|
+
if (lodash_1.default.isUndefined(params.recipient) || !lodash_1.default.isString(params.recipient)) {
|
|
405
|
+
throw new Error(`recipient must be a string, got ${params.recipient} (type ${typeof params.recipient})`);
|
|
406
|
+
}
|
|
407
|
+
if (!this.isValidAddress(params.recipient)) {
|
|
408
|
+
throw new Error('recipient not a valid address');
|
|
409
|
+
}
|
|
410
|
+
if (!exports.optionalDeps.ethUtil.bufferToHex || !exports.optionalDeps.ethAbi.soliditySHA3) {
|
|
411
|
+
throw new Error('ethereum not fully supported in this environment');
|
|
412
|
+
}
|
|
413
|
+
// Get token balance from external API
|
|
414
|
+
const coinSpecific = params.wallet.coinSpecific();
|
|
415
|
+
if (!coinSpecific || !lodash_1.default.isString(coinSpecific.baseAddress)) {
|
|
416
|
+
throw new Error('missing required coin specific property baseAddress');
|
|
417
|
+
}
|
|
418
|
+
const recoveryAmount = await this.queryAddressTokenBalance(params.tokenContractAddress, coinSpecific.baseAddress);
|
|
419
|
+
if (params.broadcast) {
|
|
420
|
+
// We're going to create a normal ETH transaction that sends an amount of 0 ETH to the
|
|
421
|
+
// tokenContractAddress and encode the unsupported-token-send data in the data field
|
|
422
|
+
// #tricksy
|
|
423
|
+
const sendMethodArgs = [
|
|
424
|
+
{
|
|
425
|
+
name: '_to',
|
|
426
|
+
type: 'address',
|
|
427
|
+
value: params.recipient,
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
name: '_value',
|
|
431
|
+
type: 'uint256',
|
|
432
|
+
value: recoveryAmount.toString(10),
|
|
433
|
+
},
|
|
434
|
+
];
|
|
435
|
+
const methodSignature = exports.optionalDeps.ethAbi.methodID('transfer', lodash_1.default.map(sendMethodArgs, 'type'));
|
|
436
|
+
const encodedArgs = exports.optionalDeps.ethAbi.rawEncode(lodash_1.default.map(sendMethodArgs, 'type'), lodash_1.default.map(sendMethodArgs, 'value'));
|
|
437
|
+
const sendData = Buffer.concat([methodSignature, encodedArgs]);
|
|
438
|
+
const broadcastParams = {
|
|
439
|
+
address: params.tokenContractAddress,
|
|
440
|
+
amount: '0',
|
|
441
|
+
data: sendData.toString('hex'),
|
|
442
|
+
};
|
|
443
|
+
if (params.walletPassphrase) {
|
|
444
|
+
broadcastParams.walletPassphrase = params.walletPassphrase;
|
|
445
|
+
}
|
|
446
|
+
else if (params.prv) {
|
|
447
|
+
broadcastParams.prv = params.prv;
|
|
448
|
+
}
|
|
449
|
+
return await params.wallet.send(broadcastParams);
|
|
450
|
+
}
|
|
451
|
+
const recipient = {
|
|
452
|
+
address: params.recipient,
|
|
453
|
+
amount: recoveryAmount.toString(10),
|
|
454
|
+
};
|
|
455
|
+
// This signature will be valid for one week
|
|
456
|
+
const expireTime = Math.floor(new Date().getTime() / 1000) + 60 * 60 * 24 * 7;
|
|
457
|
+
// Get sequence ID. We do this by building a 'fake' eth transaction, so the platform will increment and return us the new sequence id
|
|
458
|
+
// This _does_ require the user to have a non-zero wallet balance
|
|
459
|
+
const { nextContractSequenceId, gasPrice, gasLimit } = (await params.wallet.prebuildTransaction({
|
|
460
|
+
recipients: [
|
|
461
|
+
{
|
|
462
|
+
address: params.recipient,
|
|
463
|
+
amount: '1',
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
}));
|
|
467
|
+
// these recoveries need to be processed by support, but if the customer sends any transactions before recovery is
|
|
468
|
+
// complete the sequence ID will be invalid. artificially inflate the sequence ID to allow more time for processing
|
|
469
|
+
const safeSequenceId = nextContractSequenceId + 1000;
|
|
470
|
+
// Build sendData for ethereum tx
|
|
471
|
+
const operationTypes = ['string', 'address', 'uint', 'address', 'uint', 'uint'];
|
|
472
|
+
const operationArgs = [
|
|
473
|
+
// Token operation has prefix has been added here so that ether operation hashes, signatures cannot be re-used for tokenSending
|
|
474
|
+
network.tokenOperationHashPrefix,
|
|
475
|
+
new exports.optionalDeps.ethUtil.BN(exports.optionalDeps.ethUtil.stripHexPrefix(recipient.address), 16),
|
|
476
|
+
recipient.amount,
|
|
477
|
+
new exports.optionalDeps.ethUtil.BN(exports.optionalDeps.ethUtil.stripHexPrefix(params.tokenContractAddress), 16),
|
|
478
|
+
expireTime,
|
|
479
|
+
safeSequenceId,
|
|
480
|
+
];
|
|
481
|
+
const operationHash = exports.optionalDeps.ethUtil.bufferToHex(exports.optionalDeps.ethAbi.soliditySHA3(operationTypes, operationArgs));
|
|
482
|
+
const userPrv = await params.wallet.getPrv({
|
|
483
|
+
prv: params.prv,
|
|
484
|
+
walletPassphrase: params.walletPassphrase,
|
|
485
|
+
});
|
|
486
|
+
const signature = sdk_core_1.Util.ethSignMsgHash(operationHash, sdk_core_1.Util.xprvToEthPrivateKey(userPrv));
|
|
487
|
+
return {
|
|
488
|
+
halfSigned: {
|
|
489
|
+
recipient: recipient,
|
|
490
|
+
expireTime: expireTime,
|
|
491
|
+
contractSequenceId: safeSequenceId,
|
|
492
|
+
operationHash: operationHash,
|
|
493
|
+
signature: signature,
|
|
494
|
+
gasLimit: gasLimit,
|
|
495
|
+
gasPrice: gasPrice,
|
|
496
|
+
tokenContractAddress: params.tokenContractAddress,
|
|
497
|
+
walletId: params.wallet.id(),
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Ensure either enterprise or newFeeAddress is passed, to know whether to create new key or use enterprise key
|
|
503
|
+
* @param {PrecreateBitGoOptions} params
|
|
504
|
+
* @param {string} params.enterprise {String} the enterprise id to associate with this key
|
|
505
|
+
* @param {string} params.newFeeAddress {Boolean} create a new fee address (enterprise not needed in this case)
|
|
506
|
+
* @returns {void}
|
|
507
|
+
*/
|
|
508
|
+
preCreateBitGo(params) {
|
|
509
|
+
// We always need params object, since either enterprise or newFeeAddress is required
|
|
510
|
+
if (!lodash_1.default.isObject(params)) {
|
|
511
|
+
throw new Error(`preCreateBitGo must be passed a params object. Got ${params} (type ${typeof params})`);
|
|
512
|
+
}
|
|
513
|
+
if (lodash_1.default.isUndefined(params.enterprise) && lodash_1.default.isUndefined(params.newFeeAddress)) {
|
|
514
|
+
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.');
|
|
515
|
+
}
|
|
516
|
+
// Check whether key should be an enterprise key or a BitGo key for a new fee address
|
|
517
|
+
if (!lodash_1.default.isUndefined(params.enterprise) && !lodash_1.default.isUndefined(params.newFeeAddress)) {
|
|
518
|
+
throw new Error(`Incompatible arguments - cannot pass both enterprise and newFeeAddress parameter.`);
|
|
519
|
+
}
|
|
520
|
+
if (!lodash_1.default.isUndefined(params.enterprise) && !lodash_1.default.isString(params.enterprise)) {
|
|
521
|
+
throw new Error(`enterprise should be a string - got ${params.enterprise} (type ${typeof params.enterprise})`);
|
|
522
|
+
}
|
|
523
|
+
if (!lodash_1.default.isUndefined(params.newFeeAddress) && !lodash_1.default.isBoolean(params.newFeeAddress)) {
|
|
524
|
+
throw new Error(`newFeeAddress should be a boolean - got ${params.newFeeAddress} (type ${typeof params.newFeeAddress})`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Queries public block explorer to get the next ETHLike coin's nonce that should be used for the given ETH address
|
|
529
|
+
* @param {string} address
|
|
530
|
+
* @param {string} apiKey - optional API key to use instead of the one from the environment
|
|
531
|
+
* @returns {Promise<number>}
|
|
532
|
+
*/
|
|
533
|
+
async getAddressNonce(address, apiKey) {
|
|
534
|
+
// Get nonce for backup key (should be 0)
|
|
535
|
+
let nonce = 0;
|
|
536
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
537
|
+
chainid: this.getChainId().toString(),
|
|
538
|
+
module: 'account',
|
|
539
|
+
action: 'txlist',
|
|
540
|
+
address,
|
|
541
|
+
}, apiKey);
|
|
542
|
+
if (result && typeof result?.nonce === 'number') {
|
|
543
|
+
return Number(result.nonce);
|
|
544
|
+
}
|
|
545
|
+
if (!result || !Array.isArray(result.result)) {
|
|
546
|
+
throw new sdk_core_1.RecoveryProviderError('Unable to find next nonce from Etherscan, got: ' + JSON.stringify(result));
|
|
547
|
+
}
|
|
548
|
+
const backupKeyTxList = result.result;
|
|
549
|
+
if (backupKeyTxList.length > 0) {
|
|
550
|
+
// Calculate last nonce used
|
|
551
|
+
const outgoingTxs = backupKeyTxList.filter((tx) => tx.from === address);
|
|
552
|
+
nonce = outgoingTxs.length;
|
|
553
|
+
}
|
|
554
|
+
return nonce;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Helper function for recover()
|
|
558
|
+
* This transforms the unsigned transaction information into a format the BitGo offline vault expects
|
|
559
|
+
* @param {UnformattedTxInfo} txInfo - tx info
|
|
560
|
+
* @param {EthLikeTxLib.Transaction | EthLikeTxLib.FeeMarketEIP1559Transaction} ethTx - the ethereumjs tx object
|
|
561
|
+
* @param {string} userKey - the user's key
|
|
562
|
+
* @param {string} backupKey - the backup key
|
|
563
|
+
* @param {Buffer} gasPrice - gas price for the tx
|
|
564
|
+
* @param {number} gasLimit - gas limit for the tx
|
|
565
|
+
* @param {EIP1559} eip1559 - eip1559 params
|
|
566
|
+
* @param {ReplayProtectionOptions} replayProtectionOptions - replay protection options
|
|
567
|
+
* @param {apiKey} apiKey - optional apiKey to use when retrieving block chain data
|
|
568
|
+
* @returns {Promise<OfflineVaultTxInfo>}
|
|
569
|
+
*/
|
|
570
|
+
async formatForOfflineVault(txInfo, ethTx, userKey, backupKey, gasPrice, gasLimit, eip1559, replayProtectionOptions, apiKey) {
|
|
571
|
+
if (!ethTx.to) {
|
|
572
|
+
throw new Error('Eth tx must have a `to` address');
|
|
573
|
+
}
|
|
574
|
+
const backupHDNode = secp256k1_1.bip32.fromBase58(backupKey);
|
|
575
|
+
const backupSigningKey = backupHDNode.publicKey;
|
|
576
|
+
const response = {
|
|
577
|
+
tx: ethTx.serialize().toString('hex'),
|
|
578
|
+
userKey,
|
|
579
|
+
backupKey,
|
|
580
|
+
coin: this.getChain(),
|
|
581
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
582
|
+
gasLimit,
|
|
583
|
+
recipients: [txInfo.recipient],
|
|
584
|
+
walletContractAddress: ethTx.to.toString(),
|
|
585
|
+
amount: txInfo.recipient.amount,
|
|
586
|
+
backupKeyNonce: await this.getAddressNonce(`0x${exports.optionalDeps.ethUtil.publicToAddress(backupSigningKey, true).toString('hex')}`, apiKey),
|
|
587
|
+
eip1559,
|
|
588
|
+
replayProtectionOptions,
|
|
589
|
+
};
|
|
590
|
+
lodash_1.default.extend(response, txInfo);
|
|
591
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
592
|
+
return response;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Helper function for recover()
|
|
596
|
+
* This transforms the unsigned transaction information into a format the BitGo offline vault expects
|
|
597
|
+
* @param {UnformattedTxInfo} txInfo - tx info
|
|
598
|
+
* @param {EthLikeTxLib.Transaction | EthLikeTxLib.FeeMarketEIP1559Transaction} ethTx - the ethereumjs tx object
|
|
599
|
+
* @param {string} userKey - the user's key
|
|
600
|
+
* @param {string} backupKey - the backup key
|
|
601
|
+
* @param {Buffer} gasPrice - gas price for the tx
|
|
602
|
+
* @param {number} gasLimit - gas limit for the tx
|
|
603
|
+
* @param {number} backupKeyNonce - the nonce of the backup key address
|
|
604
|
+
* @param {EIP1559} eip1559 - eip1559 params
|
|
605
|
+
* @param {ReplayProtectionOptions} replayProtectionOptions - replay protection options
|
|
606
|
+
* @returns {Promise<OfflineVaultTxInfo>}
|
|
607
|
+
*/
|
|
608
|
+
formatForOfflineVaultTSS(txInfo, ethTx, userKey, backupKey, gasPrice, gasLimit, backupKeyNonce, eip1559, replayProtectionOptions) {
|
|
609
|
+
if (!ethTx.to) {
|
|
610
|
+
throw new Error('Eth tx must have a `to` address');
|
|
611
|
+
}
|
|
612
|
+
const response = {
|
|
613
|
+
tx: ethTx.serialize().toString('hex'),
|
|
614
|
+
txHex: ethTx.getMessageToSign(false).toString(),
|
|
615
|
+
userKey,
|
|
616
|
+
backupKey,
|
|
617
|
+
coin: this.getChain(),
|
|
618
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
619
|
+
gasLimit,
|
|
620
|
+
recipients: [txInfo.recipient],
|
|
621
|
+
walletContractAddress: ethTx.to.toString(),
|
|
622
|
+
amount: txInfo.recipient.amount,
|
|
623
|
+
backupKeyNonce: backupKeyNonce,
|
|
624
|
+
eip1559,
|
|
625
|
+
replayProtectionOptions,
|
|
626
|
+
};
|
|
627
|
+
lodash_1.default.extend(response, txInfo);
|
|
628
|
+
return response;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Check whether the gas price passed in by user are within our max and min bounds
|
|
632
|
+
* If they are not set, set them to the defaults
|
|
633
|
+
* @param {number} userGasPrice - user defined gas price
|
|
634
|
+
* @returns {number} the gas price to use for this transaction
|
|
635
|
+
*/
|
|
636
|
+
setGasPrice(userGasPrice) {
|
|
637
|
+
if (!userGasPrice) {
|
|
638
|
+
return statics_1.ethGasConfigs.defaultGasPrice;
|
|
639
|
+
}
|
|
640
|
+
const gasPriceMax = statics_1.ethGasConfigs.maximumGasPrice;
|
|
641
|
+
const gasPriceMin = statics_1.ethGasConfigs.minimumGasPrice;
|
|
642
|
+
if (userGasPrice < gasPriceMin || userGasPrice > gasPriceMax) {
|
|
643
|
+
throw new Error(`Gas price must be between ${gasPriceMin} and ${gasPriceMax}`);
|
|
644
|
+
}
|
|
645
|
+
return userGasPrice;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Check whether gas limit passed in by user are within our max and min bounds
|
|
649
|
+
* If they are not set, set them to the defaults
|
|
650
|
+
* @param {number} userGasLimit user defined gas limit
|
|
651
|
+
* @returns {number} the gas limit to use for this transaction
|
|
652
|
+
*/
|
|
653
|
+
setGasLimit(userGasLimit) {
|
|
654
|
+
if (!userGasLimit) {
|
|
655
|
+
return statics_1.ethGasConfigs.defaultGasLimit;
|
|
656
|
+
}
|
|
657
|
+
const gasLimitMax = statics_1.ethGasConfigs.maximumGasLimit;
|
|
658
|
+
const gasLimitMin = statics_1.ethGasConfigs.minimumGasLimit;
|
|
659
|
+
if (userGasLimit < gasLimitMin || userGasLimit > gasLimitMax) {
|
|
660
|
+
throw new Error(`Gas limit must be between ${gasLimitMin} and ${gasLimitMax}`);
|
|
661
|
+
}
|
|
662
|
+
return userGasLimit;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Helper function for signTransaction for the rare case that SDK is doing the second signature
|
|
666
|
+
* Note: we are expecting this to be called from the offline vault
|
|
667
|
+
* @param {SignFinalOptions.txPrebuild} params.txPrebuild
|
|
668
|
+
* @param {string} params.prv
|
|
669
|
+
* @returns {{txHex: string}}
|
|
670
|
+
*/
|
|
671
|
+
async signFinalEthLike(params) {
|
|
672
|
+
const signingKey = new lib_1.KeyPair({ prv: params.prv }).getKeys().prv;
|
|
673
|
+
if (lodash_1.default.isUndefined(signingKey)) {
|
|
674
|
+
throw new Error('missing private key');
|
|
675
|
+
}
|
|
676
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
677
|
+
try {
|
|
678
|
+
txBuilder.from(params.txPrebuild.halfSigned?.txHex);
|
|
679
|
+
}
|
|
680
|
+
catch (e) {
|
|
681
|
+
throw new Error('invalid half-signed transaction');
|
|
682
|
+
}
|
|
683
|
+
txBuilder.sign({ key: signingKey });
|
|
684
|
+
const tx = await txBuilder.build();
|
|
685
|
+
return {
|
|
686
|
+
txHex: tx.toBroadcastFormat(),
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Assemble half-sign prebuilt transaction
|
|
691
|
+
* @param {SignTransactionOptions} params
|
|
692
|
+
*/
|
|
693
|
+
async signTransaction(params) {
|
|
694
|
+
// Normally the SDK provides the first signature for an EthLike tx, but occasionally it provides the second and final one.
|
|
695
|
+
if (params.isLastSignature) {
|
|
696
|
+
// In this case when we're doing the second (final) signature, the logic is different.
|
|
697
|
+
return await this.signFinalEthLike(params);
|
|
698
|
+
}
|
|
699
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
700
|
+
txBuilder.from(params.txPrebuild.txHex);
|
|
701
|
+
txBuilder
|
|
702
|
+
.transfer()
|
|
703
|
+
.coin(this.staticsCoin?.name)
|
|
704
|
+
.key(new lib_1.KeyPair({ prv: params.prv }).getKeys().prv);
|
|
705
|
+
if (params.walletVersion) {
|
|
706
|
+
txBuilder.walletVersion(params.walletVersion);
|
|
707
|
+
}
|
|
708
|
+
const transaction = await txBuilder.build();
|
|
709
|
+
// In case of tx with contract data from a custodial wallet, we are running into an issue
|
|
710
|
+
// as halfSigned is not having the data field. So, we are adding the data field to the halfSigned tx
|
|
711
|
+
let recipients = params.txPrebuild.recipients || params.recipients;
|
|
712
|
+
if (recipients === undefined) {
|
|
713
|
+
recipients = transaction.outputs.map((output) => ({ address: output.address, amount: output.value }));
|
|
714
|
+
}
|
|
715
|
+
const txParams = {
|
|
716
|
+
eip1559: params.txPrebuild.eip1559,
|
|
717
|
+
txHex: transaction.toBroadcastFormat(),
|
|
718
|
+
recipients: recipients,
|
|
719
|
+
expiration: params.txPrebuild.expireTime,
|
|
720
|
+
hopTransaction: params.txPrebuild.hopTransaction,
|
|
721
|
+
custodianTransactionId: params.custodianTransactionId,
|
|
722
|
+
expireTime: params.expireTime,
|
|
723
|
+
contractSequenceId: params.txPrebuild.nextContractSequenceId,
|
|
724
|
+
sequenceId: params.sequenceId,
|
|
725
|
+
...(params.txPrebuild.isBatch ? { isBatch: params.txPrebuild.isBatch } : {}),
|
|
726
|
+
};
|
|
727
|
+
return { halfSigned: txParams };
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Method to validate recovery params
|
|
731
|
+
* @param {RecoverOptions} params
|
|
732
|
+
* @returns {void}
|
|
733
|
+
*/
|
|
734
|
+
validateRecoveryParams(params) {
|
|
735
|
+
if (params.userKey === undefined) {
|
|
736
|
+
throw new Error('missing userKey');
|
|
737
|
+
}
|
|
738
|
+
if (params.backupKey === undefined) {
|
|
739
|
+
throw new Error('missing backupKey');
|
|
740
|
+
}
|
|
741
|
+
if (!params.isUnsignedSweep &&
|
|
742
|
+
params.walletPassphrase === undefined &&
|
|
743
|
+
!params.userKey.startsWith('xpub') &&
|
|
744
|
+
!params.isTss) {
|
|
745
|
+
throw new Error('missing wallet passphrase');
|
|
746
|
+
}
|
|
747
|
+
if (params.walletContractAddress === undefined || !this.isValidAddress(params.walletContractAddress)) {
|
|
748
|
+
throw new Error('invalid walletContractAddress');
|
|
749
|
+
}
|
|
750
|
+
if (params.recoveryDestination === undefined || !this.isValidAddress(params.recoveryDestination)) {
|
|
751
|
+
throw new Error('invalid recoveryDestination');
|
|
752
|
+
}
|
|
753
|
+
if (!this.staticsCoin?.features.includes(statics_1.CoinFeature.EIP1559)) {
|
|
754
|
+
if (params.eip1559) {
|
|
755
|
+
throw new Error('Invalid fee params. EIP1559 not supported');
|
|
756
|
+
}
|
|
757
|
+
if (params.replayProtectionOptions?.hardfork === 'london') {
|
|
758
|
+
throw new Error('Invalid replayProtection options. Cannot use the hardfork "london" for this chain');
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Helper which Adds signatures to tx object and re-serializes tx
|
|
764
|
+
* @param {EthLikeCommon.default} ethCommon
|
|
765
|
+
* @param {EthLikeTxLib.FeeMarketEIP1559Transaction | EthLikeTxLib.Transaction} tx
|
|
766
|
+
* @param {ECDSAMethodTypes.Signature} signature
|
|
767
|
+
* @returns {EthLikeTxLib.FeeMarketEIP1559Transaction | EthLikeTxLib.Transaction}
|
|
768
|
+
*/
|
|
769
|
+
getSignedTxFromSignature(ethCommon, tx, signature) {
|
|
770
|
+
// get signed Tx from signature
|
|
771
|
+
const txData = tx.toJSON();
|
|
772
|
+
const yParity = signature.recid;
|
|
773
|
+
const baseParams = {
|
|
774
|
+
to: txData.to,
|
|
775
|
+
nonce: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.nonce), 'hex'),
|
|
776
|
+
value: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.value), 'hex'),
|
|
777
|
+
gasLimit: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.gasLimit), 'hex'),
|
|
778
|
+
data: txData.data,
|
|
779
|
+
r: (0, ethereumjs_util_1.addHexPrefix)(signature.r),
|
|
780
|
+
s: (0, ethereumjs_util_1.addHexPrefix)(signature.s),
|
|
781
|
+
};
|
|
782
|
+
let finalTx;
|
|
783
|
+
if (txData.maxFeePerGas && txData.maxPriorityFeePerGas) {
|
|
784
|
+
finalTx = tx_1.FeeMarketEIP1559Transaction.fromTxData({
|
|
785
|
+
...baseParams,
|
|
786
|
+
maxPriorityFeePerGas: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.maxPriorityFeePerGas), 'hex'),
|
|
787
|
+
maxFeePerGas: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.maxFeePerGas), 'hex'),
|
|
788
|
+
v: new bn_js_1.default(yParity.toString()),
|
|
789
|
+
}, { common: ethCommon });
|
|
790
|
+
}
|
|
791
|
+
else if (txData.gasPrice) {
|
|
792
|
+
const v = BigInt(35) + BigInt(yParity) + BigInt(ethCommon.chainIdBN().toNumber()) * BigInt(2);
|
|
793
|
+
finalTx = tx_1.Transaction.fromTxData({
|
|
794
|
+
...baseParams,
|
|
795
|
+
v: new bn_js_1.default(v.toString()),
|
|
796
|
+
gasPrice: new bn_js_1.default((0, ethereumjs_util_1.stripHexPrefix)(txData.gasPrice.toString()), 'hex'),
|
|
797
|
+
}, { common: ethCommon });
|
|
798
|
+
}
|
|
799
|
+
return finalTx;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Builds a funds recovery transaction without BitGo
|
|
803
|
+
* @param params
|
|
804
|
+
* @param {string} params.userKey - [encrypted] xprv
|
|
805
|
+
* @param {string} params.backupKey - [encrypted] xprv or xpub if the xprv is held by a KRS provider
|
|
806
|
+
* @param {string} params.walletPassphrase - used to decrypt userKey and backupKey
|
|
807
|
+
* @param {string} params.walletContractAddress - the ETH address of the wallet contract
|
|
808
|
+
* @param {string} params.krsProvider - necessary if backup key is held by KRS
|
|
809
|
+
* @param {string} params.recoveryDestination - target address to send recovered funds to
|
|
810
|
+
* @param {string} params.bitgoFeeAddress - wrong chain wallet fee address for evm based cross chain recovery txn
|
|
811
|
+
* @param {string} params.bitgoDestinationAddress - target bitgo address where fee will be sent for evm based cross chain recovery txn
|
|
812
|
+
*/
|
|
813
|
+
async recover(params) {
|
|
814
|
+
if (params.isTss === true) {
|
|
815
|
+
return this.recoverTSS(params);
|
|
816
|
+
}
|
|
817
|
+
return this.recoverEthLike(params);
|
|
818
|
+
}
|
|
819
|
+
generateForwarderAddress(baseAddress, feeAddress, forwarderFactoryAddress, forwarderImplementationAddress, index) {
|
|
820
|
+
const salt = (0, ethereumjs_util_1.addHexPrefix)(index.toString(16));
|
|
821
|
+
const saltBuffer = (0, ethereumjs_util_1.setLengthLeft)((0, ethereumjs_util_1.toBuffer)(salt), 32);
|
|
822
|
+
const { createForwarderParams, createForwarderTypes } = (0, lib_1.getCreateForwarderParamsAndTypes)(baseAddress, saltBuffer, feeAddress);
|
|
823
|
+
const calculationSalt = (0, ethereumjs_util_1.bufferToHex)(exports.optionalDeps.ethAbi.soliditySHA3(createForwarderTypes, createForwarderParams));
|
|
824
|
+
const initCode = (0, lib_1.getProxyInitcode)(forwarderImplementationAddress);
|
|
825
|
+
return (0, lib_1.calculateForwarderV1Address)(forwarderFactoryAddress, calculationSalt, initCode);
|
|
826
|
+
}
|
|
827
|
+
deriveAddressFromPublicKey(commonKeychain, index) {
|
|
828
|
+
const derivationPath = `m/${index}`;
|
|
829
|
+
const pubkeySize = 33;
|
|
830
|
+
const ecdsaMpc = new sdk_core_1.Ecdsa();
|
|
831
|
+
const derivedPublicKey = Buffer.from(ecdsaMpc.deriveUnhardened(commonKeychain, derivationPath), 'hex')
|
|
832
|
+
.subarray(0, pubkeySize)
|
|
833
|
+
.toString('hex');
|
|
834
|
+
const publicKey = Buffer.from(derivedPublicKey, 'hex').slice(0, 66).toString('hex');
|
|
835
|
+
const keyPair = new lib_1.KeyPair({ pub: publicKey });
|
|
836
|
+
const address = keyPair.getAddress();
|
|
837
|
+
return address;
|
|
838
|
+
}
|
|
839
|
+
getConsolidationAddress(params, index) {
|
|
840
|
+
const possibleConsolidationAddresses = [];
|
|
841
|
+
if (params.walletContractAddress && params.bitgoFeeAddress) {
|
|
842
|
+
const ethNetwork = this.getNetwork();
|
|
843
|
+
const forwarderFactoryAddress = ethNetwork?.walletV4ForwarderFactoryAddress;
|
|
844
|
+
const forwarderImplementationAddress = ethNetwork?.walletV4ForwarderImplementationAddress;
|
|
845
|
+
try {
|
|
846
|
+
const forwarderAddress = this.generateForwarderAddress(params.walletContractAddress, params.bitgoFeeAddress, forwarderFactoryAddress, forwarderImplementationAddress, index);
|
|
847
|
+
possibleConsolidationAddresses.push(forwarderAddress);
|
|
848
|
+
}
|
|
849
|
+
catch (e) {
|
|
850
|
+
console.log(`Failed to generate forwarder address: ${e.message}`);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
if (params.userKey) {
|
|
854
|
+
try {
|
|
855
|
+
const derivedAddress = this.deriveAddressFromPublicKey(params.userKey, index);
|
|
856
|
+
possibleConsolidationAddresses.push(derivedAddress);
|
|
857
|
+
}
|
|
858
|
+
catch (e) {
|
|
859
|
+
console.log(`Failed to generate derived address: ${e}`);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
if (possibleConsolidationAddresses.length === 0) {
|
|
863
|
+
throw new Error('Unable to generate consolidation address. Check that wallet contract address, fee address, or user key is valid.');
|
|
864
|
+
}
|
|
865
|
+
return possibleConsolidationAddresses;
|
|
866
|
+
}
|
|
867
|
+
async recoverConsolidations(params) {
|
|
868
|
+
const isUnsignedSweep = !params.userKey && !params.backupKey && !params.walletPassphrase;
|
|
869
|
+
const startIdx = params.startingScanIndex || 1;
|
|
870
|
+
const endIdx = params.endingScanIndex || startIdx + exports.DEFAULT_SCAN_FACTOR;
|
|
871
|
+
if (!params.walletContractAddress || params.walletContractAddress === '') {
|
|
872
|
+
throw new Error(`Invalid wallet contract address ${params.walletContractAddress}`);
|
|
873
|
+
}
|
|
874
|
+
if (!params.bitgoFeeAddress || params.bitgoFeeAddress === '') {
|
|
875
|
+
throw new Error(`Invalid fee address ${params.bitgoFeeAddress}`);
|
|
876
|
+
}
|
|
877
|
+
if (startIdx < 1 || endIdx <= startIdx || endIdx - startIdx > 10 * exports.DEFAULT_SCAN_FACTOR) {
|
|
878
|
+
throw new Error(`Invalid starting or ending index to scan for addresses. startingScanIndex: ${startIdx}, endingScanIndex: ${endIdx}.`);
|
|
879
|
+
}
|
|
880
|
+
const consolidatedTransactions = [];
|
|
881
|
+
let lastScanIndex = startIdx;
|
|
882
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
883
|
+
const consolidationAddress = this.getConsolidationAddress(params, i);
|
|
884
|
+
for (const address of consolidationAddress) {
|
|
885
|
+
const recoverParams = {
|
|
886
|
+
apiKey: params.apiKey,
|
|
887
|
+
backupKey: params.backupKey || '',
|
|
888
|
+
gasLimit: params.gasLimit,
|
|
889
|
+
recoveryDestination: params.recoveryDestination || '',
|
|
890
|
+
userKey: params.userKey || '',
|
|
891
|
+
walletContractAddress: address,
|
|
892
|
+
derivationSeed: '',
|
|
893
|
+
isTss: params.isTss,
|
|
894
|
+
eip1559: {
|
|
895
|
+
maxFeePerGas: params.eip1559?.maxFeePerGas || 20,
|
|
896
|
+
maxPriorityFeePerGas: params.eip1559?.maxPriorityFeePerGas || 200000,
|
|
897
|
+
},
|
|
898
|
+
replayProtectionOptions: {
|
|
899
|
+
chain: params.replayProtectionOptions?.chain || 0,
|
|
900
|
+
hardfork: params.replayProtectionOptions?.hardfork || 'london',
|
|
901
|
+
},
|
|
902
|
+
bitgoKey: '',
|
|
903
|
+
ignoreAddressTypes: [],
|
|
904
|
+
};
|
|
905
|
+
let recoveryTransaction;
|
|
906
|
+
try {
|
|
907
|
+
recoveryTransaction = await this.recover(recoverParams);
|
|
908
|
+
}
|
|
909
|
+
catch (e) {
|
|
910
|
+
if (e instanceof sdk_core_1.ErrorNoInputToRecover) {
|
|
911
|
+
lastScanIndex = i;
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
throw e;
|
|
915
|
+
}
|
|
916
|
+
if (isUnsignedSweep) {
|
|
917
|
+
consolidatedTransactions.push(recoveryTransaction.txRequests[0]);
|
|
918
|
+
}
|
|
919
|
+
else {
|
|
920
|
+
consolidatedTransactions.push(recoveryTransaction);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
// To avoid rate limit for etherscan
|
|
924
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
925
|
+
// lastScanIndex = i;
|
|
926
|
+
}
|
|
927
|
+
if (consolidatedTransactions.length === 0) {
|
|
928
|
+
throw new Error(`Did not find an address with sufficient funds to recover. Please start the next scan at address index ${lastScanIndex + 1}.`);
|
|
929
|
+
}
|
|
930
|
+
return { transactions: consolidatedTransactions, lastScanIndex };
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Builds a funds recovery transaction without BitGo for non-TSS transaction
|
|
934
|
+
* @param params
|
|
935
|
+
* @param {string} params.userKey [encrypted] xprv or xpub
|
|
936
|
+
* @param {string} params.backupKey [encrypted] xprv or xpub if the xprv is held by a KRS provider
|
|
937
|
+
* @param {string} params.walletPassphrase used to decrypt userKey and backupKey
|
|
938
|
+
* @param {string} params.walletContractAddress the EthLike address of the wallet contract
|
|
939
|
+
* @param {string} params.krsProvider necessary if backup key is held by KRS
|
|
940
|
+
* @param {string} params.recoveryDestination target address to send recovered funds to
|
|
941
|
+
* @param {string} params.bitgoFeeAddress wrong chain wallet fee address for evm based cross chain recovery txn
|
|
942
|
+
* @param {string} params.bitgoDestinationAddress target bitgo address where fee will be sent for evm based cross chain recovery txn
|
|
943
|
+
* @returns {Promise<RecoveryInfo | OfflineVaultTxInfo>}
|
|
944
|
+
*/
|
|
945
|
+
async recoverEthLike(params) {
|
|
946
|
+
// bitgoFeeAddress is only defined when it is a evm cross chain recovery
|
|
947
|
+
// as we use fee from this wrong chain address for the recovery txn on the correct chain.
|
|
948
|
+
if (params.bitgoFeeAddress) {
|
|
949
|
+
return this.recoverEthLikeforEvmBasedRecovery(params);
|
|
950
|
+
}
|
|
951
|
+
this.validateRecoveryParams(params);
|
|
952
|
+
const isUnsignedSweep = params.isUnsignedSweep ?? (0, sdk_core_1.getIsUnsignedSweep)(params);
|
|
953
|
+
// Clean up whitespace from entered values
|
|
954
|
+
let userKey = params.userKey.replace(/\s/g, '');
|
|
955
|
+
const backupKey = params.backupKey.replace(/\s/g, '');
|
|
956
|
+
const gasLimit = new exports.optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit));
|
|
957
|
+
const gasPrice = params.eip1559
|
|
958
|
+
? new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas)
|
|
959
|
+
: new exports.optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice));
|
|
960
|
+
if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) {
|
|
961
|
+
try {
|
|
962
|
+
userKey = await this.bitgo.decrypt({
|
|
963
|
+
input: userKey,
|
|
964
|
+
password: params.walletPassphrase,
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
catch (e) {
|
|
968
|
+
throw new Error(`Error decrypting user keychain: ${e.message}`);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
let backupKeyAddress;
|
|
972
|
+
let backupSigningKey;
|
|
973
|
+
if (isUnsignedSweep) {
|
|
974
|
+
const backupHDNode = secp256k1_1.bip32.fromBase58(backupKey);
|
|
975
|
+
backupSigningKey = backupHDNode.publicKey;
|
|
976
|
+
backupKeyAddress = `0x${exports.optionalDeps.ethUtil.publicToAddress(backupSigningKey, true).toString('hex')}`;
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
// Decrypt backup private key and get address
|
|
980
|
+
let backupPrv;
|
|
981
|
+
try {
|
|
982
|
+
backupPrv = await this.bitgo.decrypt({
|
|
983
|
+
input: backupKey,
|
|
984
|
+
password: params.walletPassphrase,
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
catch (e) {
|
|
988
|
+
throw new Error(`Error decrypting backup keychain: ${e.message}`);
|
|
989
|
+
}
|
|
990
|
+
const keyPair = new lib_1.KeyPair({ prv: backupPrv });
|
|
991
|
+
backupSigningKey = keyPair.getKeys().prv;
|
|
992
|
+
if (!backupSigningKey) {
|
|
993
|
+
throw new Error('no private key');
|
|
994
|
+
}
|
|
995
|
+
backupKeyAddress = keyPair.getAddress();
|
|
996
|
+
}
|
|
997
|
+
await this.assertRecoveryKeysAreWalletSigners(userKey, backupKeyAddress, params.walletContractAddress, params.apiKey);
|
|
998
|
+
const backupKeyNonce = await this.getAddressNonce(backupKeyAddress, params.apiKey);
|
|
999
|
+
// get balance of backupKey to ensure funds are available to pay fees
|
|
1000
|
+
const backupKeyBalance = await this.queryAddressBalance(backupKeyAddress, params.apiKey);
|
|
1001
|
+
let totalGasNeeded = gasPrice.mul(gasLimit);
|
|
1002
|
+
// On L2 chains with L1 data fees, add buffer for L1 fees
|
|
1003
|
+
if (this.staticsCoin?.family !== undefined && lib_1.coinFamiliesWithL1Fees.includes(this.staticsCoin.family)) {
|
|
1004
|
+
totalGasNeeded = totalGasNeeded.add(new exports.optionalDeps.ethUtil.BN(statics_1.ethGasConfigs.l1GasFeeBuffer));
|
|
1005
|
+
}
|
|
1006
|
+
const weiToGwei = 10 ** 9;
|
|
1007
|
+
if (backupKeyBalance.lt(totalGasNeeded)) {
|
|
1008
|
+
throw new Error(`Backup key address ${backupKeyAddress} has balance ${(backupKeyBalance / weiToGwei).toString()} Gwei.` +
|
|
1009
|
+
`This address must have a balance of at least ${(totalGasNeeded / weiToGwei).toString()}` +
|
|
1010
|
+
` Gwei to perform recoveries. Try sending some funds to this address then retry.`);
|
|
1011
|
+
}
|
|
1012
|
+
// get balance of wallet
|
|
1013
|
+
const txAmount = await this.queryAddressBalance(params.walletContractAddress, params.apiKey);
|
|
1014
|
+
if (new bignumber_js_1.BigNumber(txAmount).isLessThanOrEqualTo(0)) {
|
|
1015
|
+
throw new sdk_core_1.ErrorNoInputToRecover('Wallet does not have enough funds to recover');
|
|
1016
|
+
}
|
|
1017
|
+
// build recipients object
|
|
1018
|
+
const recipients = [
|
|
1019
|
+
{
|
|
1020
|
+
address: params.recoveryDestination,
|
|
1021
|
+
amount: txAmount.toString(10),
|
|
1022
|
+
},
|
|
1023
|
+
];
|
|
1024
|
+
// Get sequence ID using contract call
|
|
1025
|
+
// we need to wait between making two explorer api calls to avoid getting banned
|
|
1026
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1027
|
+
const sequenceId = await this.querySequenceId(params.walletContractAddress, params.apiKey);
|
|
1028
|
+
let operationHash, signature;
|
|
1029
|
+
// Get operation hash and sign it
|
|
1030
|
+
if (!isUnsignedSweep) {
|
|
1031
|
+
operationHash = this.getOperationSha3ForExecuteAndConfirm(recipients, this.getDefaultExpireTime(), sequenceId);
|
|
1032
|
+
signature = sdk_core_1.Util.ethSignMsgHash(operationHash, sdk_core_1.Util.xprvToEthPrivateKey(userKey));
|
|
1033
|
+
try {
|
|
1034
|
+
sdk_core_1.Util.ecRecoverEthAddress(operationHash, signature);
|
|
1035
|
+
}
|
|
1036
|
+
catch (e) {
|
|
1037
|
+
throw new Error('Invalid signature');
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
const txInfo = {
|
|
1041
|
+
recipient: recipients[0],
|
|
1042
|
+
expireTime: this.getDefaultExpireTime(),
|
|
1043
|
+
contractSequenceId: sequenceId,
|
|
1044
|
+
operationHash: operationHash,
|
|
1045
|
+
signature: signature,
|
|
1046
|
+
gasLimit: gasLimit.toString(10),
|
|
1047
|
+
};
|
|
1048
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
1049
|
+
txBuilder.counter(backupKeyNonce);
|
|
1050
|
+
txBuilder.contract(params.walletContractAddress);
|
|
1051
|
+
let txFee;
|
|
1052
|
+
if (params.eip1559) {
|
|
1053
|
+
txFee = {
|
|
1054
|
+
eip1559: {
|
|
1055
|
+
maxPriorityFeePerGas: params.eip1559.maxPriorityFeePerGas,
|
|
1056
|
+
maxFeePerGas: params.eip1559.maxFeePerGas,
|
|
1057
|
+
},
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
txFee = { fee: gasPrice.toString() };
|
|
1062
|
+
}
|
|
1063
|
+
txBuilder.fee({
|
|
1064
|
+
...txFee,
|
|
1065
|
+
gasLimit: gasLimit.toString(),
|
|
1066
|
+
});
|
|
1067
|
+
const transferBuilder = txBuilder.transfer();
|
|
1068
|
+
transferBuilder
|
|
1069
|
+
.coin(this.staticsCoin?.name)
|
|
1070
|
+
.amount(recipients[0].amount)
|
|
1071
|
+
.contractSequenceId(sequenceId)
|
|
1072
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
1073
|
+
.to(params.recoveryDestination);
|
|
1074
|
+
const tx = await txBuilder.build();
|
|
1075
|
+
if (isUnsignedSweep) {
|
|
1076
|
+
const response = {
|
|
1077
|
+
txHex: tx.toBroadcastFormat(),
|
|
1078
|
+
userKey,
|
|
1079
|
+
backupKey,
|
|
1080
|
+
coin: this.getChain(),
|
|
1081
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1082
|
+
gasLimit,
|
|
1083
|
+
recipients: [txInfo.recipient],
|
|
1084
|
+
walletContractAddress: tx.toJson().to,
|
|
1085
|
+
amount: txInfo.recipient.amount,
|
|
1086
|
+
backupKeyNonce,
|
|
1087
|
+
eip1559: params.eip1559,
|
|
1088
|
+
};
|
|
1089
|
+
lodash_1.default.extend(response, txInfo);
|
|
1090
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
1091
|
+
return response;
|
|
1092
|
+
}
|
|
1093
|
+
txBuilder
|
|
1094
|
+
.transfer()
|
|
1095
|
+
.coin(this.staticsCoin?.name)
|
|
1096
|
+
.key(new lib_1.KeyPair({ prv: userKey }).getKeys().prv);
|
|
1097
|
+
txBuilder.sign({ key: backupSigningKey });
|
|
1098
|
+
const signedTx = await txBuilder.build();
|
|
1099
|
+
return {
|
|
1100
|
+
id: signedTx.toJson().id,
|
|
1101
|
+
tx: signedTx.toBroadcastFormat(),
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Extract recipients from transaction hex
|
|
1106
|
+
* @param txHex - The transaction hex string
|
|
1107
|
+
* @returns Array of recipients with address and amount
|
|
1108
|
+
*/
|
|
1109
|
+
async extractRecipientsFromTxHex(txHex) {
|
|
1110
|
+
const txBuffer = exports.optionalDeps.ethUtil.toBuffer(txHex);
|
|
1111
|
+
const decodedTx = exports.optionalDeps.EthTx.TransactionFactory.fromSerializedData(txBuffer);
|
|
1112
|
+
const recipients = [];
|
|
1113
|
+
if (decodedTx.data && decodedTx.data.length > 0) {
|
|
1114
|
+
const dataHex = exports.optionalDeps.ethUtil.bufferToHex(decodedTx.data);
|
|
1115
|
+
const transferData = (0, lib_1.decodeTransferData)(dataHex);
|
|
1116
|
+
if (transferData.to) {
|
|
1117
|
+
recipients.push({
|
|
1118
|
+
address: transferData.to,
|
|
1119
|
+
amount: transferData.amount,
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
return recipients;
|
|
1124
|
+
}
|
|
1125
|
+
async sendCrossChainRecoveryTransaction(params) {
|
|
1126
|
+
const buildResponse = await this.buildCrossChainRecoveryTransaction(params.recoveryId);
|
|
1127
|
+
if (params.walletType === 'cold') {
|
|
1128
|
+
return buildResponse;
|
|
1129
|
+
}
|
|
1130
|
+
if (!params.encryptedPrv) {
|
|
1131
|
+
throw new Error('missing encryptedPrv');
|
|
1132
|
+
}
|
|
1133
|
+
let userKeyPrv;
|
|
1134
|
+
try {
|
|
1135
|
+
userKeyPrv = await this.bitgo.decrypt({
|
|
1136
|
+
input: params.encryptedPrv,
|
|
1137
|
+
password: params.walletPassphrase,
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
catch (e) {
|
|
1141
|
+
throw new Error(`Error decrypting user keychain: ${e.message}`);
|
|
1142
|
+
}
|
|
1143
|
+
const keyPair = new lib_1.KeyPair({ prv: userKeyPrv });
|
|
1144
|
+
const userSigningKey = keyPair.getKeys().prv;
|
|
1145
|
+
if (!userSigningKey) {
|
|
1146
|
+
throw new Error('no private key');
|
|
1147
|
+
}
|
|
1148
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
1149
|
+
const txHex = buildResponse.txHex;
|
|
1150
|
+
txBuilder.from(txHex);
|
|
1151
|
+
if (buildResponse.walletVersion) {
|
|
1152
|
+
// If walletVersion is provided, set it in the txBuilder
|
|
1153
|
+
txBuilder.walletVersion(buildResponse.walletVersion);
|
|
1154
|
+
}
|
|
1155
|
+
txBuilder
|
|
1156
|
+
.transfer()
|
|
1157
|
+
.coin(this.staticsCoin?.name)
|
|
1158
|
+
.key(userSigningKey);
|
|
1159
|
+
const tx = await txBuilder.build();
|
|
1160
|
+
const res = await this.bitgo
|
|
1161
|
+
.post(this.bitgo.microservicesUrl(`/api/recovery/v1/crosschain/${params.recoveryId}/sign`))
|
|
1162
|
+
.send({ txHex: tx.toBroadcastFormat() });
|
|
1163
|
+
return {
|
|
1164
|
+
coin: this.staticsCoin?.name,
|
|
1165
|
+
txid: res.body.txid,
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
async buildCrossChainRecoveryTransaction(recoveryId) {
|
|
1169
|
+
const res = await this.bitgo.get(this.bitgo.microservicesUrl(`/api/recovery/v1/crosschain/${recoveryId}/buildtx`));
|
|
1170
|
+
// Extract recipients from the transaction hex
|
|
1171
|
+
const recipients = await this.extractRecipientsFromTxHex(res.body.txHex);
|
|
1172
|
+
return {
|
|
1173
|
+
coin: res.body.coin,
|
|
1174
|
+
txHex: res.body.txHex,
|
|
1175
|
+
txid: res.body.txid,
|
|
1176
|
+
walletVersion: res.body.walletVersion,
|
|
1177
|
+
recipients,
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Builds a unsigned (for cold, custody wallet) or
|
|
1182
|
+
* half-signed (for hot wallet) evm cross chain recovery transaction with
|
|
1183
|
+
* same expected arguments as recover method.
|
|
1184
|
+
* This helps recover funds from evm based wrong chain.
|
|
1185
|
+
* @param {RecoverOptions} params
|
|
1186
|
+
* @returns {Promise<RecoveryInfo | OfflineVaultTxInfo>}
|
|
1187
|
+
*/
|
|
1188
|
+
async recoverEthLikeforEvmBasedRecovery(params) {
|
|
1189
|
+
this.validateEvmBasedRecoveryParams(params);
|
|
1190
|
+
// Clean up whitespace from entered values
|
|
1191
|
+
const userKey = params.userKey.replace(/\s/g, '');
|
|
1192
|
+
const bitgoFeeAddress = params.bitgoFeeAddress?.replace(/\s/g, '').toLowerCase();
|
|
1193
|
+
const bitgoDestinationAddress = params.bitgoDestinationAddress?.replace(/\s/g, '').toLowerCase();
|
|
1194
|
+
const recoveryDestination = params.recoveryDestination?.replace(/\s/g, '').toLowerCase();
|
|
1195
|
+
const walletContractAddress = params.walletContractAddress?.replace(/\s/g, '').toLowerCase();
|
|
1196
|
+
const tokenContractAddress = params.tokenContractAddress?.replace(/\s/g, '').toLowerCase();
|
|
1197
|
+
let userSigningKey;
|
|
1198
|
+
let userKeyPrv;
|
|
1199
|
+
if (params.walletPassphrase) {
|
|
1200
|
+
if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) {
|
|
1201
|
+
try {
|
|
1202
|
+
userKeyPrv = await this.bitgo.decrypt({
|
|
1203
|
+
input: userKey,
|
|
1204
|
+
password: params.walletPassphrase,
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
catch (e) {
|
|
1208
|
+
throw new Error(`Error decrypting user keychain: ${e.message}`);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
const keyPair = new lib_1.KeyPair({ prv: userKeyPrv });
|
|
1212
|
+
userSigningKey = keyPair.getKeys().prv;
|
|
1213
|
+
if (!userSigningKey) {
|
|
1214
|
+
throw new Error('no private key');
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
// Use default gasLimit for cold and custody wallets
|
|
1218
|
+
let gasLimit = params.gasLimit || userKey.startsWith('xpub') || !userKey
|
|
1219
|
+
? new exports.optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit))
|
|
1220
|
+
: new exports.optionalDeps.ethUtil.BN(0);
|
|
1221
|
+
const gasPrice = params.eip1559
|
|
1222
|
+
? new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas)
|
|
1223
|
+
: params.gasPrice
|
|
1224
|
+
? new exports.optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice))
|
|
1225
|
+
: await this.getGasPriceFromExternalAPI(this.staticsCoin?.name, params.apiKey);
|
|
1226
|
+
const bitgoFeeAddressNonce = await this.getAddressNonce(bitgoFeeAddress, params.apiKey);
|
|
1227
|
+
if (tokenContractAddress) {
|
|
1228
|
+
return this.recoverEthLikeTokenforEvmBasedRecovery(params, bitgoFeeAddressNonce, gasLimit, gasPrice, userKey, userSigningKey, params.apiKey);
|
|
1229
|
+
}
|
|
1230
|
+
// get balance of wallet
|
|
1231
|
+
const txAmount = await this.queryAddressBalance(walletContractAddress, params.apiKey);
|
|
1232
|
+
const bitgoFeePercentage = 0; // TODO: BG-71912 can change the fee% here.
|
|
1233
|
+
const bitgoFeeAmount = txAmount * (bitgoFeePercentage / 100);
|
|
1234
|
+
// build recipients object
|
|
1235
|
+
const recipients = [
|
|
1236
|
+
{
|
|
1237
|
+
address: recoveryDestination,
|
|
1238
|
+
amount: new bignumber_js_1.BigNumber(txAmount).minus(bitgoFeeAmount).toFixed(),
|
|
1239
|
+
},
|
|
1240
|
+
];
|
|
1241
|
+
if (bitgoFeePercentage > 0) {
|
|
1242
|
+
if (lodash_1.default.isUndefined(bitgoDestinationAddress) || !this.isValidAddress(bitgoDestinationAddress)) {
|
|
1243
|
+
throw new Error('invalid bitgoDestinationAddress');
|
|
1244
|
+
}
|
|
1245
|
+
recipients.push({
|
|
1246
|
+
address: bitgoDestinationAddress,
|
|
1247
|
+
amount: bitgoFeeAmount.toString(10),
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
// calculate batch data
|
|
1251
|
+
const BATCH_METHOD_NAME = 'batch';
|
|
1252
|
+
const BATCH_METHOD_TYPES = ['address[]', 'uint256[]'];
|
|
1253
|
+
const batchExecutionInfo = this.getBatchExecutionInfo(recipients);
|
|
1254
|
+
const batchData = exports.optionalDeps.ethUtil.addHexPrefix(this.getMethodCallData(BATCH_METHOD_NAME, BATCH_METHOD_TYPES, batchExecutionInfo.values).toString('hex'));
|
|
1255
|
+
// Get sequence ID using contract call
|
|
1256
|
+
// we need to wait between making two explorer api calls to avoid getting banned
|
|
1257
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1258
|
+
const sequenceId = await this.querySequenceId(walletContractAddress, params.apiKey);
|
|
1259
|
+
const network = this.getNetwork();
|
|
1260
|
+
const batcherContractAddress = network?.batcherContractAddress;
|
|
1261
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
1262
|
+
txBuilder.counter(bitgoFeeAddressNonce);
|
|
1263
|
+
txBuilder.contract(walletContractAddress);
|
|
1264
|
+
let txFee;
|
|
1265
|
+
if (params.eip1559) {
|
|
1266
|
+
txFee = {
|
|
1267
|
+
eip1559: {
|
|
1268
|
+
maxPriorityFeePerGas: params.eip1559.maxPriorityFeePerGas,
|
|
1269
|
+
maxFeePerGas: params.eip1559.maxFeePerGas,
|
|
1270
|
+
},
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
else {
|
|
1274
|
+
txFee = { fee: gasPrice.toString() };
|
|
1275
|
+
}
|
|
1276
|
+
txBuilder.fee({
|
|
1277
|
+
...txFee,
|
|
1278
|
+
gasLimit: gasLimit.toString(),
|
|
1279
|
+
});
|
|
1280
|
+
const transferBuilder = txBuilder.transfer();
|
|
1281
|
+
if (!batcherContractAddress) {
|
|
1282
|
+
transferBuilder
|
|
1283
|
+
.coin(this.staticsCoin?.name)
|
|
1284
|
+
.amount(batchExecutionInfo.totalAmount)
|
|
1285
|
+
.contractSequenceId(sequenceId)
|
|
1286
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
1287
|
+
.to(recoveryDestination);
|
|
1288
|
+
}
|
|
1289
|
+
else {
|
|
1290
|
+
transferBuilder
|
|
1291
|
+
.coin(this.staticsCoin?.name)
|
|
1292
|
+
.amount(batchExecutionInfo.totalAmount)
|
|
1293
|
+
.contractSequenceId(sequenceId)
|
|
1294
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
1295
|
+
.to(batcherContractAddress)
|
|
1296
|
+
.data(batchData);
|
|
1297
|
+
}
|
|
1298
|
+
if (params.walletPassphrase) {
|
|
1299
|
+
transferBuilder.key(userSigningKey);
|
|
1300
|
+
}
|
|
1301
|
+
// If the intended chain is arbitrum or optimism, we need to use wallet version 4
|
|
1302
|
+
// since these contracts construct operationHash differently
|
|
1303
|
+
if (params.intendedChain && ['arbeth', 'opeth'].includes(statics_1.coins.get(params.intendedChain).family)) {
|
|
1304
|
+
txBuilder.walletVersion(4);
|
|
1305
|
+
}
|
|
1306
|
+
// If gasLimit was not passed as a param or if it is not cold/custody wallet, then fetch the gasLimit from Explorer
|
|
1307
|
+
if (!params.gasLimit && userKey && !userKey.startsWith('xpub')) {
|
|
1308
|
+
const sendData = txBuilder.getSendData();
|
|
1309
|
+
gasLimit = await this.getGasLimitFromExternalAPI(params.intendedChain, params.bitgoFeeAddress, params.walletContractAddress, sendData, params.apiKey);
|
|
1310
|
+
txBuilder.fee({
|
|
1311
|
+
...txFee,
|
|
1312
|
+
gasLimit: gasLimit.toString(),
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
// Get the balance of bitgoFeeAddress to ensure funds are available to pay fees
|
|
1316
|
+
await this.ensureSufficientBalance(bitgoFeeAddress, gasPrice, gasLimit, params.apiKey);
|
|
1317
|
+
const tx = await txBuilder.build();
|
|
1318
|
+
const txInfo = {
|
|
1319
|
+
recipients: recipients,
|
|
1320
|
+
expireTime: this.getDefaultExpireTime(),
|
|
1321
|
+
contractSequenceId: sequenceId,
|
|
1322
|
+
gasLimit: gasLimit.toString(10),
|
|
1323
|
+
isEvmBasedCrossChainRecovery: true,
|
|
1324
|
+
};
|
|
1325
|
+
const response = {
|
|
1326
|
+
txHex: tx.toBroadcastFormat(),
|
|
1327
|
+
userKey,
|
|
1328
|
+
coin: this.getChain(),
|
|
1329
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1330
|
+
gasLimit,
|
|
1331
|
+
recipients: txInfo.recipients,
|
|
1332
|
+
walletContractAddress: tx.toJson().to,
|
|
1333
|
+
amount: batchExecutionInfo.totalAmount,
|
|
1334
|
+
backupKeyNonce: bitgoFeeAddressNonce,
|
|
1335
|
+
eip1559: params.eip1559,
|
|
1336
|
+
...(txBuilder.getWalletVersion() === 4 ? { walletVersion: txBuilder.getWalletVersion() } : {}),
|
|
1337
|
+
};
|
|
1338
|
+
lodash_1.default.extend(response, txInfo);
|
|
1339
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
1340
|
+
if (params.walletPassphrase) {
|
|
1341
|
+
const halfSignedTxn = {
|
|
1342
|
+
halfSigned: {
|
|
1343
|
+
txHex: tx.toBroadcastFormat(),
|
|
1344
|
+
recipients: txInfo.recipients,
|
|
1345
|
+
expireTime: txInfo.expireTime,
|
|
1346
|
+
},
|
|
1347
|
+
};
|
|
1348
|
+
lodash_1.default.extend(response, halfSignedTxn);
|
|
1349
|
+
const feesUsed = {
|
|
1350
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1351
|
+
gasLimit: exports.optionalDeps.ethUtil.bufferToInt(gasLimit).toFixed(),
|
|
1352
|
+
};
|
|
1353
|
+
response['feesUsed'] = feesUsed;
|
|
1354
|
+
}
|
|
1355
|
+
return response;
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Query explorer for the balance of an address for a token
|
|
1359
|
+
* @param {string} tokenContractAddress - address where the token smart contract is hosted
|
|
1360
|
+
* @param {string} walletContractAddress - address of the wallet
|
|
1361
|
+
* @param {string} apiKey - optional API key to use instead of the one from the environment
|
|
1362
|
+
* @returns {BigNumber} token balaance in base units
|
|
1363
|
+
*/
|
|
1364
|
+
async queryAddressTokenBalance(tokenContractAddress, walletContractAddress, apiKey) {
|
|
1365
|
+
if (!exports.optionalDeps.ethUtil.isValidAddress(tokenContractAddress)) {
|
|
1366
|
+
throw new Error('cannot get balance for invalid token address');
|
|
1367
|
+
}
|
|
1368
|
+
if (!exports.optionalDeps.ethUtil.isValidAddress(walletContractAddress)) {
|
|
1369
|
+
throw new Error('cannot get token balance for invalid wallet address');
|
|
1370
|
+
}
|
|
1371
|
+
const result = await this.recoveryBlockchainExplorerQuery({
|
|
1372
|
+
chainid: this.getChainId().toString(),
|
|
1373
|
+
module: 'account',
|
|
1374
|
+
action: 'tokenbalance',
|
|
1375
|
+
contractaddress: tokenContractAddress,
|
|
1376
|
+
address: walletContractAddress,
|
|
1377
|
+
tag: 'latest',
|
|
1378
|
+
}, apiKey);
|
|
1379
|
+
// throw if the result does not exist or the result is not a valid number
|
|
1380
|
+
if (!result || !result.result || isNaN(result.result)) {
|
|
1381
|
+
throw new sdk_core_1.RecoveryProviderError(`Could not obtain token address balance for ${tokenContractAddress} from Etherscan, got: ${result.result}`);
|
|
1382
|
+
}
|
|
1383
|
+
return new exports.optionalDeps.ethUtil.BN(result.result, 10);
|
|
1384
|
+
}
|
|
1385
|
+
async recoverEthLikeTokenforEvmBasedRecovery(params, bitgoFeeAddressNonce, gasLimit, gasPrice, userKey, userSigningKey, apiKey) {
|
|
1386
|
+
// get token balance of wallet
|
|
1387
|
+
const txAmount = await this.queryAddressTokenBalance(params.tokenContractAddress, params.walletContractAddress, apiKey);
|
|
1388
|
+
// build recipients object
|
|
1389
|
+
const recipients = [
|
|
1390
|
+
{
|
|
1391
|
+
address: params.recoveryDestination,
|
|
1392
|
+
amount: new bignumber_js_1.BigNumber(txAmount).toFixed(),
|
|
1393
|
+
},
|
|
1394
|
+
];
|
|
1395
|
+
// Get sequence ID using contract call
|
|
1396
|
+
// we need to wait between making two explorer api calls to avoid getting banned
|
|
1397
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1398
|
+
const sequenceId = await this.querySequenceId(params.walletContractAddress, params.apiKey);
|
|
1399
|
+
const txBuilder = this.getTransactionBuilder(params.common);
|
|
1400
|
+
txBuilder.counter(bitgoFeeAddressNonce);
|
|
1401
|
+
txBuilder.contract(params.walletContractAddress);
|
|
1402
|
+
let txFee;
|
|
1403
|
+
if (params.eip1559) {
|
|
1404
|
+
txFee = {
|
|
1405
|
+
eip1559: {
|
|
1406
|
+
maxPriorityFeePerGas: params.eip1559.maxPriorityFeePerGas,
|
|
1407
|
+
maxFeePerGas: params.eip1559.maxFeePerGas,
|
|
1408
|
+
},
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
else {
|
|
1412
|
+
txFee = { fee: gasPrice.toString() };
|
|
1413
|
+
}
|
|
1414
|
+
txBuilder.fee({
|
|
1415
|
+
...txFee,
|
|
1416
|
+
gasLimit: gasLimit.toString(),
|
|
1417
|
+
});
|
|
1418
|
+
const transferBuilder = txBuilder.transfer();
|
|
1419
|
+
const network = this.getNetwork();
|
|
1420
|
+
const token = (0, lib_1.getToken)(params.tokenContractAddress, network, this.staticsCoin?.family)?.name;
|
|
1421
|
+
transferBuilder
|
|
1422
|
+
.amount(txAmount)
|
|
1423
|
+
.contractSequenceId(sequenceId)
|
|
1424
|
+
.expirationTime(this.getDefaultExpireTime())
|
|
1425
|
+
.to(params.recoveryDestination);
|
|
1426
|
+
if (token) {
|
|
1427
|
+
transferBuilder.coin(token);
|
|
1428
|
+
}
|
|
1429
|
+
else {
|
|
1430
|
+
transferBuilder
|
|
1431
|
+
.coin(this.staticsCoin?.name)
|
|
1432
|
+
.tokenContractAddress(params.tokenContractAddress);
|
|
1433
|
+
}
|
|
1434
|
+
if (params.walletPassphrase) {
|
|
1435
|
+
txBuilder.transfer().key(userSigningKey);
|
|
1436
|
+
}
|
|
1437
|
+
// If the intended chain is arbitrum or optimism, we need to use wallet version 4
|
|
1438
|
+
// since these contracts construct operationHash differently
|
|
1439
|
+
if (params.intendedChain && ['arbeth', 'opeth'].includes(statics_1.coins.get(params.intendedChain).family)) {
|
|
1440
|
+
txBuilder.walletVersion(4);
|
|
1441
|
+
}
|
|
1442
|
+
if (!params.gasLimit && userKey && !userKey.startsWith('xpub')) {
|
|
1443
|
+
const sendData = txBuilder.getSendData();
|
|
1444
|
+
gasLimit = await this.getGasLimitFromExternalAPI(params.intendedChain, params.bitgoFeeAddress, params.walletContractAddress, sendData, apiKey);
|
|
1445
|
+
txBuilder.fee({
|
|
1446
|
+
...txFee,
|
|
1447
|
+
gasLimit: gasLimit.toString(),
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
// Get the balance of bitgoFeeAddress to ensure funds are available to pay fees
|
|
1451
|
+
await this.ensureSufficientBalance(params.bitgoFeeAddress, gasPrice, gasLimit, params.apiKey);
|
|
1452
|
+
const tx = await txBuilder.build();
|
|
1453
|
+
const txInfo = {
|
|
1454
|
+
recipients: recipients,
|
|
1455
|
+
expireTime: this.getDefaultExpireTime(),
|
|
1456
|
+
contractSequenceId: sequenceId,
|
|
1457
|
+
gasLimit: gasLimit.toString(10),
|
|
1458
|
+
isEvmBasedCrossChainRecovery: true,
|
|
1459
|
+
};
|
|
1460
|
+
const response = {
|
|
1461
|
+
txHex: tx.toBroadcastFormat(),
|
|
1462
|
+
userKey,
|
|
1463
|
+
coin: token ? token : this.getChain(),
|
|
1464
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1465
|
+
gasLimit,
|
|
1466
|
+
recipients: txInfo.recipients,
|
|
1467
|
+
walletContractAddress: tx.toJson().to,
|
|
1468
|
+
amount: txAmount.toString(),
|
|
1469
|
+
backupKeyNonce: bitgoFeeAddressNonce,
|
|
1470
|
+
eip1559: params.eip1559,
|
|
1471
|
+
...(txBuilder.getWalletVersion() === 4 ? { walletVersion: txBuilder.getWalletVersion() } : {}),
|
|
1472
|
+
};
|
|
1473
|
+
lodash_1.default.extend(response, txInfo);
|
|
1474
|
+
response.nextContractSequenceId = response.contractSequenceId;
|
|
1475
|
+
if (params.walletPassphrase) {
|
|
1476
|
+
const halfSignedTxn = {
|
|
1477
|
+
halfSigned: {
|
|
1478
|
+
txHex: tx.toBroadcastFormat(),
|
|
1479
|
+
recipients: txInfo.recipients,
|
|
1480
|
+
expireTime: txInfo.expireTime,
|
|
1481
|
+
},
|
|
1482
|
+
};
|
|
1483
|
+
lodash_1.default.extend(response, halfSignedTxn);
|
|
1484
|
+
const feesUsed = {
|
|
1485
|
+
gasPrice: exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed(),
|
|
1486
|
+
gasLimit: exports.optionalDeps.ethUtil.bufferToInt(gasLimit).toFixed(),
|
|
1487
|
+
};
|
|
1488
|
+
response['feesUsed'] = feesUsed;
|
|
1489
|
+
}
|
|
1490
|
+
return response;
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Validate evm based cross chain recovery params
|
|
1494
|
+
* @param params {RecoverOptions}
|
|
1495
|
+
* @returns {void}
|
|
1496
|
+
*/
|
|
1497
|
+
validateEvmBasedRecoveryParams(params) {
|
|
1498
|
+
if (lodash_1.default.isUndefined(params.bitgoFeeAddress) || !this.isValidAddress(params.bitgoFeeAddress)) {
|
|
1499
|
+
throw new Error('invalid bitgoFeeAddress');
|
|
1500
|
+
}
|
|
1501
|
+
if (lodash_1.default.isUndefined(params.walletContractAddress) || !this.isValidAddress(params.walletContractAddress)) {
|
|
1502
|
+
throw new Error('invalid walletContractAddress');
|
|
1503
|
+
}
|
|
1504
|
+
if (lodash_1.default.isUndefined(params.recoveryDestination) || !this.isValidAddress(params.recoveryDestination)) {
|
|
1505
|
+
throw new Error('invalid recoveryDestination');
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Return types, values, and total amount in wei to send in a batch transaction, using the method signature
|
|
1510
|
+
* `distributeBatch(address[], uint256[])`
|
|
1511
|
+
* @param {Recipient[]} recipients - transaction recipients
|
|
1512
|
+
* @returns {GetBatchExecutionInfoRT} information needed to execute the batch transaction
|
|
1513
|
+
*/
|
|
1514
|
+
getBatchExecutionInfo(recipients) {
|
|
1515
|
+
const addresses = [];
|
|
1516
|
+
const amounts = [];
|
|
1517
|
+
let sum = new bignumber_js_1.BigNumber('0');
|
|
1518
|
+
lodash_1.default.forEach(recipients, ({ address, amount }) => {
|
|
1519
|
+
addresses.push(address);
|
|
1520
|
+
amounts.push(amount);
|
|
1521
|
+
sum = sum.plus(amount);
|
|
1522
|
+
});
|
|
1523
|
+
return {
|
|
1524
|
+
values: [addresses, amounts],
|
|
1525
|
+
totalAmount: sum.toFixed(),
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Build arguments to call the send method on the wallet contract
|
|
1530
|
+
* @param txInfo
|
|
1531
|
+
*/
|
|
1532
|
+
getSendMethodArgs(txInfo) {
|
|
1533
|
+
// Method signature is
|
|
1534
|
+
// sendMultiSig(address toAddress, uint value, bytes data, uint expireTime, uint sequenceId, bytes signature)
|
|
1535
|
+
return [
|
|
1536
|
+
{
|
|
1537
|
+
name: 'toAddress',
|
|
1538
|
+
type: 'address',
|
|
1539
|
+
value: txInfo.recipient.address,
|
|
1540
|
+
},
|
|
1541
|
+
{
|
|
1542
|
+
name: 'value',
|
|
1543
|
+
type: 'uint',
|
|
1544
|
+
value: txInfo.recipient.amount,
|
|
1545
|
+
},
|
|
1546
|
+
{
|
|
1547
|
+
name: 'data',
|
|
1548
|
+
type: 'bytes',
|
|
1549
|
+
value: exports.optionalDeps.ethUtil.toBuffer(exports.optionalDeps.ethUtil.addHexPrefix(txInfo.recipient.data || '')),
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
name: 'expireTime',
|
|
1553
|
+
type: 'uint',
|
|
1554
|
+
value: txInfo.expireTime,
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
name: 'sequenceId',
|
|
1558
|
+
type: 'uint',
|
|
1559
|
+
value: txInfo.contractSequenceId,
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
name: 'signature',
|
|
1563
|
+
type: 'bytes',
|
|
1564
|
+
value: exports.optionalDeps.ethUtil.toBuffer(exports.optionalDeps.ethUtil.addHexPrefix(txInfo.signature)),
|
|
1565
|
+
},
|
|
1566
|
+
];
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Recovers a tx with TSS key shares
|
|
1570
|
+
* same expected arguments as recover method, but with TSS key shares
|
|
1571
|
+
*/
|
|
1572
|
+
async recoverTSS(params) {
|
|
1573
|
+
this.validateRecoveryParams(params);
|
|
1574
|
+
// Clean up whitespace from entered values
|
|
1575
|
+
const userPublicOrPrivateKeyShare = params.userKey.replace(/\s/g, '');
|
|
1576
|
+
const backupPrivateOrPublicKeyShare = params.backupKey.replace(/\s/g, '');
|
|
1577
|
+
if ((0, sdk_core_1.getIsUnsignedSweep)({
|
|
1578
|
+
userKey: userPublicOrPrivateKeyShare,
|
|
1579
|
+
backupKey: backupPrivateOrPublicKeyShare,
|
|
1580
|
+
isTss: params.isTss,
|
|
1581
|
+
})) {
|
|
1582
|
+
return this.buildUnsignedSweepTxnTSS(params);
|
|
1583
|
+
}
|
|
1584
|
+
else {
|
|
1585
|
+
const { userKeyShare, backupKeyShare, commonKeyChain } = await sdk_core_1.ECDSAUtils.getMpcV2RecoveryKeyShares(userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, params.walletPassphrase, this.bitgo);
|
|
1586
|
+
const { gasLimit, gasPrice } = await this.getGasValues(params);
|
|
1587
|
+
const MPC = new sdk_core_1.Ecdsa();
|
|
1588
|
+
const derivedCommonKeyChain = MPC.deriveUnhardened(commonKeyChain, 'm/0');
|
|
1589
|
+
const backupKeyPair = new lib_1.KeyPair({ pub: derivedCommonKeyChain.slice(0, 66) });
|
|
1590
|
+
const baseAddress = backupKeyPair.getAddress();
|
|
1591
|
+
if (baseAddress.toLowerCase() !== params.walletContractAddress.toLowerCase()) {
|
|
1592
|
+
throw new sdk_core_1.RecoveryKeyMismatchError(`derived wallet base address ${baseAddress} does not match the given wallet contract address ${params.walletContractAddress}`);
|
|
1593
|
+
}
|
|
1594
|
+
const unsignedTx = (await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params)).tx;
|
|
1595
|
+
const messageHash = unsignedTx.getMessageToSign(true);
|
|
1596
|
+
const signature = await sdk_core_1.ECDSAUtils.signRecoveryMpcV2(messageHash, userKeyShare, backupKeyShare, commonKeyChain);
|
|
1597
|
+
const ethCommmon = AbstractEthLikeNewCoins.getEthLikeCommon(params.eip1559, params.replayProtectionOptions);
|
|
1598
|
+
const signedTx = this.getSignedTxFromSignature(ethCommmon, unsignedTx, signature);
|
|
1599
|
+
return {
|
|
1600
|
+
id: (0, ethereumjs_util_1.addHexPrefix)(signedTx.hash().toString('hex')),
|
|
1601
|
+
tx: (0, ethereumjs_util_1.addHexPrefix)(signedTx.serialize().toString('hex')),
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
async getGasValues(params) {
|
|
1606
|
+
const gasLimit = new exports.optionalDeps.ethUtil.BN(this.setGasLimit(params.gasLimit));
|
|
1607
|
+
const gasPrice = params.eip1559
|
|
1608
|
+
? new exports.optionalDeps.ethUtil.BN(params.eip1559.maxFeePerGas)
|
|
1609
|
+
: new exports.optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice));
|
|
1610
|
+
return { gasLimit, gasPrice };
|
|
1611
|
+
}
|
|
1612
|
+
async buildUnsignedSweepTxnTSS(params) {
|
|
1613
|
+
const userPublicOrPrivateKeyShare = params.userKey.replace(/\s/g, '');
|
|
1614
|
+
const backupPrivateOrPublicKeyShare = params.backupKey.replace(/\s/g, '');
|
|
1615
|
+
const { gasLimit, gasPrice } = await this.getGasValues(params);
|
|
1616
|
+
const backupKeyPair = new lib_1.KeyPair({ pub: backupPrivateOrPublicKeyShare });
|
|
1617
|
+
const baseAddress = backupKeyPair.getAddress();
|
|
1618
|
+
const { txInfo, tx, nonce } = await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params);
|
|
1619
|
+
return this.formatForOfflineVaultTSS(txInfo, tx, userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, gasPrice, gasLimit, nonce, params.eip1559, params.replayProtectionOptions);
|
|
1620
|
+
}
|
|
1621
|
+
async buildUnsignedSweepTxnMPCv2(params) {
|
|
1622
|
+
const { gasLimit, gasPrice } = await this.getGasValues(params);
|
|
1623
|
+
const recoverParams = params;
|
|
1624
|
+
this.validateUnsignedSweepTSSParams(recoverParams);
|
|
1625
|
+
const derivationPath = recoverParams.derivationSeed ? (0, sdk_lib_mpc_1.getDerivationPath)(recoverParams.derivationSeed) : 'm/0';
|
|
1626
|
+
const MPC = new sdk_core_1.Ecdsa();
|
|
1627
|
+
const derivedCommonKeyChain = MPC.deriveUnhardened(recoverParams.backupKey, derivationPath);
|
|
1628
|
+
const backupKeyPair = new lib_1.KeyPair({ pub: derivedCommonKeyChain.slice(0, 66) });
|
|
1629
|
+
const baseAddress = backupKeyPair.getAddress();
|
|
1630
|
+
const { txInfo, tx, nonce } = await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params);
|
|
1631
|
+
return this.buildTxRequestForOfflineVaultMPCv2(txInfo, tx, derivationPath, nonce, gasPrice, gasLimit, params.eip1559, params.replayProtectionOptions, recoverParams.backupKey);
|
|
1632
|
+
}
|
|
1633
|
+
async createBroadcastableSweepTransaction(params) {
|
|
1634
|
+
const req = params.signatureShares;
|
|
1635
|
+
const broadcastableTransactions = [];
|
|
1636
|
+
let lastScanIndex = 0;
|
|
1637
|
+
for (let i = 0; i < req.length; i++) {
|
|
1638
|
+
const transaction = req[i]?.txRequest?.transactions?.[0]?.unsignedTx;
|
|
1639
|
+
if (!req[i].ovc || !req[i].ovc[0].ecdsaSignature) {
|
|
1640
|
+
throw new Error('Missing signature(s)');
|
|
1641
|
+
}
|
|
1642
|
+
if (!transaction.signableHex) {
|
|
1643
|
+
throw new Error('Missing signable hex');
|
|
1644
|
+
}
|
|
1645
|
+
const signature = req[i].ovc[0].ecdsaSignature;
|
|
1646
|
+
if (!signature) {
|
|
1647
|
+
throw new Error('Signature is undefined');
|
|
1648
|
+
}
|
|
1649
|
+
const shares = signature.toString().split(':');
|
|
1650
|
+
if (shares.length !== 4) {
|
|
1651
|
+
throw new Error('Invalid signature');
|
|
1652
|
+
}
|
|
1653
|
+
const finalSignature = {
|
|
1654
|
+
recid: Number(shares[0]),
|
|
1655
|
+
r: shares[1],
|
|
1656
|
+
s: shares[2],
|
|
1657
|
+
y: shares[3],
|
|
1658
|
+
};
|
|
1659
|
+
if (!transaction.coinSpecific?.commonKeyChain) {
|
|
1660
|
+
throw new Error(`Missing common keychain for transaction at index ${i}`);
|
|
1661
|
+
}
|
|
1662
|
+
const commonKeyChain = transaction.coinSpecific.commonKeyChain;
|
|
1663
|
+
if (!transaction.derivationPath) {
|
|
1664
|
+
throw new Error(`Missing derivation path for transaction at index ${i}`);
|
|
1665
|
+
}
|
|
1666
|
+
if (!commonKeyChain) {
|
|
1667
|
+
throw new Error(`Missing common key chain for transaction at index ${i}`);
|
|
1668
|
+
}
|
|
1669
|
+
const ethCommmon = AbstractEthLikeNewCoins.getEthLikeCommon(transaction.eip1559, transaction.replayProtectionOptions);
|
|
1670
|
+
let unsignedTx;
|
|
1671
|
+
if (transaction.eip1559) {
|
|
1672
|
+
unsignedTx = tx_1.FeeMarketEIP1559Transaction.fromSerializedTx(Buffer.from(transaction.serializedTxHex, 'hex'));
|
|
1673
|
+
}
|
|
1674
|
+
else {
|
|
1675
|
+
unsignedTx = tx_1.Transaction.fromSerializedTx(Buffer.from(transaction.serializedTxHex, 'hex'));
|
|
1676
|
+
}
|
|
1677
|
+
const signedTx = this.getSignedTxFromSignature(ethCommmon, unsignedTx, finalSignature);
|
|
1678
|
+
broadcastableTransactions.push({
|
|
1679
|
+
serializedTx: (0, ethereumjs_util_1.addHexPrefix)(signedTx.serialize().toString('hex')),
|
|
1680
|
+
});
|
|
1681
|
+
if (i === req.length - 1 && transaction.coinSpecific?.lastScanIndex) {
|
|
1682
|
+
lastScanIndex = transaction.coinSpecific?.lastScanIndex;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return { transactions: broadcastableTransactions, lastScanIndex };
|
|
1686
|
+
}
|
|
1687
|
+
/**
|
|
1688
|
+
* Method to validate recovery params
|
|
1689
|
+
* @param {RecoverOptions} params
|
|
1690
|
+
* @returns {void}
|
|
1691
|
+
*/
|
|
1692
|
+
async validateUnsignedSweepTSSParams(params) {
|
|
1693
|
+
if (lodash_1.default.isUndefined(params.backupKey) && params.backupKey === '') {
|
|
1694
|
+
throw new Error('missing commonKeyChain');
|
|
1695
|
+
}
|
|
1696
|
+
if (!lodash_1.default.isUndefined(params.derivationSeed) && typeof params.derivationSeed !== 'string') {
|
|
1697
|
+
throw new Error('invalid derivationSeed');
|
|
1698
|
+
}
|
|
1699
|
+
if (lodash_1.default.isUndefined(params.recoveryDestination) || !this.isValidAddress(params.recoveryDestination)) {
|
|
1700
|
+
throw new Error('missing or invalid destinationAddress');
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* Helper function for recover()
|
|
1705
|
+
* This transforms the unsigned transaction information into a format the BitGo offline vault expects
|
|
1706
|
+
* @param {UnformattedTxInfo} txInfo - tx info
|
|
1707
|
+
* @param {LegacyTransaction | FeeMarketEIP1559Transaction} ethTx - the ethereumjs tx object
|
|
1708
|
+
* @param {string} derivationPath - the derivationPath
|
|
1709
|
+
* @param {number} nonce - the nonce of the backup key address
|
|
1710
|
+
* @param {Buffer} gasPrice - gas price for the tx
|
|
1711
|
+
* @param {number} gasLimit - gas limit for the tx
|
|
1712
|
+
* @param {EIP1559} eip1559 - eip1559 params
|
|
1713
|
+
* @param replayProtectionOptions
|
|
1714
|
+
* @param commonKeyChain
|
|
1715
|
+
* @returns {Promise<OfflineVaultTxInfo>}
|
|
1716
|
+
*/
|
|
1717
|
+
buildTxRequestForOfflineVaultMPCv2(txInfo, ethTx, derivationPath, nonce, gasPrice, gasLimit, eip1559, replayProtectionOptions, commonKeyChain) {
|
|
1718
|
+
if (!ethTx.to) {
|
|
1719
|
+
throw new Error('Eth tx must have a `to` address');
|
|
1720
|
+
}
|
|
1721
|
+
const fee = eip1559
|
|
1722
|
+
? gasLimit * eip1559.maxFeePerGas
|
|
1723
|
+
: gasLimit * exports.optionalDeps.ethUtil.bufferToInt(gasPrice).toFixed();
|
|
1724
|
+
const unsignedTx = {
|
|
1725
|
+
serializedTxHex: ethTx.serialize().toString('hex'),
|
|
1726
|
+
signableHex: ethTx instanceof tx_1.FeeMarketEIP1559Transaction
|
|
1727
|
+
? ethTx.getMessageToSign(false).toString('hex')
|
|
1728
|
+
: Buffer.from(rlp_1.RLP.encode((0, ethereumjs_util_1.bufArrToArr)(ethTx.getMessageToSign(false)))).toString('hex'),
|
|
1729
|
+
derivationPath: derivationPath,
|
|
1730
|
+
feeInfo: {
|
|
1731
|
+
fee: fee,
|
|
1732
|
+
feeString: fee.toString(),
|
|
1733
|
+
},
|
|
1734
|
+
parsedTx: {
|
|
1735
|
+
spendAmount: txInfo.recipient.amount,
|
|
1736
|
+
outputs: [
|
|
1737
|
+
{
|
|
1738
|
+
coinName: this.getChain(),
|
|
1739
|
+
address: txInfo.recipient.address,
|
|
1740
|
+
valueString: txInfo.recipient.amount,
|
|
1741
|
+
},
|
|
1742
|
+
],
|
|
1743
|
+
},
|
|
1744
|
+
coinSpecific: {
|
|
1745
|
+
commonKeyChain: commonKeyChain,
|
|
1746
|
+
},
|
|
1747
|
+
eip1559: eip1559,
|
|
1748
|
+
replayProtectionOptions: replayProtectionOptions,
|
|
1749
|
+
};
|
|
1750
|
+
return {
|
|
1751
|
+
txRequests: [
|
|
1752
|
+
{
|
|
1753
|
+
walletCoin: this.getChain(),
|
|
1754
|
+
transactions: [
|
|
1755
|
+
{
|
|
1756
|
+
unsignedTx: unsignedTx,
|
|
1757
|
+
nonce: nonce,
|
|
1758
|
+
signatureShares: [],
|
|
1759
|
+
},
|
|
1760
|
+
],
|
|
1761
|
+
},
|
|
1762
|
+
],
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
async buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params) {
|
|
1766
|
+
const txAmount = await this.validateBalanceAndGetTxAmount(baseAddress, gasPrice, gasLimit, params.apiKey);
|
|
1767
|
+
const nonce = await this.getAddressNonce(baseAddress, params.apiKey);
|
|
1768
|
+
const recipients = [
|
|
1769
|
+
{
|
|
1770
|
+
address: params.recoveryDestination,
|
|
1771
|
+
amount: txAmount.toString(10),
|
|
1772
|
+
},
|
|
1773
|
+
];
|
|
1774
|
+
const txInfo = {
|
|
1775
|
+
recipient: recipients[0],
|
|
1776
|
+
expireTime: this.getDefaultExpireTime(),
|
|
1777
|
+
gasLimit: gasLimit.toString(10),
|
|
1778
|
+
};
|
|
1779
|
+
const txParams = {
|
|
1780
|
+
to: params.recoveryDestination,
|
|
1781
|
+
nonce: nonce,
|
|
1782
|
+
value: txAmount,
|
|
1783
|
+
gasPrice: gasPrice,
|
|
1784
|
+
gasLimit: gasLimit,
|
|
1785
|
+
data: Buffer.from('0x'),
|
|
1786
|
+
eip1559: params.eip1559,
|
|
1787
|
+
replayProtectionOptions: params.replayProtectionOptions,
|
|
1788
|
+
};
|
|
1789
|
+
const tx = AbstractEthLikeNewCoins.buildTransaction(txParams);
|
|
1790
|
+
return { txInfo, tx, nonce };
|
|
1791
|
+
}
|
|
1792
|
+
async validateBalanceAndGetTxAmount(baseAddress, gasPrice, gasLimit, apiKey) {
|
|
1793
|
+
const baseAddressBalance = await this.queryAddressBalance(baseAddress, apiKey);
|
|
1794
|
+
let totalGasNeeded = gasPrice.mul(gasLimit);
|
|
1795
|
+
// On L2 chains with L1 data fees, add buffer for L1 fees
|
|
1796
|
+
if (this.staticsCoin?.family !== undefined && lib_1.coinFamiliesWithL1Fees.includes(this.staticsCoin.family)) {
|
|
1797
|
+
totalGasNeeded = totalGasNeeded.add(new exports.optionalDeps.ethUtil.BN(statics_1.ethGasConfigs.l1GasFeeBuffer));
|
|
1798
|
+
}
|
|
1799
|
+
const weiToGwei = new bn_js_1.default(10 ** 9);
|
|
1800
|
+
if (baseAddressBalance.lt(totalGasNeeded)) {
|
|
1801
|
+
throw new Error(`Backup key address ${baseAddress} has balance ${baseAddressBalance.div(weiToGwei).toString()} Gwei.` +
|
|
1802
|
+
`This address must have a balance of at least ${totalGasNeeded.div(weiToGwei).toString()}` +
|
|
1803
|
+
` Gwei to perform recoveries. Try sending some ETH to this address then retry.`);
|
|
1804
|
+
}
|
|
1805
|
+
const txAmount = baseAddressBalance.sub(totalGasNeeded);
|
|
1806
|
+
return txAmount;
|
|
1807
|
+
}
|
|
1808
|
+
/**
|
|
1809
|
+
* Make a query to blockchain explorer for information such as balance, token balance, solidity calls
|
|
1810
|
+
* @param query {Object} key-value pairs of parameters to append after /api
|
|
1811
|
+
* @param apiKey {string} optional API key to use instead of the one from the environment
|
|
1812
|
+
* @returns {Object} response from the blockchain explorer
|
|
1813
|
+
*/
|
|
1814
|
+
async recoveryBlockchainExplorerQuery(query, apiKey) {
|
|
1815
|
+
throw new Error('method not implemented');
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Creates the extra parameters needed to build a hop transaction
|
|
1819
|
+
* @param buildParams The original build parameters
|
|
1820
|
+
* @returns extra parameters object to merge with the original build parameters object and send to the platform
|
|
1821
|
+
*/
|
|
1822
|
+
async createHopTransactionParams(buildParams) {
|
|
1823
|
+
const wallet = buildParams.wallet;
|
|
1824
|
+
const recipients = buildParams.recipients;
|
|
1825
|
+
const walletPassphrase = buildParams.walletPassphrase;
|
|
1826
|
+
const userKeychain = await this.keychains().get({ id: wallet.keyIds()[0] });
|
|
1827
|
+
const userPrv = await wallet.getUserPrv({ keychain: userKeychain, walletPassphrase });
|
|
1828
|
+
const userPrvBuffer = secp256k1_1.bip32.fromBase58(userPrv).privateKey;
|
|
1829
|
+
if (!userPrvBuffer) {
|
|
1830
|
+
throw new Error('invalid userPrv');
|
|
1831
|
+
}
|
|
1832
|
+
if (!recipients || !Array.isArray(recipients)) {
|
|
1833
|
+
throw new Error('expecting array of recipients');
|
|
1834
|
+
}
|
|
1835
|
+
// Right now we only support 1 recipient
|
|
1836
|
+
if (recipients.length !== 1) {
|
|
1837
|
+
throw new Error('must send to exactly 1 recipient');
|
|
1838
|
+
}
|
|
1839
|
+
const recipientAddress = recipients[0].address;
|
|
1840
|
+
const recipientAmount = recipients[0].amount;
|
|
1841
|
+
const feeEstimateParams = {
|
|
1842
|
+
recipient: recipientAddress,
|
|
1843
|
+
amount: recipientAmount,
|
|
1844
|
+
hop: true,
|
|
1845
|
+
};
|
|
1846
|
+
const feeEstimate = await this.feeEstimate(feeEstimateParams);
|
|
1847
|
+
const gasLimit = feeEstimate.gasLimitEstimate;
|
|
1848
|
+
const gasPrice = Math.round(feeEstimate.feeEstimate / gasLimit);
|
|
1849
|
+
const gasPriceMax = gasPrice * 5;
|
|
1850
|
+
// Payment id a random number so its different for every tx
|
|
1851
|
+
const paymentId = Math.floor(Math.random() * 10000000000).toString();
|
|
1852
|
+
const hopDigest = AbstractEthLikeNewCoins.getHopDigest([
|
|
1853
|
+
recipientAddress,
|
|
1854
|
+
recipientAmount,
|
|
1855
|
+
gasPriceMax.toString(),
|
|
1856
|
+
gasLimit.toString(),
|
|
1857
|
+
paymentId,
|
|
1858
|
+
]);
|
|
1859
|
+
const userReqSig = exports.optionalDeps.ethUtil.addHexPrefix(Buffer.from(secp256k1_2.default.ecdsaSign(hopDigest, userPrvBuffer).signature).toString('hex'));
|
|
1860
|
+
return {
|
|
1861
|
+
hopParams: {
|
|
1862
|
+
gasPriceMax,
|
|
1863
|
+
userReqSig,
|
|
1864
|
+
paymentId,
|
|
1865
|
+
gasLimit,
|
|
1866
|
+
},
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
/**
|
|
1870
|
+
* Validates that the hop prebuild from the HSM is valid and correct
|
|
1871
|
+
* @param {IWallet} wallet - The wallet that the prebuild is for
|
|
1872
|
+
* @param {HopPrebuild} hopPrebuild - The prebuild to validate
|
|
1873
|
+
* @param {Object} originalParams - The original parameters passed to prebuildTransaction
|
|
1874
|
+
* @param {Recipient[]} originalParams.recipients - The original recipients array
|
|
1875
|
+
* @returns {void}
|
|
1876
|
+
* @throws Error if The prebuild is invalid
|
|
1877
|
+
*/
|
|
1878
|
+
async validateHopPrebuild(wallet, hopPrebuild, originalParams) {
|
|
1879
|
+
const { tx, id, signature } = hopPrebuild;
|
|
1880
|
+
// first, validate the HSM signature
|
|
1881
|
+
const serverXpub = sdk_core_1.common.Environments[this.bitgo.getEnv()].hsmXpub;
|
|
1882
|
+
const serverPubkeyBuffer = secp256k1_1.bip32.fromBase58(serverXpub).publicKey;
|
|
1883
|
+
const signatureBuffer = Buffer.from(exports.optionalDeps.ethUtil.stripHexPrefix(signature), 'hex');
|
|
1884
|
+
const messageBuffer = Buffer.from(exports.optionalDeps.ethUtil.padToEven(exports.optionalDeps.ethUtil.stripHexPrefix(id)), 'hex');
|
|
1885
|
+
const sig = new Uint8Array(signatureBuffer.slice(1));
|
|
1886
|
+
const isValidSignature = secp256k1_2.default.ecdsaVerify(sig, messageBuffer, serverPubkeyBuffer);
|
|
1887
|
+
if (!isValidSignature) {
|
|
1888
|
+
throw new Error(`Hop txid signature invalid - pub: ${serverXpub}, msg: ${messageBuffer?.toString()}, sig: ${signatureBuffer?.toString()}`);
|
|
1889
|
+
}
|
|
1890
|
+
const builtHopTx = exports.optionalDeps.EthTx.TransactionFactory.fromSerializedData(exports.optionalDeps.ethUtil.toBuffer(tx));
|
|
1891
|
+
// If original params are given, we can check them against the transaction prebuild params
|
|
1892
|
+
if (!lodash_1.default.isNil(originalParams)) {
|
|
1893
|
+
const { recipients } = originalParams;
|
|
1894
|
+
// Then validate that the tx params actually equal the requested params
|
|
1895
|
+
const originalAmount = new bignumber_js_1.BigNumber(recipients[0].amount);
|
|
1896
|
+
const originalDestination = recipients[0].address;
|
|
1897
|
+
const hopAmount = new bignumber_js_1.BigNumber(exports.optionalDeps.ethUtil.bufferToHex(builtHopTx.value));
|
|
1898
|
+
if (!builtHopTx.to) {
|
|
1899
|
+
throw new Error(`Transaction does not have a destination address`);
|
|
1900
|
+
}
|
|
1901
|
+
const hopDestination = builtHopTx.to.toString();
|
|
1902
|
+
if (!hopAmount.eq(originalAmount)) {
|
|
1903
|
+
throw new Error(`Hop amount: ${hopAmount} does not equal original amount: ${originalAmount}`);
|
|
1904
|
+
}
|
|
1905
|
+
if (hopDestination.toLowerCase() !== originalDestination.toLowerCase()) {
|
|
1906
|
+
throw new Error(`Hop destination: ${hopDestination} does not equal original recipient: ${hopDestination}`);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
if (!builtHopTx.verifySignature()) {
|
|
1910
|
+
// We dont want to continue at all in this case, at risk of ETH being stuck on the hop address
|
|
1911
|
+
throw new Error(`Invalid hop transaction signature, txid: ${id}`);
|
|
1912
|
+
}
|
|
1913
|
+
if (exports.optionalDeps.ethUtil.addHexPrefix(builtHopTx.hash().toString('hex')) !== id) {
|
|
1914
|
+
throw new Error(`Signed hop txid does not equal actual txid`);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
/**
|
|
1918
|
+
* Gets the hop digest for the user to sign. This is validated in the HSM to prove that the user requested this tx
|
|
1919
|
+
* @param {string[]} paramsArr - The parameters to hash together for the digest
|
|
1920
|
+
* @returns {Buffer}
|
|
1921
|
+
*/
|
|
1922
|
+
static getHopDigest(paramsArr) {
|
|
1923
|
+
const hash = (0, keccak_1.default)('keccak256');
|
|
1924
|
+
hash.update([AbstractEthLikeNewCoins.hopTransactionSalt, ...paramsArr].join('$'));
|
|
1925
|
+
return hash.digest();
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* Modify prebuild before sending it to the server. Add things like hop transaction params
|
|
1929
|
+
* @param {BuildOptions} buildParams - The whitelisted parameters for this prebuild
|
|
1930
|
+
* @param {boolean} buildParams.hop - True if this should prebuild a hop tx, else false
|
|
1931
|
+
* @param {Recipient[]} buildParams.recipients - The recipients array of this transaction
|
|
1932
|
+
* @param {Wallet} buildParams.wallet - The wallet sending this tx
|
|
1933
|
+
* @param {string} buildParams.walletPassphrase - the passphrase for this wallet
|
|
1934
|
+
* @returns {Promise<BuildOptions>}
|
|
1935
|
+
*/
|
|
1936
|
+
async getExtraPrebuildParams(buildParams) {
|
|
1937
|
+
if (!lodash_1.default.isUndefined(buildParams.hop) &&
|
|
1938
|
+
buildParams.hop &&
|
|
1939
|
+
!lodash_1.default.isUndefined(buildParams.wallet) &&
|
|
1940
|
+
!lodash_1.default.isUndefined(buildParams.recipients) &&
|
|
1941
|
+
!lodash_1.default.isUndefined(buildParams.walletPassphrase)) {
|
|
1942
|
+
if (this instanceof ethLikeToken_1.EthLikeToken) {
|
|
1943
|
+
throw new Error(`Hop transactions are not enabled for ERC-20 tokens, nor are they necessary. Please remove the 'hop' parameter and try again.`);
|
|
1944
|
+
}
|
|
1945
|
+
return (await this.createHopTransactionParams({
|
|
1946
|
+
wallet: buildParams.wallet,
|
|
1947
|
+
recipients: buildParams.recipients,
|
|
1948
|
+
walletPassphrase: buildParams.walletPassphrase,
|
|
1949
|
+
}));
|
|
1950
|
+
}
|
|
1951
|
+
return {};
|
|
1952
|
+
}
|
|
1953
|
+
/**
|
|
1954
|
+
* Modify prebuild after receiving it from the server. Add things like nlocktime
|
|
1955
|
+
* @param {TransactionPrebuild} params - The prebuild to modify
|
|
1956
|
+
* @returns {TransactionPrebuild} The modified prebuild
|
|
1957
|
+
*/
|
|
1958
|
+
async postProcessPrebuild(params) {
|
|
1959
|
+
if (!lodash_1.default.isUndefined(params.hopTransaction) && !lodash_1.default.isUndefined(params.wallet) && !lodash_1.default.isUndefined(params.buildParams)) {
|
|
1960
|
+
await this.validateHopPrebuild(params.wallet, params.hopTransaction, params.buildParams);
|
|
1961
|
+
}
|
|
1962
|
+
return params;
|
|
1963
|
+
}
|
|
1964
|
+
/**
|
|
1965
|
+
* Coin-specific things done before signing a transaction, i.e. verification
|
|
1966
|
+
* @param {PresignTransactionOptions} params
|
|
1967
|
+
* @returns {Promise<PresignTransactionOptions>}
|
|
1968
|
+
*/
|
|
1969
|
+
async presignTransaction(params) {
|
|
1970
|
+
if (!lodash_1.default.isUndefined(params.hopTransaction) && !lodash_1.default.isUndefined(params.wallet) && !lodash_1.default.isUndefined(params.buildParams)) {
|
|
1971
|
+
await this.validateHopPrebuild(params.wallet, params.hopTransaction);
|
|
1972
|
+
}
|
|
1973
|
+
return params;
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Fetch fee estimate information from the server
|
|
1977
|
+
* @param {Object} params - The params passed into the function
|
|
1978
|
+
* @param {boolean} [params.hop] - True if we should estimate fee for a hop transaction
|
|
1979
|
+
* @param {string} [params.recipient] - The recipient of the transaction to estimate a send to
|
|
1980
|
+
* @param {string} [params.data] - The ETH tx data to estimate a send for
|
|
1981
|
+
* @returns {Object} The fee info returned from the server
|
|
1982
|
+
*/
|
|
1983
|
+
async feeEstimate(params) {
|
|
1984
|
+
const query = {};
|
|
1985
|
+
if (params && params.hop) {
|
|
1986
|
+
query.hop = params.hop;
|
|
1987
|
+
}
|
|
1988
|
+
if (params && params.recipient) {
|
|
1989
|
+
query.recipient = params.recipient;
|
|
1990
|
+
}
|
|
1991
|
+
if (params && params.data) {
|
|
1992
|
+
query.data = params.data;
|
|
1993
|
+
}
|
|
1994
|
+
if (params && params.amount) {
|
|
1995
|
+
query.amount = params.amount;
|
|
1996
|
+
}
|
|
1997
|
+
return await this.bitgo.get(this.url('/tx/fee')).query(query).result();
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
2000
|
+
* Generate secp256k1 key pair
|
|
2001
|
+
*
|
|
2002
|
+
* @param {Buffer} seed
|
|
2003
|
+
* @returns {KeyPair} object with generated pub and prv
|
|
2004
|
+
*/
|
|
2005
|
+
generateKeyPair(seed) {
|
|
2006
|
+
if (!seed) {
|
|
2007
|
+
// An extended private key has both a normal 256 bit private key and a 256
|
|
2008
|
+
// bit chain code, both of which must be random. 512 bits is therefore the
|
|
2009
|
+
// maximum entropy and gives us maximum security against cracking.
|
|
2010
|
+
seed = (0, crypto_1.randomBytes)(512 / 8);
|
|
2011
|
+
}
|
|
2012
|
+
const extendedKey = secp256k1_1.bip32.fromSeed(seed);
|
|
2013
|
+
const xpub = extendedKey.neutered().toBase58();
|
|
2014
|
+
return {
|
|
2015
|
+
pub: xpub,
|
|
2016
|
+
prv: extendedKey.toBase58(),
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
async parseTransaction(params) {
|
|
2020
|
+
return {};
|
|
2021
|
+
}
|
|
2022
|
+
/**
|
|
2023
|
+
* Get forwarder factory and implementation addresses for deposit address verification.
|
|
2024
|
+
* Forwarders are smart contracts that forward funds to the base wallet address.
|
|
2025
|
+
*
|
|
2026
|
+
* @param {number | undefined} forwarderVersion - The wallet version
|
|
2027
|
+
* @returns {object} Factory and implementation addresses for forwarders
|
|
2028
|
+
*/
|
|
2029
|
+
getForwarderFactoryAddressesAndForwarderImplementationAddress(forwarderVersion) {
|
|
2030
|
+
const ethNetwork = this.getNetwork();
|
|
2031
|
+
switch (forwarderVersion) {
|
|
2032
|
+
case 1:
|
|
2033
|
+
if (!ethNetwork?.forwarderFactoryAddress || !ethNetwork?.forwarderImplementationAddress) {
|
|
2034
|
+
throw new Error('Forwarder factory addresses not configured for this network');
|
|
2035
|
+
}
|
|
2036
|
+
return {
|
|
2037
|
+
forwarderFactoryAddress: ethNetwork.forwarderFactoryAddress,
|
|
2038
|
+
forwarderImplementationAddress: ethNetwork.forwarderImplementationAddress,
|
|
2039
|
+
};
|
|
2040
|
+
case 2:
|
|
2041
|
+
if (!ethNetwork?.walletV2ForwarderFactoryAddress || !ethNetwork?.walletV2ForwarderImplementationAddress) {
|
|
2042
|
+
throw new Error('Wallet v2 factory addresses not configured for this network');
|
|
2043
|
+
}
|
|
2044
|
+
return {
|
|
2045
|
+
forwarderFactoryAddress: ethNetwork.walletV2ForwarderFactoryAddress,
|
|
2046
|
+
forwarderImplementationAddress: ethNetwork.walletV2ForwarderImplementationAddress,
|
|
2047
|
+
};
|
|
2048
|
+
case 4:
|
|
2049
|
+
case 5:
|
|
2050
|
+
if (!ethNetwork?.walletV4ForwarderFactoryAddress || !ethNetwork?.walletV4ForwarderImplementationAddress) {
|
|
2051
|
+
throw new Error(`Forwarder v${forwarderVersion} factory addresses not configured for this network`);
|
|
2052
|
+
}
|
|
2053
|
+
return {
|
|
2054
|
+
forwarderFactoryAddress: ethNetwork.walletV4ForwarderFactoryAddress,
|
|
2055
|
+
forwarderImplementationAddress: ethNetwork.walletV4ForwarderImplementationAddress,
|
|
2056
|
+
};
|
|
2057
|
+
default:
|
|
2058
|
+
throw new Error(`Forwarder version ${forwarderVersion} not supported`);
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
/**
|
|
2062
|
+
* Get wallet base address factory and implementation addresses.
|
|
2063
|
+
* This is used for base address verification for V1, V2, V4, and V5 wallets.
|
|
2064
|
+
* The base address is the main wallet contract deployed via CREATE2.
|
|
2065
|
+
*
|
|
2066
|
+
* @param {number} walletVersion - The wallet version (1, 2, 4, or 5)
|
|
2067
|
+
* @returns {object} Factory and implementation addresses for the wallet base address
|
|
2068
|
+
* @throws {Error} if wallet version addresses are not configured
|
|
2069
|
+
*/
|
|
2070
|
+
getWalletAddressFactoryAddressesAndImplementationAddress(walletVersion) {
|
|
2071
|
+
const ethNetwork = this.getNetwork();
|
|
2072
|
+
switch (walletVersion) {
|
|
2073
|
+
case 1:
|
|
2074
|
+
if (!ethNetwork?.walletFactoryAddress || !ethNetwork?.walletImplementationAddress) {
|
|
2075
|
+
throw new Error('Wallet v1 factory addresses not configured for this network');
|
|
2076
|
+
}
|
|
2077
|
+
return {
|
|
2078
|
+
walletFactoryAddress: ethNetwork.walletFactoryAddress,
|
|
2079
|
+
walletImplementationAddress: ethNetwork.walletImplementationAddress,
|
|
2080
|
+
};
|
|
2081
|
+
case 2:
|
|
2082
|
+
if (!ethNetwork?.walletV2FactoryAddress || !ethNetwork?.walletV2ImplementationAddress) {
|
|
2083
|
+
throw new Error('Wallet v2 factory addresses not configured for this network');
|
|
2084
|
+
}
|
|
2085
|
+
return {
|
|
2086
|
+
walletFactoryAddress: ethNetwork.walletV2FactoryAddress,
|
|
2087
|
+
walletImplementationAddress: ethNetwork.walletV2ImplementationAddress,
|
|
2088
|
+
};
|
|
2089
|
+
case 4:
|
|
2090
|
+
case 5:
|
|
2091
|
+
if (!ethNetwork?.walletV4ForwarderFactoryAddress || !ethNetwork?.walletV4ForwarderImplementationAddress) {
|
|
2092
|
+
throw new Error(`Wallet v${walletVersion} factory addresses not configured for this network`);
|
|
2093
|
+
}
|
|
2094
|
+
return {
|
|
2095
|
+
walletFactoryAddress: ethNetwork.walletV4ForwarderFactoryAddress,
|
|
2096
|
+
walletImplementationAddress: ethNetwork.walletV4ForwarderImplementationAddress,
|
|
2097
|
+
};
|
|
2098
|
+
default:
|
|
2099
|
+
throw new Error(`Wallet version ${walletVersion} not supported`);
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
/**
|
|
2103
|
+
* Helper method to create a salt buffer from hex string.
|
|
2104
|
+
* Converts a hex salt string to a 32-byte buffer.
|
|
2105
|
+
*
|
|
2106
|
+
* @param {string} salt - The hex salt string
|
|
2107
|
+
* @returns {Buffer} 32-byte salt buffer
|
|
2108
|
+
*/
|
|
2109
|
+
createSaltBuffer(salt) {
|
|
2110
|
+
const ethUtil = exports.optionalDeps.ethUtil;
|
|
2111
|
+
return ethUtil.setLengthLeft(Buffer.from(ethUtil.padToEven(ethUtil.stripHexPrefix(salt || '')), 'hex'), 32);
|
|
2112
|
+
}
|
|
2113
|
+
/**
|
|
2114
|
+
* Verify BIP32 wallet base address (V1, V2, V4).
|
|
2115
|
+
* These wallets use a wallet factory to deploy base addresses with CREATE2.
|
|
2116
|
+
* The address is derived from the keychains' ethAddresses and a salt.
|
|
2117
|
+
*
|
|
2118
|
+
* @param {VerifyBip32BaseAddressOptions} params - Verification parameters
|
|
2119
|
+
* @returns {object} Expected and actual addresses for comparison
|
|
2120
|
+
*/
|
|
2121
|
+
verifyCreate2BaseAddress(params) {
|
|
2122
|
+
const { address, coinSpecific, keychains, walletVersion } = params;
|
|
2123
|
+
if (!coinSpecific.salt) {
|
|
2124
|
+
throw new Error(`missing salt for v${walletVersion} base address verification`);
|
|
2125
|
+
}
|
|
2126
|
+
// Get wallet factory and implementation addresses for the wallet version
|
|
2127
|
+
const { walletFactoryAddress, walletImplementationAddress } = this.getWalletAddressFactoryAddressesAndImplementationAddress(walletVersion);
|
|
2128
|
+
const initcode = (0, lib_1.getProxyInitcode)(walletImplementationAddress);
|
|
2129
|
+
// Convert the wallet salt to a buffer, pad to 32 bytes
|
|
2130
|
+
const saltBuffer = this.createSaltBuffer(coinSpecific.salt);
|
|
2131
|
+
// Reconstruct calculationSalt using keychains' ethAddresses and wallet salt
|
|
2132
|
+
const ethAddresses = keychains.map((kc) => {
|
|
2133
|
+
if (!kc.ethAddress) {
|
|
2134
|
+
throw new Error(`keychain missing ethAddress for v${walletVersion} base address verification`);
|
|
2135
|
+
}
|
|
2136
|
+
return kc.ethAddress;
|
|
2137
|
+
});
|
|
2138
|
+
const calculationSalt = exports.optionalDeps.ethUtil.bufferToHex(exports.optionalDeps.ethAbi.soliditySHA3(['address[]', 'bytes32'], [ethAddresses, saltBuffer]));
|
|
2139
|
+
const expectedAddress = (0, lib_1.calculateForwarderV1Address)(walletFactoryAddress, calculationSalt, initcode);
|
|
2140
|
+
if (expectedAddress !== address) {
|
|
2141
|
+
throw new sdk_core_1.UnexpectedAddressError(`address validation failure: expected ${expectedAddress} but got ${address}`);
|
|
2142
|
+
}
|
|
2143
|
+
return true;
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* Verify forwarder receive address (deposit address).
|
|
2147
|
+
* Forwarder addresses are derived using CREATE2 from the base address and salt.
|
|
2148
|
+
*
|
|
2149
|
+
* @param {VerifyEthAddressOptions} params - Verification parameters
|
|
2150
|
+
* @param {number} forwarderVersion - The forwarder version
|
|
2151
|
+
* @returns {object} Expected and actual addresses for comparison
|
|
2152
|
+
*/
|
|
2153
|
+
verifyForwarderAddress(params, forwarderVersion) {
|
|
2154
|
+
const { address, coinSpecific, baseAddress } = params;
|
|
2155
|
+
const { forwarderFactoryAddress, forwarderImplementationAddress } = this.getForwarderFactoryAddressesAndForwarderImplementationAddress(forwarderVersion);
|
|
2156
|
+
const initcode = (0, lib_1.getProxyInitcode)(forwarderImplementationAddress);
|
|
2157
|
+
const saltBuffer = this.createSaltBuffer(coinSpecific.salt || '');
|
|
2158
|
+
const { createForwarderParams, createForwarderTypes } = forwarderVersion === 4
|
|
2159
|
+
? (0, lib_1.getCreateForwarderParamsAndTypes)(baseAddress, saltBuffer, coinSpecific.feeAddress)
|
|
2160
|
+
: (0, lib_1.getCreateForwarderParamsAndTypes)(baseAddress, saltBuffer);
|
|
2161
|
+
const calculationSalt = exports.optionalDeps.ethUtil.bufferToHex(exports.optionalDeps.ethAbi.soliditySHA3(createForwarderTypes, createForwarderParams));
|
|
2162
|
+
const expectedAddress = (0, lib_1.calculateForwarderV1Address)(forwarderFactoryAddress, calculationSalt, initcode);
|
|
2163
|
+
if (expectedAddress !== address) {
|
|
2164
|
+
throw new sdk_core_1.UnexpectedAddressError(`address validation failure: expected ${expectedAddress} but got ${address}`);
|
|
2165
|
+
}
|
|
2166
|
+
return true;
|
|
2167
|
+
}
|
|
2168
|
+
/**
|
|
2169
|
+
* Make sure an address is a wallet address and throw an error if it's not.
|
|
2170
|
+
* @param {Object} params
|
|
2171
|
+
* @param {string} params.address - The derived address string on the network
|
|
2172
|
+
* @param {Object} params.coinSpecific - Coin-specific details for the address such as a forwarderVersion
|
|
2173
|
+
* @param {string} params.baseAddress - The base address of the wallet on the network
|
|
2174
|
+
* @throws {InvalidAddressError}
|
|
2175
|
+
* @throws {InvalidAddressVerificationObjectPropertyError}
|
|
2176
|
+
* @throws {UnexpectedAddressError}
|
|
2177
|
+
* @returns {boolean} True iff address is a wallet address
|
|
2178
|
+
*/
|
|
2179
|
+
async isWalletAddress(params) {
|
|
2180
|
+
const { address, impliedForwarderVersion, coinSpecific, baseAddress } = params;
|
|
2181
|
+
const forwarderVersion = impliedForwarderVersion ?? coinSpecific?.forwarderVersion;
|
|
2182
|
+
// Validate address format
|
|
2183
|
+
if (address && !this.isValidAddress(address)) {
|
|
2184
|
+
throw new sdk_core_1.InvalidAddressError(`invalid address: ${address}`);
|
|
2185
|
+
}
|
|
2186
|
+
// Forwarder version 0 addresses cannot be verified because we do not store the nonce value required for address derivation.
|
|
2187
|
+
if (forwarderVersion === 0) {
|
|
2188
|
+
return true;
|
|
2189
|
+
}
|
|
2190
|
+
// Determine if we are verifying a base address
|
|
2191
|
+
const isVerifyingBaseAddress = baseAddress && address === baseAddress;
|
|
2192
|
+
// TSS/MPC wallet address verification (V3, V5, V6)
|
|
2193
|
+
// V5 base addresses use TSS, but V5 forwarders use the regular forwarder verification
|
|
2194
|
+
const isTssWalletVersion = params.walletVersion === 3 || params.walletVersion === 5 || params.walletVersion === 6;
|
|
2195
|
+
const shouldUseTssVerification = (0, sdk_core_1.isTssVerifyAddressOptions)(params) && isTssWalletVersion && (params.walletVersion !== 5 || isVerifyingBaseAddress);
|
|
2196
|
+
if (shouldUseTssVerification) {
|
|
2197
|
+
if (isVerifyingBaseAddress) {
|
|
2198
|
+
const index = typeof params.index === 'string' ? parseInt(params.index, 10) : params.index;
|
|
2199
|
+
if (index !== 0) {
|
|
2200
|
+
throw new Error(`Base address verification requires index 0, but got index ${params.index}. ` +
|
|
2201
|
+
`The base address is always derived at index 0.`);
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
return (0, sdk_core_1.verifyMPCWalletAddress)({ ...params, keyCurve: 'secp256k1' }, this.isValidAddress, (pubKey) => {
|
|
2205
|
+
return new lib_1.KeyPair({ pub: pubKey }).getAddress();
|
|
2206
|
+
});
|
|
2207
|
+
}
|
|
2208
|
+
// From here on, we need baseAddress and coinSpecific for non-TSS verifications
|
|
2209
|
+
if (lodash_1.default.isUndefined(baseAddress) || !this.isValidAddress(baseAddress)) {
|
|
2210
|
+
throw new sdk_core_1.InvalidAddressError('invalid base address');
|
|
2211
|
+
}
|
|
2212
|
+
if (!lodash_1.default.isObject(coinSpecific)) {
|
|
2213
|
+
throw new sdk_core_1.InvalidAddressVerificationObjectPropertyError('address validation failure: coinSpecific field must be an object');
|
|
2214
|
+
}
|
|
2215
|
+
// BIP32 wallet base address verification (V1, V2, V4)
|
|
2216
|
+
if (isVerifyingBaseAddress && isVerifyContractBaseAddressOptions(params)) {
|
|
2217
|
+
return this.verifyCreate2BaseAddress(params);
|
|
2218
|
+
}
|
|
2219
|
+
// Forwarder receive address verification (deposit addresses)
|
|
2220
|
+
if (!isVerifyingBaseAddress) {
|
|
2221
|
+
return this.verifyForwarderAddress(params, forwarderVersion);
|
|
2222
|
+
}
|
|
2223
|
+
// If we reach here, it's a base address verification for an unsupported wallet version
|
|
2224
|
+
throw new Error(`Base address verification not supported for wallet version ${params.walletVersion}`);
|
|
2225
|
+
}
|
|
2226
|
+
/**
|
|
2227
|
+
* Locally derive an ETH wallet receive address from a derivation path, using the wallet's
|
|
2228
|
+
* commonKeychain only (no private keys, no network access). The inverse of
|
|
2229
|
+
* {@link isWalletAddress}: it *produces* the address via the same secp256k1 MPC derivation
|
|
2230
|
+
* that isWalletAddress checks against (`deriveMPCWalletAddress` + `KeyPair.getAddress()`),
|
|
2231
|
+
* so derive and verify can never diverge.
|
|
2232
|
+
*
|
|
2233
|
+
* Supports MPC/TSS wallets only (wallet versions 3, 5, 6). Legacy BIP32 forwarder wallets
|
|
2234
|
+
* (versions 1, 2, 4) derive per-index forwarder contract addresses and are handled separately.
|
|
2235
|
+
* @param params keychains (commonKeychain), derivation index, walletVersion, and optional SMC seed
|
|
2236
|
+
* @returns the derived address, the index used, and the HD derivation path
|
|
2237
|
+
*/
|
|
2238
|
+
async deriveAddress(params) {
|
|
2239
|
+
const { walletVersion, index } = params;
|
|
2240
|
+
const forwarderVersion = params.coinSpecific?.forwarderVersion;
|
|
2241
|
+
// Mirrors isWalletAddress's branching:
|
|
2242
|
+
// - MPC/TSS receive addresses: v3 & v6 (every index), and the v5 *base* address (index 0).
|
|
2243
|
+
// - Forwarder (CREATE2) receive addresses: legacy v1/v2/v4 wallets, and v5 forwarders (index > 0).
|
|
2244
|
+
const useMpc = walletVersion === 3 || walletVersion === 6 || (walletVersion === 5 && index === 0);
|
|
2245
|
+
if (useMpc) {
|
|
2246
|
+
const { address, derivationPath } = await (0, sdk_core_1.deriveMPCWalletAddress)({
|
|
2247
|
+
// extractCommonKeychain validates the commonKeychain is present at runtime
|
|
2248
|
+
keychains: (params.keychains ?? []),
|
|
2249
|
+
index,
|
|
2250
|
+
derivedFromParentWithSeed: params.derivedFromParentWithSeed,
|
|
2251
|
+
multisigTypeVersion: params.multisigTypeVersion,
|
|
2252
|
+
keyCurve: 'secp256k1',
|
|
2253
|
+
}, (pubKey) => new lib_1.KeyPair({ pub: pubKey }).getAddress());
|
|
2254
|
+
return { address, index, derivationPath };
|
|
2255
|
+
}
|
|
2256
|
+
// Forwarder (CREATE2) derivation for legacy multisig / v5 forwarder receive addresses.
|
|
2257
|
+
if (forwarderVersion === 0) {
|
|
2258
|
+
throw new Error('Forwarder version 0 addresses cannot be derived: the nonce required for address derivation is not stored.');
|
|
2259
|
+
}
|
|
2260
|
+
if (forwarderVersion === 1 || forwarderVersion === 2 || forwarderVersion === 4) {
|
|
2261
|
+
const { baseAddress, coinSpecific } = params;
|
|
2262
|
+
if (!baseAddress) {
|
|
2263
|
+
throw new Error('baseAddress is required to derive a forwarder address');
|
|
2264
|
+
}
|
|
2265
|
+
const feeAddress = coinSpecific?.feeAddress ?? '';
|
|
2266
|
+
if (forwarderVersion === 4 && !feeAddress) {
|
|
2267
|
+
throw new Error('coinSpecific.feeAddress is required to derive a v4 forwarder address');
|
|
2268
|
+
}
|
|
2269
|
+
const { forwarderFactoryAddress, forwarderImplementationAddress } = this.getForwarderFactoryAddressesAndForwarderImplementationAddress(forwarderVersion);
|
|
2270
|
+
// For BitGo forwarders the per-address salt is the derivation index, so the forwarder
|
|
2271
|
+
// address is reproducible from (baseAddress, index) plus the network factory/implementation.
|
|
2272
|
+
const address = this.generateForwarderAddress(baseAddress, feeAddress, forwarderFactoryAddress, forwarderImplementationAddress, index);
|
|
2273
|
+
return { address, index, coinSpecific: { forwarderVersion } };
|
|
2274
|
+
}
|
|
2275
|
+
throw new Error(`deriveAddress: could not determine an ETH derivation strategy. Provide an MPC wallet ` +
|
|
2276
|
+
`(walletVersion 3/5/6 with commonKeychain), or a legacy forwarder (coinSpecific.forwarderVersion ` +
|
|
2277
|
+
`1/2/4 with baseAddress). Got walletVersion=${walletVersion}, forwarderVersion=${forwarderVersion}.`);
|
|
2278
|
+
}
|
|
2279
|
+
/**
|
|
2280
|
+
*
|
|
2281
|
+
* @param {TransactionPrebuild} txPrebuild
|
|
2282
|
+
* @returns {boolean}
|
|
2283
|
+
*/
|
|
2284
|
+
verifyCoin(txPrebuild) {
|
|
2285
|
+
const nativeCoin = this.getChain().split(':')[0];
|
|
2286
|
+
return txPrebuild.coin === nativeCoin;
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Generate transaction explanation for error reporting
|
|
2290
|
+
* @param txPrebuild - Transaction prebuild containing txHex and fee info
|
|
2291
|
+
* @returns Stringified JSON explanation
|
|
2292
|
+
*/
|
|
2293
|
+
async getTxExplanation(txPrebuild) {
|
|
2294
|
+
if (!txPrebuild?.txHex || !txPrebuild?.gasPrice) {
|
|
2295
|
+
return undefined;
|
|
2296
|
+
}
|
|
2297
|
+
try {
|
|
2298
|
+
const explanation = await this.explainTransaction({
|
|
2299
|
+
txHex: txPrebuild.txHex,
|
|
2300
|
+
feeInfo: {
|
|
2301
|
+
fee: txPrebuild.gasPrice.toString(),
|
|
2302
|
+
},
|
|
2303
|
+
});
|
|
2304
|
+
return JSON.stringify(explanation, null, 2);
|
|
2305
|
+
}
|
|
2306
|
+
catch (e) {
|
|
2307
|
+
const errorDetails = {
|
|
2308
|
+
error: 'Failed to parse transaction explanation',
|
|
2309
|
+
txHex: txPrebuild.txHex,
|
|
2310
|
+
details: e instanceof Error ? e.message : String(e),
|
|
2311
|
+
};
|
|
2312
|
+
return JSON.stringify(errorDetails, null, 2);
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
/**
|
|
2316
|
+
* Verify if a tss transaction is valid
|
|
2317
|
+
*
|
|
2318
|
+
/** @inheritdoc CoinWithSignableConsistency */
|
|
2319
|
+
assertSignableConsistency(serializedTxHex, signableHex) {
|
|
2320
|
+
const common = AbstractEthLikeNewCoins.getCustomChainCommon(this.getChainId());
|
|
2321
|
+
// getCustomChainCommon defaults to the london hardfork, which is only valid for chains
|
|
2322
|
+
// that activated EIP-1559. Chains without it (e.g. BSC, XDC) sign under petersburg.
|
|
2323
|
+
if (!this.staticsCoin?.features.includes(statics_1.CoinFeature.EIP1559)) {
|
|
2324
|
+
common.setHardfork(exports.optionalDeps.EthCommon.Hardfork.Petersburg);
|
|
2325
|
+
}
|
|
2326
|
+
const ethTx = tx_1.TransactionFactory.fromSerializedData((0, ethereumjs_util_1.toBuffer)((0, ethereumjs_util_1.addHexPrefix)(serializedTxHex)), { common });
|
|
2327
|
+
const derivedSignableHex = ethTx instanceof tx_1.FeeMarketEIP1559Transaction
|
|
2328
|
+
? ethTx.getMessageToSign(false).toString('hex')
|
|
2329
|
+
: Buffer.from(rlp_1.RLP.encode((0, ethereumjs_util_1.bufArrToArr)(ethTx.getMessageToSign(false)))).toString('hex');
|
|
2330
|
+
if (derivedSignableHex !== signableHex) {
|
|
2331
|
+
throw new sdk_core_1.InvalidTransactionError('signableHex is inconsistent with serializedTxHex: possible server tampering');
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
/**
|
|
2335
|
+
* Decode-and-assert for the ERC-7984 shield approve leg (defense-in-depth).
|
|
2336
|
+
*
|
|
2337
|
+
* The approve is signed through the chain coin, whose generic checks never parse calldata.
|
|
2338
|
+
* WP builds the bytes server-side; re-parse them here so the client signs only
|
|
2339
|
+
* `approve(wrapper, amount)` on the pair's underlying ERC-20 with the amount from the intent.
|
|
2340
|
+
* Fails closed on any deviation.
|
|
2341
|
+
*
|
|
2342
|
+
* Scope: this runs when the declared type is `wrapApprove`, and {@link assertWrapperApproveIsDeclared} also
|
|
2343
|
+
* forces any approve whose spender is a registered wrapper through it. Deliberately NOT covered: an approve
|
|
2344
|
+
* of a registered underlying to a third-party spender under another declared type (tokenApproval, defiApprove,
|
|
2345
|
+
* customTx) is indistinguishable from a legitimate approval, so a server that relabels the intent of such a
|
|
2346
|
+
* tx still skips this check on signing paths that carry no caller txParams (pendingApproval re-sign).
|
|
2347
|
+
*/
|
|
2348
|
+
async verifyWrapApproveTransaction(params, throwRecipientMismatch) {
|
|
2349
|
+
const { txParams, txPrebuild } = params;
|
|
2350
|
+
if (!txPrebuild?.txHex) {
|
|
2351
|
+
throw new Error('missing txHex in txPrebuild');
|
|
2352
|
+
}
|
|
2353
|
+
const wrapParams = txParams.wrapParams;
|
|
2354
|
+
if (!wrapParams?.tokenName || !wrapParams.amount) {
|
|
2355
|
+
return throwRecipientMismatch('wrapApprove transaction requires wrapParams with tokenName and amount', []);
|
|
2356
|
+
}
|
|
2357
|
+
let wrapper;
|
|
2358
|
+
try {
|
|
2359
|
+
const staticsCoin = statics_1.coins.get(wrapParams.tokenName);
|
|
2360
|
+
if (!(staticsCoin instanceof statics_1.Erc7984Coin) || !staticsCoin.underlyingErc20Address) {
|
|
2361
|
+
throw new Error('not an ERC-7984 pair');
|
|
2362
|
+
}
|
|
2363
|
+
wrapper = staticsCoin;
|
|
2364
|
+
}
|
|
2365
|
+
catch {
|
|
2366
|
+
return throwRecipientMismatch(`wrapApprove: '${wrapParams.tokenName}' is not a registered ERC-7984 shield pair`, []);
|
|
2367
|
+
}
|
|
2368
|
+
const txBuilder = this.getTransactionBuilder();
|
|
2369
|
+
txBuilder.from(txPrebuild.txHex);
|
|
2370
|
+
const txJson = (await txBuilder.build()).toJson();
|
|
2371
|
+
const mismatch = (message, address = txJson.to, amount = txJson.value) => throwRecipientMismatch(message, [{ address: address || '', amount: amount || '' }]);
|
|
2372
|
+
if (!txJson.to || txJson.to.toLowerCase() !== wrapper.underlyingErc20Address) {
|
|
2373
|
+
await mismatch('wrapApprove transaction must target the underlying ERC-20 of the wrapper');
|
|
2374
|
+
}
|
|
2375
|
+
if (txJson.value !== undefined && String(txJson.value) !== '0') {
|
|
2376
|
+
await mismatch('wrapApprove transaction must not carry native value');
|
|
2377
|
+
}
|
|
2378
|
+
let decoded;
|
|
2379
|
+
try {
|
|
2380
|
+
decoded = (0, lib_1.decodeApproveCalldata)(txJson.data || '');
|
|
2381
|
+
}
|
|
2382
|
+
catch (e) {
|
|
2383
|
+
return mismatch(`wrapApprove transaction must use ERC-20 approve(address,uint256) calldata — ${e.message}`);
|
|
2384
|
+
}
|
|
2385
|
+
if (decoded.spender.toLowerCase() !== wrapper.contractAddress.toLowerCase()) {
|
|
2386
|
+
await mismatch('wrapApprove transaction spender does not match the wrapper contract', decoded.spender);
|
|
2387
|
+
}
|
|
2388
|
+
if (decoded.amount !== wrapParams.amount) {
|
|
2389
|
+
await mismatch('wrapApprove transaction amount does not match the requested amount', txJson.to, decoded.amount);
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
/**
|
|
2393
|
+
* Content-based guard for the ERC-7984 shield approve leg, for txs NOT declared `wrapApprove`.
|
|
2394
|
+
*
|
|
2395
|
+
* On signing paths that carry no caller txParams (pendingApproval re-sign) the declared type is the
|
|
2396
|
+
* server-persisted `intent.intentType`, so a compromised or buggy WP could relabel a mutated
|
|
2397
|
+
* `approve(wrapper, amount)` as another type and skip `verifyWrapApproveTransaction`. So an
|
|
2398
|
+
* `approve(address,uint256)` sent to a registered ERC-7984 underlying whose spender is that pair's wrapper
|
|
2399
|
+
* MUST be declared `wrapApprove` (and so pass the spender/amount check).
|
|
2400
|
+
*
|
|
2401
|
+
* A tx is rejected only when ALL of these hold:
|
|
2402
|
+
* 1. the tx targets a registered ERC-7984 underlying (`to` is in `getErc7984WrappersByUnderlying()`);
|
|
2403
|
+
* 2. the calldata is `approve(address,uint256)` (selector match);
|
|
2404
|
+
* 3. the spender (first ABI word) is that underlying's wrapper;
|
|
2405
|
+
* 4. the declared type is not `wrapApprove` (enforced by the caller, which only invokes this method then).
|
|
2406
|
+
*
|
|
2407
|
+
* Scoped to wrapper-directed approvals only: approvals of the same token to any other spender, transfers,
|
|
2408
|
+
* and every other call are untouched. Known exceptions to "only the shield journey approves the wrapper":
|
|
2409
|
+
* a dApp (WalletConnect) approving the wrapper directly would be rejected here, and the unbuilt Phase-2
|
|
2410
|
+
* `approve(0)` allowance-reset leg must be declared `wrapApprove` (or get its own branch).
|
|
2411
|
+
*
|
|
2412
|
+
* On multisig wallets the approve is wrapped in sendMultiSig(underlying, 0, approve(...), ...), so the inner
|
|
2413
|
+
* target and calldata are evaluated instead of the outer ones.
|
|
2414
|
+
*
|
|
2415
|
+
* Parsing is best-effort: if the tx cannot be parsed by this coin's builder the check is skipped, so it can
|
|
2416
|
+
* never introduce a new failure for a tx that was verifiable before.
|
|
2417
|
+
*/
|
|
2418
|
+
async assertWrapperApproveIsDeclared(params, throwRecipientMismatch) {
|
|
2419
|
+
const { txPrebuild } = params;
|
|
2420
|
+
if (!txPrebuild?.txHex) {
|
|
2421
|
+
return;
|
|
2422
|
+
}
|
|
2423
|
+
const wrappersByUnderlying = getErc7984WrappersByUnderlying();
|
|
2424
|
+
if (wrappersByUnderlying.size === 0) {
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
// Cheap prefilter: calldata is serialized as literal bytes, so an approve can only be present if the
|
|
2428
|
+
// 4-byte selector appears in the hex. Nearly every other tx returns here without touching the builder.
|
|
2429
|
+
if (!txPrebuild.txHex.toLowerCase().includes(lib_1.approveMethodId.slice(2).toLowerCase())) {
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2432
|
+
let txJson;
|
|
2433
|
+
try {
|
|
2434
|
+
const txBuilder = this.getTransactionBuilder();
|
|
2435
|
+
txBuilder.from(txPrebuild.txHex);
|
|
2436
|
+
txJson = (await txBuilder.build()).toJson();
|
|
2437
|
+
}
|
|
2438
|
+
catch {
|
|
2439
|
+
// Not parseable by this builder: leave it to the type-specific checks and the signer.
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2442
|
+
let target = txJson.to;
|
|
2443
|
+
let data = (txJson.data || '').toLowerCase();
|
|
2444
|
+
if (data.startsWith(lib_1.sendMultisigMethodId.toLowerCase())) {
|
|
2445
|
+
try {
|
|
2446
|
+
const inner = (0, lib_1.decodeNativeTransferData)(txJson.data || '');
|
|
2447
|
+
target = inner.to;
|
|
2448
|
+
data = inner.data.toLowerCase();
|
|
2449
|
+
}
|
|
2450
|
+
catch {
|
|
2451
|
+
// Not a decodable sendMultiSig call: leave it to the type-specific checks and the signer.
|
|
2452
|
+
return;
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
const wrappers = target ? wrappersByUnderlying.get(target.toLowerCase()) : undefined;
|
|
2456
|
+
// Read the spender leniently (first ABI word after the selector) rather than via the strict decoder: an
|
|
2457
|
+
// ERC-20 ignores trailing bytes, so approve(wrapper, amount) + junk would otherwise slip past a strict parse.
|
|
2458
|
+
if (!wrappers || !data.startsWith(lib_1.approveMethodId.toLowerCase()) || data.length < 10 + 64) {
|
|
2459
|
+
return;
|
|
2460
|
+
}
|
|
2461
|
+
const spender = `0x${data.slice(10 + 24, 10 + 64)}`;
|
|
2462
|
+
if (wrappers.has(spender)) {
|
|
2463
|
+
await throwRecipientMismatch('approve() of an ERC-7984 wrapper must be declared as a wrapApprove intent', [
|
|
2464
|
+
{ address: spender, amount: txJson.value || '' },
|
|
2465
|
+
]);
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
/**
|
|
2469
|
+
* @param {VerifyEthTransactionOptions} params
|
|
2470
|
+
* @param {TransactionParams} params.txParams - params object passed to send
|
|
2471
|
+
* @param {TransactionPrebuild} params.txPrebuild - prebuild object returned by server
|
|
2472
|
+
* @param {Wallet} params.wallet - Wallet object to obtain keys to verify against
|
|
2473
|
+
* @returns {boolean}
|
|
2474
|
+
* @throws {TxIntentMismatchRecipientError} if transaction recipients don't match user intent
|
|
2475
|
+
*/
|
|
2476
|
+
async verifyTssTransaction(params) {
|
|
2477
|
+
const { txParams, txPrebuild, wallet } = params;
|
|
2478
|
+
// Helper to throw TxIntentMismatchRecipientError with recipient details
|
|
2479
|
+
const throwRecipientMismatch = async (message, mismatchedRecipients) => {
|
|
2480
|
+
const txExplanation = await this.getTxExplanation(txPrebuild);
|
|
2481
|
+
throw new sdk_core_1.TxIntentMismatchRecipientError(message, undefined, [txParams], txPrebuild?.txHex, mismatchedRecipients, txExplanation);
|
|
2482
|
+
};
|
|
2483
|
+
if (!txParams?.recipients &&
|
|
2484
|
+
!(txParams.prebuildTx?.consolidateId ||
|
|
2485
|
+
txPrebuild?.consolidateId ||
|
|
2486
|
+
txParams.stakingRequestId ||
|
|
2487
|
+
txParams.prebuildTx?.stakingRequestId ||
|
|
2488
|
+
(txParams.type && sdk_core_1.NO_RECIPIENT_TX_TYPES.has(txParams.type)))) {
|
|
2489
|
+
throw new Error('missing txParams');
|
|
2490
|
+
}
|
|
2491
|
+
if (!wallet || !txPrebuild) {
|
|
2492
|
+
throw new Error('missing params');
|
|
2493
|
+
}
|
|
2494
|
+
if (txParams.hop && txParams.recipients && txParams.recipients.length > 1) {
|
|
2495
|
+
throw new Error('tx cannot be both a batch and hop transaction');
|
|
2496
|
+
}
|
|
2497
|
+
if (txParams.type && ['transfer'].includes(txParams.type)) {
|
|
2498
|
+
if (txParams.recipients && txParams.recipients.length === 1) {
|
|
2499
|
+
const recipients = txParams.recipients;
|
|
2500
|
+
const expectedAmount = recipients[0].amount.toString();
|
|
2501
|
+
const expectedDestination = recipients[0].address;
|
|
2502
|
+
const txBuilder = this.getTransactionBuilder();
|
|
2503
|
+
txBuilder.from(txPrebuild.txHex);
|
|
2504
|
+
const tx = await txBuilder.build();
|
|
2505
|
+
const txJson = tx.toJson();
|
|
2506
|
+
if (txJson.data === '0x') {
|
|
2507
|
+
if (expectedAmount !== txJson.value) {
|
|
2508
|
+
await throwRecipientMismatch('the transaction amount in txPrebuild does not match the value given by client', [{ address: txJson.to, amount: txJson.value }]);
|
|
2509
|
+
}
|
|
2510
|
+
if (expectedDestination.toLowerCase() !== txJson.to.toLowerCase()) {
|
|
2511
|
+
await throwRecipientMismatch('destination address does not match with the recipient address', [
|
|
2512
|
+
{ address: txJson.to, amount: txJson.value },
|
|
2513
|
+
]);
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
else if (txJson.data.startsWith('0xa9059cbb')) {
|
|
2517
|
+
const [recipientAddress, amount] = (0, lib_1.getRawDecoded)(['address', 'uint256'], (0, lib_1.getBufferedByteCode)('0xa9059cbb', txJson.data));
|
|
2518
|
+
// Check if recipients[0].data exists (WalletConnect flow)
|
|
2519
|
+
let expectedRecipientAddress;
|
|
2520
|
+
let expectedTokenAmount;
|
|
2521
|
+
const recipientData = recipients[0].data;
|
|
2522
|
+
if (recipientData && recipientData.startsWith('0xa9059cbb')) {
|
|
2523
|
+
// WalletConnect: decode expected recipient and amount from recipients[0].data
|
|
2524
|
+
const [expectedRecipient, expectedAmount] = (0, lib_1.getRawDecoded)(['address', 'uint256'], (0, lib_1.getBufferedByteCode)('0xa9059cbb', recipientData));
|
|
2525
|
+
expectedRecipientAddress = (0, ethereumjs_util_1.addHexPrefix)(expectedRecipient.toString()).toLowerCase();
|
|
2526
|
+
expectedTokenAmount = expectedAmount.toString();
|
|
2527
|
+
}
|
|
2528
|
+
else {
|
|
2529
|
+
// Normal flow: use recipients[0].address and recipients[0].amount
|
|
2530
|
+
expectedRecipientAddress = expectedDestination.toLowerCase();
|
|
2531
|
+
expectedTokenAmount = expectedAmount;
|
|
2532
|
+
}
|
|
2533
|
+
if (expectedTokenAmount !== amount.toString()) {
|
|
2534
|
+
await throwRecipientMismatch('the transaction amount in txPrebuild does not match the value given by client', [{ address: (0, ethereumjs_util_1.addHexPrefix)(recipientAddress.toString()), amount: amount.toString() }]);
|
|
2535
|
+
}
|
|
2536
|
+
if (expectedRecipientAddress !== (0, ethereumjs_util_1.addHexPrefix)(recipientAddress.toString()).toLowerCase()) {
|
|
2537
|
+
await throwRecipientMismatch('destination address does not match with the recipient address', [
|
|
2538
|
+
{ address: (0, ethereumjs_util_1.addHexPrefix)(recipientAddress.toString()), amount: amount.toString() },
|
|
2539
|
+
]);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
if (txParams.type && ['defiApprove', 'defiDeposit', 'defiWithdraw'].includes(txParams.type)) {
|
|
2545
|
+
if (!txPrebuild.txHex) {
|
|
2546
|
+
throw new Error('missing txHex in txPrebuild');
|
|
2547
|
+
}
|
|
2548
|
+
const baseAddress = wallet.coinSpecific()?.baseAddress;
|
|
2549
|
+
const defiParams = txParams.defiParams;
|
|
2550
|
+
if (!baseAddress || !defiParams) {
|
|
2551
|
+
await throwRecipientMismatch('DeFi transaction is missing required intent parameters', []);
|
|
2552
|
+
return false;
|
|
2553
|
+
}
|
|
2554
|
+
const validatedBaseAddress = baseAddress;
|
|
2555
|
+
const validatedDefiParams = defiParams;
|
|
2556
|
+
const txBuilder = this.getTransactionBuilder();
|
|
2557
|
+
txBuilder.from(txPrebuild.txHex);
|
|
2558
|
+
const txJson = (await txBuilder.build()).toJson();
|
|
2559
|
+
const mismatch = async (message, address = txJson.to, amount = txJson.value) => throwRecipientMismatch(message, [{ address: address || '', amount: amount || '' }]);
|
|
2560
|
+
const data = txJson.data || '';
|
|
2561
|
+
if (txParams.type === 'defiApprove') {
|
|
2562
|
+
const selector = (0, ethereumjs_util_1.addHexPrefix)(exports.optionalDeps.ethAbi.methodID('approve', ['address', 'uint256']).toString('hex'));
|
|
2563
|
+
if (!data.startsWith(selector)) {
|
|
2564
|
+
await mismatch('defiApprove transaction must use ERC-20 approve(address,uint256) calldata');
|
|
2565
|
+
}
|
|
2566
|
+
const [, amount] = (0, lib_1.getRawDecoded)(['address', 'uint256'], (0, lib_1.getBufferedByteCode)(selector, data));
|
|
2567
|
+
if (amount.toString() !== validatedDefiParams.amount) {
|
|
2568
|
+
await mismatch('defiApprove transaction amount does not match the requested amount', txJson.to, amount.toString());
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
else if (txParams.type === 'defiDeposit') {
|
|
2572
|
+
const selector = (0, ethereumjs_util_1.addHexPrefix)(exports.optionalDeps.ethAbi.methodID('deposit', ['uint256', 'address']).toString('hex'));
|
|
2573
|
+
if (!data.startsWith(selector)) {
|
|
2574
|
+
await mismatch('defiDeposit transaction must use deposit(uint256,address) calldata');
|
|
2575
|
+
}
|
|
2576
|
+
const [amount, receiver] = (0, lib_1.getRawDecoded)(['uint256', 'address'], (0, lib_1.getBufferedByteCode)(selector, data));
|
|
2577
|
+
const decodedReceiver = (0, ethereumjs_util_1.addHexPrefix)(receiver.toString()).toLowerCase();
|
|
2578
|
+
if (decodedReceiver !== validatedBaseAddress.toLowerCase()) {
|
|
2579
|
+
await mismatch('defiDeposit transaction receiver does not match wallet base address', decodedReceiver, amount.toString());
|
|
2580
|
+
}
|
|
2581
|
+
if (amount.toString() !== validatedDefiParams.amount) {
|
|
2582
|
+
await mismatch('defiDeposit transaction amount does not match the requested amount', decodedReceiver, amount.toString());
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
else {
|
|
2586
|
+
const selector = (0, ethereumjs_util_1.addHexPrefix)(exports.optionalDeps.ethAbi.methodID('redeem', ['uint256', 'address', 'address']).toString('hex'));
|
|
2587
|
+
if (!data.startsWith(selector)) {
|
|
2588
|
+
await mismatch('defiWithdraw transaction must use redeem(uint256,address,address) calldata');
|
|
2589
|
+
}
|
|
2590
|
+
const [shares, receiver, owner] = (0, lib_1.getRawDecoded)(['uint256', 'address', 'address'], (0, lib_1.getBufferedByteCode)(selector, data));
|
|
2591
|
+
const decodedReceiver = (0, ethereumjs_util_1.addHexPrefix)(receiver.toString()).toLowerCase();
|
|
2592
|
+
const decodedOwner = (0, ethereumjs_util_1.addHexPrefix)(owner.toString()).toLowerCase();
|
|
2593
|
+
if (decodedReceiver !== validatedBaseAddress.toLowerCase() ||
|
|
2594
|
+
decodedOwner !== validatedBaseAddress.toLowerCase()) {
|
|
2595
|
+
await mismatch('defiWithdraw transaction receiver and owner must match wallet base address', decodedReceiver, shares.toString());
|
|
2596
|
+
}
|
|
2597
|
+
if (shares.toString() !== validatedDefiParams.amount) {
|
|
2598
|
+
await mismatch('defiWithdraw transaction shares do not match the requested amount', decodedReceiver, shares.toString());
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
if (txParams.type === 'wrapApprove') {
|
|
2603
|
+
await this.verifyWrapApproveTransaction(params, throwRecipientMismatch);
|
|
2604
|
+
}
|
|
2605
|
+
else {
|
|
2606
|
+
// Declared as anything else: only an approve of a registered wrapper is rejected (see the method doc);
|
|
2607
|
+
// for every other tx this returns without parsing or after one best-effort parse.
|
|
2608
|
+
await this.assertWrapperApproveIsDeclared(params, throwRecipientMismatch);
|
|
2609
|
+
}
|
|
2610
|
+
// Verify consolidation transactions send to base address
|
|
2611
|
+
if (params.verification?.consolidationToBaseAddress) {
|
|
2612
|
+
const coinSpecific = wallet.coinSpecific();
|
|
2613
|
+
if (!coinSpecific || !coinSpecific.baseAddress) {
|
|
2614
|
+
throw new Error('Unable to determine base address for consolidation');
|
|
2615
|
+
}
|
|
2616
|
+
const baseAddress = coinSpecific.baseAddress;
|
|
2617
|
+
if (!txPrebuild.txHex) {
|
|
2618
|
+
throw new Error('missing txHex in txPrebuild');
|
|
2619
|
+
}
|
|
2620
|
+
const txBuilder = this.getTransactionBuilder();
|
|
2621
|
+
txBuilder.from(txPrebuild.txHex);
|
|
2622
|
+
const tx = await txBuilder.build();
|
|
2623
|
+
const txJson = tx.toJson();
|
|
2624
|
+
// Verify the transaction recipient matches the base address
|
|
2625
|
+
if (!txJson.to) {
|
|
2626
|
+
throw new Error('Consolidation transaction is missing recipient address');
|
|
2627
|
+
}
|
|
2628
|
+
const erc20TransferSelector = (0, ethereumjs_util_1.addHexPrefix)(exports.optionalDeps.ethAbi.methodID('transfer', ['address', 'uint256']).toString('hex'));
|
|
2629
|
+
if (txJson.data && txJson.data.startsWith(erc20TransferSelector)) {
|
|
2630
|
+
// For ERC20 token consolidations, txJson.to is the token contract address,
|
|
2631
|
+
// not the actual transfer recipient. The real recipient is encoded in the
|
|
2632
|
+
// transfer(address,uint256) calldata. Decode it and verify against baseAddress.
|
|
2633
|
+
const [recipientAddress] = (0, lib_1.getRawDecoded)(['address', 'uint256'], (0, lib_1.getBufferedByteCode)(erc20TransferSelector, txJson.data));
|
|
2634
|
+
const decodedRecipient = (0, ethereumjs_util_1.addHexPrefix)(recipientAddress.toString()).toLowerCase();
|
|
2635
|
+
if (decodedRecipient !== baseAddress.toLowerCase()) {
|
|
2636
|
+
await throwRecipientMismatch('Consolidation transaction recipient does not match wallet base address', [
|
|
2637
|
+
{ address: decodedRecipient, amount: txJson.value },
|
|
2638
|
+
]);
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
else if (txJson.to.toLowerCase() !== baseAddress.toLowerCase()) {
|
|
2642
|
+
// Native coin consolidation: txJson.to is the actual recipient
|
|
2643
|
+
await throwRecipientMismatch('Consolidation transaction recipient does not match wallet base address', [
|
|
2644
|
+
{ address: txJson.to, amount: txJson.value },
|
|
2645
|
+
]);
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
return true;
|
|
2649
|
+
}
|
|
2650
|
+
/**
|
|
2651
|
+
* Verify that a transaction prebuild complies with the original intention
|
|
2652
|
+
*
|
|
2653
|
+
* @param {VerifyEthTransactionOptions} params
|
|
2654
|
+
* @param {TransactionParams} params.txParams - params object passed to send
|
|
2655
|
+
* @param {TransactionPrebuild} params.txPrebuild - prebuild object returned by server
|
|
2656
|
+
* @param {Wallet} params.wallet - Wallet object to obtain keys to verify against
|
|
2657
|
+
* @returns {boolean}
|
|
2658
|
+
* @throws {TxIntentMismatchError} if transaction validation fails
|
|
2659
|
+
* @throws {TxIntentMismatchRecipientError} if transaction recipients don't match user intent
|
|
2660
|
+
*/
|
|
2661
|
+
async verifyTransaction(params) {
|
|
2662
|
+
const ethNetwork = this.getNetwork();
|
|
2663
|
+
const { txParams, txPrebuild, wallet, walletType } = params;
|
|
2664
|
+
if (walletType === 'tss') {
|
|
2665
|
+
return this.verifyTssTransaction(params);
|
|
2666
|
+
}
|
|
2667
|
+
// Helper to throw TxIntentMismatchRecipientError with recipient details
|
|
2668
|
+
const throwRecipientMismatch = async (message, mismatchedRecipients) => {
|
|
2669
|
+
const txExplanation = await this.getTxExplanation(txPrebuild);
|
|
2670
|
+
throw new sdk_core_1.TxIntentMismatchRecipientError(message, undefined, [txParams], txPrebuild?.txHex, mismatchedRecipients, txExplanation);
|
|
2671
|
+
};
|
|
2672
|
+
// ERC-7984 shield approval for non-TSS (onchain multisig) wallets: intent-based with no client
|
|
2673
|
+
// recipients — the multisig prebuild carries the sendMultiSig-wrapped approve calldata instead.
|
|
2674
|
+
// Checked before the generic recipient guards, which require txParams.recipients.
|
|
2675
|
+
if (txParams?.type === 'wrapApprove') {
|
|
2676
|
+
await this.verifyMultisigWrapApproveTransaction(params, throwRecipientMismatch);
|
|
2677
|
+
await this.throwIfPrebuildCoinMismatch(txParams, txPrebuild);
|
|
2678
|
+
return true;
|
|
2679
|
+
}
|
|
2680
|
+
if (!txParams?.recipients || !txPrebuild?.recipients || !wallet) {
|
|
2681
|
+
throw new Error('missing params');
|
|
2682
|
+
}
|
|
2683
|
+
// Every declared type that did not return above: only an approve of a registered wrapper is rejected, seen
|
|
2684
|
+
// through sendMultiSig (see assertWrapperApproveIsDeclared).
|
|
2685
|
+
await this.assertWrapperApproveIsDeclared(params, throwRecipientMismatch);
|
|
2686
|
+
const recipients = txParams.recipients;
|
|
2687
|
+
if (txParams.hop && recipients.length > 1) {
|
|
2688
|
+
throw new Error('tx cannot be both a batch and hop transaction');
|
|
2689
|
+
}
|
|
2690
|
+
if (txPrebuild.recipients.length > 1) {
|
|
2691
|
+
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.`);
|
|
2692
|
+
}
|
|
2693
|
+
if (txParams.type === 'enabletoken') {
|
|
2694
|
+
// ERC-7984 token enablement: intercept before hop/batch/single routing.
|
|
2695
|
+
// Handles both base-address (txPrebuild → ACL) and forwarder (txPrebuild → forwarder) wallets,
|
|
2696
|
+
// for single or multiple tokens at once.
|
|
2697
|
+
await this.verifyMultisigEnableTokenTransaction(params, throwRecipientMismatch);
|
|
2698
|
+
}
|
|
2699
|
+
else if (txParams.hop && txPrebuild.hopTransaction) {
|
|
2700
|
+
// Check recipient amount for hop transaction
|
|
2701
|
+
if (recipients.length !== 1) {
|
|
2702
|
+
throw new Error(`hop transaction only supports 1 recipient but ${recipients.length} found`);
|
|
2703
|
+
}
|
|
2704
|
+
// Check tx sends to hop address
|
|
2705
|
+
const decodedHopTx = exports.optionalDeps.EthTx.TransactionFactory.fromSerializedData(exports.optionalDeps.ethUtil.toBuffer(txPrebuild.hopTransaction.tx));
|
|
2706
|
+
const expectedHopAddress = exports.optionalDeps.ethUtil.stripHexPrefix(decodedHopTx.getSenderAddress().toString());
|
|
2707
|
+
const actualHopAddress = exports.optionalDeps.ethUtil.stripHexPrefix(txPrebuild.recipients[0].address);
|
|
2708
|
+
if (expectedHopAddress.toLowerCase() !== actualHopAddress.toLowerCase()) {
|
|
2709
|
+
await throwRecipientMismatch('recipient address of txPrebuild does not match hop address', [
|
|
2710
|
+
{ address: txPrebuild.recipients[0].address, amount: txPrebuild.recipients[0].amount.toString() },
|
|
2711
|
+
]);
|
|
2712
|
+
}
|
|
2713
|
+
// Convert TransactionRecipient array to Recipient array
|
|
2714
|
+
const hopRecipients = recipients.map((r) => {
|
|
2715
|
+
return {
|
|
2716
|
+
address: r.address,
|
|
2717
|
+
amount: typeof r.amount === 'number' ? r.amount.toString() : r.amount,
|
|
2718
|
+
};
|
|
2719
|
+
});
|
|
2720
|
+
// Check destination address and amount
|
|
2721
|
+
await this.validateHopPrebuild(wallet, txPrebuild.hopTransaction, { recipients: hopRecipients });
|
|
2722
|
+
}
|
|
2723
|
+
else if (recipients.length > 1) {
|
|
2724
|
+
// Check total amount for batch transaction
|
|
2725
|
+
if (txParams.tokenName) {
|
|
2726
|
+
const expectedTotalAmount = new bignumber_js_1.BigNumber(0);
|
|
2727
|
+
if (!expectedTotalAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
|
|
2728
|
+
await throwRecipientMismatch('batch token transaction amount in txPrebuild should be zero for token transfers', [{ address: txPrebuild.recipients[0].address, amount: txPrebuild.recipients[0].amount.toString() }]);
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
else {
|
|
2732
|
+
let expectedTotalAmount = new bignumber_js_1.BigNumber(0);
|
|
2733
|
+
for (let i = 0; i < recipients.length; i++) {
|
|
2734
|
+
expectedTotalAmount = expectedTotalAmount.plus(recipients[i].amount);
|
|
2735
|
+
}
|
|
2736
|
+
if (!expectedTotalAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
|
|
2737
|
+
await throwRecipientMismatch('batch transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client', [{ address: txPrebuild.recipients[0].address, amount: txPrebuild.recipients[0].amount.toString() }]);
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
// Check batch transaction is sent to the batcher contract address for the chain
|
|
2741
|
+
const batcherContractAddress = ethNetwork?.batcherContractAddress;
|
|
2742
|
+
if (!batcherContractAddress ||
|
|
2743
|
+
batcherContractAddress.toLowerCase() !== txPrebuild.recipients[0].address.toLowerCase()) {
|
|
2744
|
+
await throwRecipientMismatch('recipient address of txPrebuild does not match batcher address', [
|
|
2745
|
+
{ address: txPrebuild.recipients[0].address, amount: txPrebuild.recipients[0].amount.toString() },
|
|
2746
|
+
]);
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
else {
|
|
2750
|
+
// Check recipient address and amount for normal transaction
|
|
2751
|
+
if (recipients.length !== 1) {
|
|
2752
|
+
throw new Error(`normal transaction only supports 1 recipient but ${recipients.length} found`);
|
|
2753
|
+
}
|
|
2754
|
+
const expectedAmount = new bignumber_js_1.BigNumber(recipients[0].amount);
|
|
2755
|
+
if (!expectedAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
|
|
2756
|
+
await throwRecipientMismatch('normal transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client', [{ address: txPrebuild.recipients[0].address, amount: txPrebuild.recipients[0].amount.toString() }]);
|
|
2757
|
+
}
|
|
2758
|
+
if (this.isETHAddress(recipients[0].address) && recipients[0].address !== txPrebuild.recipients[0].address) {
|
|
2759
|
+
await throwRecipientMismatch('destination address in normal txPrebuild does not match that in txParams supplied by client', [{ address: txPrebuild.recipients[0].address, amount: txPrebuild.recipients[0].amount.toString() }]);
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
// Check coin is correct for all transaction types
|
|
2763
|
+
await this.throwIfPrebuildCoinMismatch(txParams, txPrebuild);
|
|
2764
|
+
return true;
|
|
2765
|
+
}
|
|
2766
|
+
/**
|
|
2767
|
+
* Check if address is valid eth address
|
|
2768
|
+
* @param address
|
|
2769
|
+
* @returns {boolean}
|
|
2770
|
+
*/
|
|
2771
|
+
/**
|
|
2772
|
+
* Verifies an ERC-7984 token enablement (decryption delegation) transaction for multisig wallets.
|
|
2773
|
+
*
|
|
2774
|
+
* Two wallet shapes are supported:
|
|
2775
|
+
*
|
|
2776
|
+
* Base-address wallet — txPrebuild targets the Zama ACL contract directly:
|
|
2777
|
+
* txParams.recipients : one entry per token, all with the wallet base address, amount '0'
|
|
2778
|
+
* txPrebuild.recipients[0] : the Zama ACL contract address
|
|
2779
|
+
*
|
|
2780
|
+
* Forwarder wallet — txPrebuild targets the forwarder which calls the ACL via callFromParent:
|
|
2781
|
+
* txParams.recipients : one entry per token, all with the forwarder address, amount '0'
|
|
2782
|
+
* txPrebuild.recipients[0] : the forwarder address (matches txParams recipients)
|
|
2783
|
+
*
|
|
2784
|
+
* In both cases all amounts must be 0 and all txParams recipients must share the same address.
|
|
2785
|
+
*/
|
|
2786
|
+
async verifyMultisigEnableTokenTransaction(params, throwRecipientMismatch) {
|
|
2787
|
+
const { txParams, txPrebuild } = params;
|
|
2788
|
+
const recipients = txParams.recipients;
|
|
2789
|
+
if (!recipients || recipients.length === 0) {
|
|
2790
|
+
throw new Error('token enablement transaction must have at least one recipient in txParams');
|
|
2791
|
+
}
|
|
2792
|
+
// All txParams recipients must have amount 0
|
|
2793
|
+
for (const r of recipients) {
|
|
2794
|
+
if (!new bignumber_js_1.BigNumber(r.amount).isEqualTo(0)) {
|
|
2795
|
+
await throwRecipientMismatch('token enablement txParams recipients must all have amount 0', [
|
|
2796
|
+
{ address: r.address, amount: String(r.amount) },
|
|
2797
|
+
]);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
// txPrebuild recipient amount must be 0
|
|
2801
|
+
if (!new bignumber_js_1.BigNumber(txPrebuild.recipients[0].amount).isEqualTo(0)) {
|
|
2802
|
+
await throwRecipientMismatch('token enablement transaction expected amount 0 in txPrebuild', [
|
|
2803
|
+
{ address: txPrebuild.recipients[0].address, amount: txPrebuild.recipients[0].amount.toString() },
|
|
2804
|
+
]);
|
|
2805
|
+
}
|
|
2806
|
+
// All txParams recipients must share the same address —
|
|
2807
|
+
// a single delegation tx covers one wallet, not multiple wallets simultaneously.
|
|
2808
|
+
const firstAddress = recipients[0].address.toLowerCase();
|
|
2809
|
+
for (const r of recipients) {
|
|
2810
|
+
if (r.address.toLowerCase() !== firstAddress) {
|
|
2811
|
+
await throwRecipientMismatch('token enablement txParams recipients must all have the same address', [
|
|
2812
|
+
{ address: r.address, amount: String(r.amount) },
|
|
2813
|
+
]);
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
const prebuildAddress = txPrebuild.recipients[0].address.toLowerCase();
|
|
2817
|
+
const zamaAclContractAddress = this.getNetwork()?.zamaAclContractAddress;
|
|
2818
|
+
// Base-address wallet: txPrebuild targets the Zama ACL contract directly.
|
|
2819
|
+
// txParams recipients carry the wallet base address — the intentional address mismatch is expected.
|
|
2820
|
+
if (zamaAclContractAddress && prebuildAddress === zamaAclContractAddress.toLowerCase()) {
|
|
2821
|
+
return;
|
|
2822
|
+
}
|
|
2823
|
+
// Forwarder wallet: txPrebuild targets the forwarder address, which must match txParams recipients.
|
|
2824
|
+
if (prebuildAddress === firstAddress) {
|
|
2825
|
+
return;
|
|
2826
|
+
}
|
|
2827
|
+
// Neither recognised shape — txPrebuild address is not the ACL contract and doesn't match txParams
|
|
2828
|
+
await throwRecipientMismatch('token enablement txPrebuild recipient must be the Zama ACL contract (base-address wallet) or match txParams recipients (forwarder wallet)', [{ address: txPrebuild.recipients[0].address, amount: txPrebuild.recipients[0].amount.toString() }]);
|
|
2829
|
+
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Shared tail check for verify paths: the prebuild must be for the coin performing the verification.
|
|
2832
|
+
*/
|
|
2833
|
+
async throwIfPrebuildCoinMismatch(txParams, txPrebuild) {
|
|
2834
|
+
if (this.verifyCoin(txPrebuild)) {
|
|
2835
|
+
return;
|
|
2836
|
+
}
|
|
2837
|
+
const txExplanation = await this.getTxExplanation(txPrebuild);
|
|
2838
|
+
throw new sdk_core_1.TxIntentMismatchError('coin in txPrebuild did not match that in txParams supplied by client', undefined, [txParams], txPrebuild?.txHex, txExplanation);
|
|
2839
|
+
}
|
|
2840
|
+
/**
|
|
2841
|
+
* Verifies an ERC-7984 wrapApprove (shield approval) transaction for non-TSS (onchain multisig) wallets.
|
|
2842
|
+
*
|
|
2843
|
+
* Multisig shape (server-built unsigned tx):
|
|
2844
|
+
* tx.to = wallet contract
|
|
2845
|
+
* tx.data = sendMultiSig(underlyingErc20, 0, approve(wrapper, amount), expireTime, sequenceId, signature)
|
|
2846
|
+
* txPrebuild.recipients[0] = { address: underlyingErc20, amount: '0', data: approve calldata }
|
|
2847
|
+
*
|
|
2848
|
+
* Checks: the sendMultiSig inner target is the wrapper pair's underlying ERC-20, the sendMultiSig
|
|
2849
|
+
* value is 0, the inner calldata is approve(wrapper, wrapParams.amount) exactly, and — when the
|
|
2850
|
+
* prebuild carries build recipients — that they mirror the sendMultiSig args encoded in txHex
|
|
2851
|
+
* (the client signs the operation hash over those recipients).
|
|
2852
|
+
*/
|
|
2853
|
+
async verifyMultisigWrapApproveTransaction(params, throwRecipientMismatch) {
|
|
2854
|
+
const { txParams, txPrebuild } = params;
|
|
2855
|
+
if (!txParams) {
|
|
2856
|
+
throw new Error('missing params');
|
|
2857
|
+
}
|
|
2858
|
+
const wrapParams = txParams.wrapParams;
|
|
2859
|
+
if (!txPrebuild?.txHex) {
|
|
2860
|
+
throw new Error('missing txHex in txPrebuild');
|
|
2861
|
+
}
|
|
2862
|
+
if (!wrapParams?.tokenName || !wrapParams.amount) {
|
|
2863
|
+
return throwRecipientMismatch('wrapApprove transaction requires wrapParams with tokenName and amount', []);
|
|
2864
|
+
}
|
|
2865
|
+
let wrapper;
|
|
2866
|
+
let underlyingErc20Address;
|
|
2867
|
+
try {
|
|
2868
|
+
const staticsCoin = statics_1.coins.get(wrapParams.tokenName);
|
|
2869
|
+
if (!(staticsCoin instanceof statics_1.Erc7984Coin) || !staticsCoin.underlyingErc20Address) {
|
|
2870
|
+
throw new Error('not an ERC-7984 pair');
|
|
2871
|
+
}
|
|
2872
|
+
wrapper = staticsCoin;
|
|
2873
|
+
underlyingErc20Address = staticsCoin.underlyingErc20Address.toLowerCase();
|
|
2874
|
+
}
|
|
2875
|
+
catch {
|
|
2876
|
+
return throwRecipientMismatch(`wrapApprove: '${wrapParams.tokenName}' is not a registered ERC-7984 shield pair`, []);
|
|
2877
|
+
}
|
|
2878
|
+
const txBuilder = this.getTransactionBuilder();
|
|
2879
|
+
txBuilder.from(txPrebuild.txHex);
|
|
2880
|
+
const txJson = (await txBuilder.build()).toJson();
|
|
2881
|
+
let innerTo;
|
|
2882
|
+
let innerAmount;
|
|
2883
|
+
let innerData;
|
|
2884
|
+
const data = txJson.data || '';
|
|
2885
|
+
if (!data.toLowerCase().startsWith(lib_1.sendMultisigMethodId.toLowerCase())) {
|
|
2886
|
+
return throwRecipientMismatch('wrapApprove multisig transaction must encode the approve call inside sendMultiSig', []);
|
|
2887
|
+
}
|
|
2888
|
+
try {
|
|
2889
|
+
const decoded = (0, lib_1.decodeNativeTransferData)(data);
|
|
2890
|
+
innerTo = decoded.to;
|
|
2891
|
+
innerAmount = decoded.amount;
|
|
2892
|
+
innerData = decoded.data;
|
|
2893
|
+
}
|
|
2894
|
+
catch (e) {
|
|
2895
|
+
return throwRecipientMismatch(`wrapApprove multisig transaction failed to decode sendMultiSig calldata — ${e.message}`, []);
|
|
2896
|
+
}
|
|
2897
|
+
const mismatch = (message, address = innerTo, amount = innerAmount) => throwRecipientMismatch(message, [{ address: address || '', amount: amount || '' }]);
|
|
2898
|
+
if (innerTo.toLowerCase() !== underlyingErc20Address) {
|
|
2899
|
+
await mismatch('wrapApprove transaction must target the underlying ERC-20 of the wrapper');
|
|
2900
|
+
}
|
|
2901
|
+
if (innerAmount !== '0') {
|
|
2902
|
+
await mismatch('wrapApprove transaction must not carry native value inside sendMultiSig');
|
|
2903
|
+
}
|
|
2904
|
+
let approve;
|
|
2905
|
+
try {
|
|
2906
|
+
approve = (0, lib_1.decodeApproveCalldata)(innerData || '');
|
|
2907
|
+
}
|
|
2908
|
+
catch (e) {
|
|
2909
|
+
return mismatch(`wrapApprove transaction must use ERC-20 approve(address,uint256) calldata — ${e.message}`);
|
|
2910
|
+
}
|
|
2911
|
+
if (approve.spender.toLowerCase() !== wrapper.contractAddress.toLowerCase()) {
|
|
2912
|
+
await mismatch('wrapApprove transaction spender does not match the wrapper contract', approve.spender);
|
|
2913
|
+
}
|
|
2914
|
+
if (approve.amount !== wrapParams.amount) {
|
|
2915
|
+
await mismatch('wrapApprove transaction amount does not match the requested amount', innerTo, approve.amount);
|
|
2916
|
+
}
|
|
2917
|
+
// Cross-check the build recipients the client signs the operation hash against — they must
|
|
2918
|
+
// mirror the sendMultiSig args encoded in txHex.
|
|
2919
|
+
const prebuildRecipient = txPrebuild.recipients?.[0];
|
|
2920
|
+
if (prebuildRecipient) {
|
|
2921
|
+
const recipientDetail = [
|
|
2922
|
+
{ address: prebuildRecipient.address, amount: prebuildRecipient.amount?.toString() ?? '' },
|
|
2923
|
+
];
|
|
2924
|
+
if (prebuildRecipient.address?.toLowerCase() !== innerTo.toLowerCase()) {
|
|
2925
|
+
await throwRecipientMismatch('wrapApprove prebuild recipient address does not match the sendMultiSig target encoded in txHex', recipientDetail);
|
|
2926
|
+
}
|
|
2927
|
+
let recipientData;
|
|
2928
|
+
if ('data' in prebuildRecipient && typeof prebuildRecipient.data === 'string') {
|
|
2929
|
+
recipientData = prebuildRecipient.data;
|
|
2930
|
+
}
|
|
2931
|
+
if (recipientData && (0, ethereumjs_util_1.stripHexPrefix)(recipientData).toLowerCase() !== (0, ethereumjs_util_1.stripHexPrefix)(innerData).toLowerCase()) {
|
|
2932
|
+
await throwRecipientMismatch('wrapApprove prebuild recipient data does not match the sendMultiSig calldata encoded in txHex', recipientDetail);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
isETHAddress(address) {
|
|
2937
|
+
return !!address.match(/0x[a-fA-F0-9]{40}/);
|
|
2938
|
+
}
|
|
2939
|
+
/**
|
|
2940
|
+
* Transform message to accommodate specific blockchain requirements.
|
|
2941
|
+
* @param {string} message - the message to prepare
|
|
2942
|
+
* @return {string} the prepared message as a hex encoded string.
|
|
2943
|
+
*/
|
|
2944
|
+
encodeMessage(message) {
|
|
2945
|
+
const prefix = `\u0019Ethereum Signed Message:\n${message.length}`;
|
|
2946
|
+
return Buffer.from(prefix.concat(message)).toString('hex');
|
|
2947
|
+
}
|
|
2948
|
+
/**
|
|
2949
|
+
* Transform the Typed data to accomodate the blockchain requirements (EIP-712)
|
|
2950
|
+
* @param {TypedData} typedData - the typed data to prepare
|
|
2951
|
+
* @return {Buffer} a buffer of the result
|
|
2952
|
+
*/
|
|
2953
|
+
encodeTypedData(typedData) {
|
|
2954
|
+
const version = typedData.version;
|
|
2955
|
+
if (version === eth_sig_util_1.SignTypedDataVersion.V1) {
|
|
2956
|
+
throw new Error('SignTypedData v1 is not supported due to security concerns');
|
|
2957
|
+
}
|
|
2958
|
+
const typedDataRaw = JSON.parse(typedData.typedDataRaw);
|
|
2959
|
+
const sanitizedData = eth_sig_util_1.TypedDataUtils.sanitizeData(typedDataRaw);
|
|
2960
|
+
const parts = [Buffer.from('1901', 'hex')];
|
|
2961
|
+
const eip712Domain = 'EIP712Domain';
|
|
2962
|
+
parts.push(eth_sig_util_1.TypedDataUtils.hashStruct(eip712Domain, sanitizedData.domain, sanitizedData.types, version));
|
|
2963
|
+
if (sanitizedData.primaryType !== eip712Domain) {
|
|
2964
|
+
parts.push(eth_sig_util_1.TypedDataUtils.hashStruct(sanitizedData.primaryType, sanitizedData.message, sanitizedData.types, version));
|
|
2965
|
+
}
|
|
2966
|
+
return Buffer.concat(parts);
|
|
2967
|
+
}
|
|
2968
|
+
/**
|
|
2969
|
+
* Build the data to transfer an ERC-721 or ERC-1155 token to another address
|
|
2970
|
+
* @param params
|
|
2971
|
+
*/
|
|
2972
|
+
buildNftTransferData(params) {
|
|
2973
|
+
const { tokenContractAddress, recipientAddress, fromAddress } = params;
|
|
2974
|
+
switch (params.type) {
|
|
2975
|
+
case 'ERC721': {
|
|
2976
|
+
const tokenId = params.tokenId;
|
|
2977
|
+
const contractData = new lib_1.ERC721TransferBuilder()
|
|
2978
|
+
.tokenContractAddress(tokenContractAddress)
|
|
2979
|
+
.to(recipientAddress)
|
|
2980
|
+
.from(fromAddress)
|
|
2981
|
+
.tokenId(tokenId)
|
|
2982
|
+
.build();
|
|
2983
|
+
return contractData;
|
|
2984
|
+
}
|
|
2985
|
+
case 'ERC1155': {
|
|
2986
|
+
const entries = params.entries;
|
|
2987
|
+
const transferBuilder = new lib_1.ERC1155TransferBuilder()
|
|
2988
|
+
.tokenContractAddress(tokenContractAddress)
|
|
2989
|
+
.to(recipientAddress)
|
|
2990
|
+
.from(fromAddress);
|
|
2991
|
+
for (const entry of entries) {
|
|
2992
|
+
transferBuilder.entry(parseInt(entry.tokenId, 10), entry.amount);
|
|
2993
|
+
}
|
|
2994
|
+
return transferBuilder.build();
|
|
2995
|
+
}
|
|
2996
|
+
default:
|
|
2997
|
+
throw new Error(`Unsupported NFT type: ${params.type}`);
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
/**
|
|
3001
|
+
* Fetch the gas price from the explorer
|
|
3002
|
+
* @param {string} wrongChainCoin - the coin that we're getting gas price for
|
|
3003
|
+
* @param {string} apiKey - optional API key to use instead of the one from the environment
|
|
3004
|
+
*/
|
|
3005
|
+
async getGasPriceFromExternalAPI(wrongChainCoin, apiKey) {
|
|
3006
|
+
try {
|
|
3007
|
+
const res = await this.recoveryBlockchainExplorerQuery({
|
|
3008
|
+
chainid: this.getChainId().toString(),
|
|
3009
|
+
module: 'proxy',
|
|
3010
|
+
action: 'eth_gasPrice',
|
|
3011
|
+
}, apiKey);
|
|
3012
|
+
const gasPrice = new bn_js_1.default(res.result.slice(2), 16);
|
|
3013
|
+
console.log(` Got gas price: ${gasPrice}`);
|
|
3014
|
+
return gasPrice;
|
|
3015
|
+
}
|
|
3016
|
+
catch (e) {
|
|
3017
|
+
if (e instanceof sdk_core_1.RecoveryProviderError) {
|
|
3018
|
+
throw e;
|
|
3019
|
+
}
|
|
3020
|
+
throw new Error(`Failed to get gas price. Please make sure to use the api key of ${wrongChainCoin}`);
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
/**
|
|
3024
|
+
* Fetch the gas limit from the explorer
|
|
3025
|
+
* @param intendedChain
|
|
3026
|
+
* @param from
|
|
3027
|
+
* @param to
|
|
3028
|
+
* @param data
|
|
3029
|
+
* @param {string} apiKey - optional API key to use instead of the one from the environment
|
|
3030
|
+
*/
|
|
3031
|
+
async getGasLimitFromExternalAPI(intendedChain, from, to, data, apiKey) {
|
|
3032
|
+
try {
|
|
3033
|
+
const res = await this.recoveryBlockchainExplorerQuery({
|
|
3034
|
+
chainid: this.getChainId().toString(),
|
|
3035
|
+
module: 'proxy',
|
|
3036
|
+
action: 'eth_estimateGas',
|
|
3037
|
+
from,
|
|
3038
|
+
to,
|
|
3039
|
+
data,
|
|
3040
|
+
}, apiKey);
|
|
3041
|
+
const gasLimit = new bn_js_1.default(res.result.slice(2), 16);
|
|
3042
|
+
console.log(`Got gas limit: ${gasLimit}`);
|
|
3043
|
+
return gasLimit;
|
|
3044
|
+
}
|
|
3045
|
+
catch (e) {
|
|
3046
|
+
if (e instanceof sdk_core_1.RecoveryProviderError) {
|
|
3047
|
+
throw e;
|
|
3048
|
+
}
|
|
3049
|
+
throw new Error(`Failed to get gas limit. Please make sure to use the privateKey aka userKey of ${intendedChain} wallet ${to}`);
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
/**
|
|
3053
|
+
* Get the balance of bitgoFeeAddress to ensure funds are available to pay fees
|
|
3054
|
+
* @param bitgoFeeAddress
|
|
3055
|
+
* @param gasPrice
|
|
3056
|
+
* @param gasLimit
|
|
3057
|
+
* @param apiKey - optional API key to use instead of the one from the environment
|
|
3058
|
+
*/
|
|
3059
|
+
async ensureSufficientBalance(bitgoFeeAddress, gasPrice, gasLimit, apiKey) {
|
|
3060
|
+
const bitgoFeeAddressBalance = await this.queryAddressBalance(bitgoFeeAddress, apiKey);
|
|
3061
|
+
const totalGasNeeded = Number(gasPrice.mul(gasLimit));
|
|
3062
|
+
const weiToGwei = 10 ** 9;
|
|
3063
|
+
if (bitgoFeeAddressBalance.lt(totalGasNeeded)) {
|
|
3064
|
+
throw new Error(`Fee address ${bitgoFeeAddress} has balance ${(bitgoFeeAddressBalance / weiToGwei).toString()} Gwei.` +
|
|
3065
|
+
`This address must have a balance of at least ${(totalGasNeeded / weiToGwei).toString()}` +
|
|
3066
|
+
` Gwei to perform recoveries. Try sending some ${this.getChain()} to this address then retry.`);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
exports.AbstractEthLikeNewCoins = AbstractEthLikeNewCoins;
|
|
3071
|
+
AbstractEthLikeNewCoins.hopTransactionSalt = 'bitgoHopAddressRequestSalt';
|
|
3072
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBcWRBLGdGQVVDO0FBL2REOztHQUVHO0FBQ0gsbURBb0Q4QjtBQUM5Qix5REFBNEQ7QUFDNUQscURBQThDO0FBQzlDLGlEQVE2QjtBQUc3Qix1Q0FBbUg7QUFDbkgseUNBQXNDO0FBQ3RDLHlEQUE0RjtBQUM1RiwrQ0FBeUM7QUFDekMsa0RBQXVCO0FBQ3ZCLG1DQUFxQztBQUNyQyxrREFBNkI7QUFDN0IscURBQWtIO0FBQ2xILG9EQUE0QjtBQUM1QixvREFBdUI7QUFDdkIsMERBQWtDO0FBRWxDLCtEQUE0RDtBQUM1RCxpREFBOEM7QUFDOUMsK0JBbUJlO0FBcVRGLFFBQUEsbUJBQW1CLEdBQUcsRUFBRSxDQUFDO0FBc0R0Qzs7O0dBR0c7QUFDSCxTQUFnQixrQ0FBa0MsQ0FDaEQsTUFBNEQ7SUFFNUQsT0FBTyxDQUNMLENBQUMsTUFBTSxDQUFDLGFBQWEsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLGFBQWEsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLGFBQWEsS0FBSyxDQUFDLENBQUM7UUFDeEYsV0FBVyxJQUFJLE1BQU07UUFDckIsS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDO1FBQy9CLE1BQU0sQ0FBQyxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUM7UUFDN0IsTUFBTSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFPLEVBQUUsRUFBRSxDQUFDLFlBQVksSUFBSSxFQUFFLElBQUksT0FBTyxFQUFFLENBQUMsVUFBVSxLQUFLLFFBQVEsQ0FBQyxDQUM3RixDQUFDO0FBQ0osQ0FBQztBQUVELE1BQU0sS0FBSyxHQUFHLElBQUEsZUFBUSxFQUFDLGtCQUFrQixDQUFDLENBQUM7QUFFM0MsSUFBSSwy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QUUsTUFBZTtRQUNoRyxNQUFNLHNCQUFzQixHQUFHLE1BQU0sSUFBSSxDQUFDLG1CQUFtQixDQUFDLGVBQWUsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUN2RixNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO1FBQ3RELE1BQU0sU0FBUyxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDMUIsSUFBSSxzQkFBc0IsQ0FBQyxFQUFFLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztZQUM5QyxNQUFNLElBQUksS0FBSyxDQUNiLGVBQWUsZUFBZSxnQkFBZ0IsQ0FBQyxzQkFBc0IsR0FBRyxTQUFTLENBQUMsQ0FBQyxRQUFRLEVBQUUsUUFBUTtnQkFDbkcsZ0RBQWdELENBQUMsY0FBYyxHQUFHLFNBQVMsQ0FBQyxDQUFDLFFBQVEsRUFBRSxFQUFFO2dCQUN6RixpREFBaUQsSUFBSSxDQUFDLFFBQVEsRUFBRSw4QkFBOEIsQ0FDakcsQ0FBQztRQUNKLENBQUM7SUFDSCxDQUFDOztBQS9tSEgsMERBZ25IQztBQS9tSFEsMENBQWtCLEdBQUcsNEJBQTRCLEFBQS9CLENBQWdDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAcHJldHRpZXJcbiAqL1xuaW1wb3J0IHtcbiAgQWRkcmVzc0NvaW5TcGVjaWZpYyxcbiAgQml0R29CYXNlLFxuICBCdWlsZE5mdFRyYW5zZmVyRGF0YU9wdGlvbnMsXG4gIGNvbW1vbixcbiAgRGVmaUludGVudFBhcmFtcyxcbiAgRWNkc2EsXG4gIEVDRFNBTWV0aG9kVHlwZXMsXG4gIEVDRFNBVXRpbHMsXG4gIEVycm9yTm9JbnB1dFRvUmVjb3ZlcixcbiAgRXRoZXJldW1MaWJyYXJ5VW5hdmFpbGFibGVFcnJvcixcbiAgRmVlRXN0aW1hdGVPcHRpb25zLFxuICBGdWxseVNpZ25lZFRyYW5zYWN0aW9uLFxuICBnZXRJc1Vuc2lnbmVkU3dlZXAsXG4gIEhhbGZTaWduZWRUcmFuc2FjdGlvbixcbiAgSW52YWxpZEFkZHJlc3NFcnJvcixcbiAgSW52YWxpZEFkZHJlc3NWZXJpZmljYXRpb25PYmplY3RQcm9wZXJ0eUVycm9yLFxuICBJbnZhbGlkVHJhbnNhY3Rpb25FcnJvcixcbiAgSVdhbGxldCxcbiAgS2V5UGFpcixcbiAgTVBDU3dlZXBSZWNvdmVyeU9wdGlvbnMsXG4gIE1QQ1N3ZWVwVHhzLFxuICBNUENUeCxcbiAgTVBDVHhzLFxuICBQYXJzZWRUcmFuc2FjdGlvbixcbiAgUGFyc2VUcmFuc2FjdGlvbk9wdGlvbnMsXG4gIFByZWJ1aWxkVHJhbnNhY3Rpb25SZXN1bHQsXG4gIFByZXNpZ25UcmFuc2FjdGlvbk9wdGlvbnMgYXMgQmFzZVByZXNpZ25UcmFuc2FjdGlvbk9wdGlvbnMsXG4gIFJlY2lwaWVudCxcbiAgUmVjb3ZlcnlLZXlNaXNtYXRjaEVycm9yLFxuICBSZWNvdmVyeVByb3ZpZGVyRXJyb3IsXG4gIFNpZ25UcmFuc2FjdGlvbk9wdGlvbnMgYXMgQmFzZVNpZ25UcmFuc2FjdGlvbk9wdGlvbnMsXG4gIFR4SW50ZW50TWlzbWF0Y2hFcnJvcixcbiAgVHhJbnRlbnRNaXNtYXRjaFJlY2lwaWVudEVycm9yLFxuICBUcmFuc2FjdGlvblBhcmFtcyxcbiAgVHJhbnNhY3Rpb25QcmVidWlsZCBhcyBCYXNlVHJhbnNhY3Rpb25QcmVidWlsZCxcbiAgVHJhbnNhY3Rpb25SZWNpcGllbnQsXG4gIFR5cGVkRGF0YSxcbiAgVW5leHBlY3RlZEFkZHJlc3NFcnJvcixcbiAgVW5zaWduZWRUcmFuc2FjdGlvblRzcyxcbiAgVXRpbCxcbiAgVmVyaWZ5QWRkcmVzc09wdGlvbnMgYXMgQmFzZVZlcmlmeUFkZHJlc3NPcHRpb25zLFxuICBWZXJpZnlUcmFuc2FjdGlvbk9wdGlvbnMsXG4gIFdhbGxldCxcbiAgdmVyaWZ5TVBDV2FsbGV0QWRkcmVzcyxcbiAgZGVyaXZlTVBDV2FsbGV0QWRkcmVzcyxcbiAgVHNzVmVyaWZ5QWRkcmVzc09wdGlvbnMsXG4gIGlzVHNzVmVyaWZ5QWRkcmVzc09wdGlvbnMsXG4gIERlcml2ZUFkZHJlc3NPcHRpb25zLFxuICBEZXJpdmVBZGRyZXNzUmVzdWx0LFxuICBOT19SRUNJUElFTlRfVFhfVFlQRVMsXG4gIENvaW5XaXRoU2lnbmFibGVDb25zaXN0ZW5jeSxcbn0gZnJvbSAnQGJpdGdvLWJldGEvc2RrLWNvcmUnO1xuaW1wb3J0IHsgZ2V0RGVyaXZhdGlvblBhdGggfSBmcm9tICdAYml0Z28tYmV0YS9zZGstbGliLW1wYyc7XG5pbXBvcnQgeyBiaXAzMiB9IGZyb20gJ0BiaXRnby1iZXRhL3NlY3AyNTZrMSc7XG5pbXBvcnQge1xuICBCYXNlQ29pbiBhcyBTdGF0aWNzQmFzZUNvaW4sXG4gIENoYWluSWROb3RGb3VuZEVycm9yLFxuICBDb2luRmVhdHVyZSxcbiAgY29pbnMsXG4gIEVyYzc5ODRDb2luLFxuICBFdGhlcmV1bU5ldHdvcmsgYXMgRXRoTGlrZU5ldHdvcmssXG4gIGV0aEdhc0NvbmZpZ3MsXG59IGZyb20gJ0BiaXRnby1iZXRhL3N0YXRpY3MnO1xuaW1wb3J0IHR5cGUgKiBhcyBFdGhMaWtlQ29tbW9uIGZyb20gJ0BldGhlcmV1bWpzL2NvbW1vbic7XG5pbXBvcnQgdHlwZSAqIGFzIEV0aExpa2VUeExpYiBmcm9tICdAZXRoZXJldW1qcy90eCc7XG5pbXBvcnQgeyBGZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24sIFRyYW5zYWN0aW9uIGFzIExlZ2FjeVRyYW5zYWN0aW9uLCBUcmFuc2FjdGlvbkZhY3RvcnkgfSBmcm9tICdAZXRoZXJldW1qcy90eCc7XG5pbXBvcnQgeyBSTFAgfSBmcm9tICdAZXRoZXJldW1qcy9ybHAnO1xuaW1wb3J0IHsgU2lnblR5cGVkRGF0YVZlcnNpb24sIFR5cGVkRGF0YVV0aWxzLCBUeXBlZE1lc3NhZ2UgfSBmcm9tICdAbWV0YW1hc2svZXRoLXNpZy11dGlsJztcbmltcG9ydCB7IEJpZ051bWJlciB9IGZyb20gJ2JpZ251bWJlci5qcyc7XG5pbXBvcnQgQk4gZnJvbSAnYm4uanMnO1xuaW1wb3J0IHsgcmFuZG9tQnl0ZXMgfSBmcm9tICdjcnlwdG8nO1xuaW1wb3J0IGRlYnVnTGliIGZyb20gJ2RlYnVnJztcbmltcG9ydCB7IGFkZEhleFByZWZpeCwgYnVmQXJyVG9BcnIsIHN0cmlwSGV4UHJlZml4LCBidWZmZXJUb0hleCwgc2V0TGVuZ3RoTGVmdCwgdG9CdWZmZXIgfSBmcm9tICdldGhlcmV1bWpzLXV0aWwnO1xuaW1wb3J0IEtlY2NhayBmcm9tICdrZWNjYWsnO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCBzZWNwMjU2azEgZnJvbSAnc2VjcDI1NmsxJztcblxuaW1wb3J0IHsgQWJzdHJhY3RFdGhMaWtlQ29pbiB9IGZyb20gJy4vYWJzdHJhY3RFdGhMaWtlQ29pbic7XG5pbXBvcnQgeyBFdGhMaWtlVG9rZW4gfSBmcm9tICcuL2V0aExpa2VUb2tlbic7XG5pbXBvcnQge1xuICBjYWxjdWxhdGVGb3J3YXJkZXJWMUFkZHJlc3MsXG4gIGNvaW5GYW1pbGllc1dpdGhMMUZlZXMsXG4gIGFwcHJvdmVNZXRob2RJZCxcbiAgZGVjb2RlQXBwcm92ZUNhbGxkYXRhLFxuICBkZWNvZGVOYXRpdmVUcmFuc2ZlckRhdGEsXG4gIGRlY29kZVRyYW5zZmVyRGF0YSxcbiAgRVJDMTE1NVRyYW5zZmVyQnVpbGRlcixcbiAgRVJDNzIxVHJhbnNmZXJCdWlsZGVyLFxuICBnZXRCdWZmZXJlZEJ5dGVDb2RlLFxuICBnZXRDb21tb24sXG4gIGdldENyZWF0ZUZvcndhcmRlclBhcmFtc0FuZFR5cGVzLFxuICBnZXRQcm94eUluaXRjb2RlLFxuICBnZXRSYXdEZWNvZGVkLFxuICBnZXRUb2tlbixcbiAgS2V5UGFpciBhcyBLZXlQYWlyTGliLFxuICBzZW5kTXVsdGlzaWdNZXRob2RJZCxcbiAgVHJhbnNhY3Rpb25CdWlsZGVyLFxuICBUcmFuc2ZlckJ1aWxkZXIsXG59IGZyb20gJy4vbGliJztcbmltcG9ydCB7IFNlbmRDcm9zc0NoYWluUmVjb3ZlcnlPcHRpb25zIH0gZnJvbSAnLi90eXBlcyc7XG5cbi8qKlxuICogVGhlIHByZWJ1aWx0IGhvcCB0cmFuc2FjdGlvbiByZXR1cm5lZCBmcm9tIHRoZSBIU01cbiAqL1xuaW50ZXJmYWNlIEhvcFByZWJ1aWxkIHtcbiAgdHg6IHN0cmluZztcbiAgaWQ6IHN0cmluZztcbiAgc2lnbmF0dXJlOiBzdHJpbmc7XG4gIHBheW1lbnRJZDogc3RyaW5nO1xuICBnYXNQcmljZTogbnVtYmVyO1xuICBnYXNMaW1pdDogbnVtYmVyO1xuICBhbW91bnQ6IG51bWJlcjtcbiAgcmVjaXBpZW50OiBzdHJpbmc7XG4gIG5vbmNlOiBudW1iZXI7XG4gIHVzZXJSZXFTaWc6IHN0cmluZztcbiAgZ2FzUHJpY2VNYXg6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBUaGUgZXh0cmEgcGFyYW1ldGVycyB0byBzZW5kIHRvIHBsYXRmb3JtIGJ1aWxkIHJvdXRlIGZvciBob3AgdHJhbnNhY3Rpb25zXG4gKi9cbmludGVyZmFjZSBIb3BQYXJhbXMge1xuICBob3BQYXJhbXM6IHtcbiAgICBnYXNQcmljZU1heDogbnVtYmVyO1xuICAgIHVzZXJSZXFTaWc6IHN0cmluZztcbiAgICBwYXltZW50SWQ6IHN0cmluZztcbiAgICBnYXNMaW1pdDogbnVtYmVyO1xuICB9O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEVJUDE1NTkge1xuICBtYXhQcmlvcml0eUZlZVBlckdhczogbnVtYmVyO1xuICBtYXhGZWVQZXJHYXM6IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyB7XG4gIGNoYWluOiBzdHJpbmcgfCBudW1iZXI7XG4gIGhhcmRmb3JrOiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVHJhbnNhY3Rpb25QcmVidWlsZCBleHRlbmRzIEJhc2VUcmFuc2FjdGlvblByZWJ1aWxkIHtcbiAgaG9wVHJhbnNhY3Rpb24/OiBIb3BQcmVidWlsZDtcbiAgYnVpbGRQYXJhbXM6IHtcbiAgICByZWNpcGllbnRzOiBSZWNpcGllbnRbXTtcbiAgfTtcbiAgcmVjaXBpZW50czogVHJhbnNhY3Rpb25SZWNpcGllbnRbXTtcbiAgbmV4dENvbnRyYWN0U2VxdWVuY2VJZDogbnVtYmVyO1xuICBnYXNQcmljZTogbnVtYmVyO1xuICBnYXNMaW1pdDogbnVtYmVyO1xuICBpc0JhdGNoOiBib29sZWFuO1xuICBjb2luOiBzdHJpbmc7XG4gIHRva2VuPzogc3RyaW5nO1xuICB0eEluZm8/OiB7XG4gICAgcmVjaXBpZW50cz86IFRyYW5zYWN0aW9uUmVjaXBpZW50W107XG4gICAgbmV4dENvbnRyYWN0U2VxdWVuY2VJZD86IG51bWJlcjtcbiAgICBlaXAxNTU5PzogRUlQMTU1OTtcbiAgICBpc0JhdGNoPzogYm9vbGVhbjtcbiAgfTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTaWduRmluYWxPcHRpb25zIHtcbiAgdHhQcmVidWlsZDoge1xuICAgIGVpcDE1NTk/OiBFSVAxNTU5O1xuICAgIHJlcGxheVByb3RlY3Rpb25PcHRpb25zPzogUmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM7XG4gICAgZ2FzUHJpY2U/OiBzdHJpbmc7XG4gICAgZ2FzTGltaXQ/OiBzdHJpbmc7XG4gICAgcmVjaXBpZW50cz86IFJlY2lwaWVudFtdO1xuICAgIGhhbGZTaWduZWQ/OiB7XG4gICAgICBleHBpcmVUaW1lOiBudW1iZXI7XG4gICAgICBjb250cmFjdFNlcXVlbmNlSWQ6IG51bWJlcjtcbiAgICAgIGJhY2t1cEtleU5vbmNlPzogbnVtYmVyO1xuICAgICAgc2lnbmF0dXJlOiBzdHJpbmc7XG4gICAgICB0eEhleD86IHN0cmluZztcbiAgICB9O1xuICAgIG5leHRDb250cmFjdFNlcXVlbmNlSWQ/OiBudW1iZXI7XG4gICAgaG9wVHJhbnNhY3Rpb24/OiBzdHJpbmc7XG4gICAgYmFja3VwS2V5Tm9uY2U/OiBudW1iZXI7XG4gICAgaXNCYXRjaD86IGJvb2xlYW47XG4gICAgdHhIZXg/OiBzdHJpbmc7XG4gICAgZXhwaXJlVGltZT86IG51bWJlcjtcbiAgICB0eEluZm8/OiB7XG4gICAgICByZWNpcGllbnRzPzogVHJhbnNhY3Rpb25SZWNpcGllbnRbXTtcbiAgICAgIG5leHRDb250cmFjdFNlcXVlbmNlSWQ/OiBudW1iZXI7XG4gICAgICBlaXAxNTU5PzogRUlQMTU1OTtcbiAgICAgIGlzQmF0Y2g/OiBib29sZWFuO1xuICAgIH07XG4gIH07XG4gIHNpZ25pbmdLZXlOb25jZT86IG51bWJlcjtcbiAgd2FsbGV0Q29udHJhY3RBZGRyZXNzPzogc3RyaW5nO1xuICBwcnY6IHN0cmluZztcbiAgcmVjaXBpZW50cz86IFJlY2lwaWVudFtdO1xuICBjb21tb24/OiBFdGhMaWtlQ29tbW9uLmRlZmF1bHQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2lnblRyYW5zYWN0aW9uT3B0aW9ucyBleHRlbmRzIEJhc2VTaWduVHJhbnNhY3Rpb25PcHRpb25zLCBTaWduRmluYWxPcHRpb25zIHtcbiAgaXNMYXN0U2lnbmF0dXJlPzogYm9vbGVhbjtcbiAgZXhwaXJlVGltZT86IG51bWJlcjtcbiAgc2VxdWVuY2VJZD86IG51bWJlcjtcbiAgZ2FzTGltaXQ/OiBudW1iZXI7XG4gIGdhc1ByaWNlPzogbnVtYmVyO1xuICBjdXN0b2RpYW5UcmFuc2FjdGlvbklkPzogc3RyaW5nO1xuICBjb21tb24/OiBFdGhMaWtlQ29tbW9uLmRlZmF1bHQ7XG4gIHdhbGxldFZlcnNpb24/OiBudW1iZXI7XG59XG5cbmV4cG9ydCB0eXBlIFNpZ25lZFRyYW5zYWN0aW9uID0gSGFsZlNpZ25lZFRyYW5zYWN0aW9uIHwgRnVsbHlTaWduZWRUcmFuc2FjdGlvbjtcblxuZXhwb3J0IGludGVyZmFjZSBGZWVzVXNlZCB7XG4gIGdhc1ByaWNlOiBudW1iZXI7XG4gIGdhc0xpbWl0OiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBQcmVjcmVhdGVCaXRHb09wdGlvbnMge1xuICBlbnRlcnByaXNlPzogc3RyaW5nO1xuICBuZXdGZWVBZGRyZXNzPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE9mZmxpbmVWYXVsdFR4SW5mbyB7XG4gIG5leHRDb250cmFjdFNlcXVlbmNlSWQ/OiBzdHJpbmc7XG4gIGNvbnRyYWN0U2VxdWVuY2VJZD86IHN0cmluZztcbiAgdHg/OiBzdHJpbmc7XG4gIHR4SGV4Pzogc3RyaW5nO1xuICB1c2VyS2V5Pzogc3RyaW5nO1xuICBiYWNrdXBLZXk/OiBzdHJpbmc7XG4gIGNvaW46IHN0cmluZztcbiAgZ2FzUHJpY2U6IG51bWJlcjtcbiAgZ2FzTGltaXQ6IG51bWJlcjtcbiAgcmVjaXBpZW50czogUmVjaXBpZW50W107XG4gIHdhbGxldENvbnRyYWN0QWRkcmVzczogc3RyaW5nO1xuICBhbW91bnQ6IHN0cmluZztcbiAgYmFja3VwS2V5Tm9uY2U6IG51bWJlcjtcbiAgLy8gRm9yIEV0aCBTcGVjaWZpYyBDb2luc1xuICBlaXAxNTU5PzogRUlQMTU1OTtcbiAgcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM/OiBSZXBsYXlQcm90ZWN0aW9uT3B0aW9ucztcbiAgLy8gRm9yIEhvdCBXYWxsZXQgRXZtQmFzZWRDcm9zc0NoYWluUmVjb3ZlcnkgU3BlY2lmaWNcbiAgaGFsZlNpZ25lZD86IEhhbGZTaWduZWRUcmFuc2FjdGlvbjtcbiAgZmVlc1VzZWQ/OiBGZWVzVXNlZDtcbiAgaXNFdm1CYXNlZENyb3NzQ2hhaW5SZWNvdmVyeT86IGJvb2xlYW47XG4gIHdhbGxldFZlcnNpb24/OiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBVbmZvcm1hdHRlZFR4SW5mbyB7XG4gIHJlY2lwaWVudDogUmVjaXBpZW50O1xufVxuXG5leHBvcnQgdHlwZSBVbnNpZ25lZFN3ZWVwVHhNUEN2MiA9IHtcbiAgdHhSZXF1ZXN0czoge1xuICAgIHRyYW5zYWN0aW9uczogW1xuICAgICAge1xuICAgICAgICB1bnNpZ25lZFR4OiBVbnNpZ25lZFRyYW5zYWN0aW9uVHNzO1xuICAgICAgICBub25jZTogbnVtYmVyO1xuICAgICAgICBzaWduYXR1cmVTaGFyZXM6IFtdO1xuICAgICAgfVxuICAgIF07XG4gICAgd2FsbGV0Q29pbjogc3RyaW5nO1xuICB9W107XG59O1xuXG5leHBvcnQgdHlwZSBVbnNpZ25lZEJ1aWxDb25zb2xpZGF0aW9uID0ge1xuICB0cmFuc2FjdGlvbnM6IE1QQ1N3ZWVwVHhzW10gfCBVbnNpZ25lZFN3ZWVwVHhNUEN2MltdIHwgUmVjb3ZlcnlJbmZvW10gfCBPZmZsaW5lVmF1bHRUeEluZm9bXTtcbiAgbGFzdFNjYW5JbmRleDogbnVtYmVyO1xufTtcblxuZXhwb3J0IHR5cGUgUmVjb3Zlck9wdGlvbnNXaXRoQnl0ZXMgPSB7XG4gIGlzVHNzOiB0cnVlO1xuICAvKipcbiAgICogQGRlcHJlY2F0ZWQgdGhpcyBpcyBubyBsb25nZXIgdXNlZFxuICAgKi9cbiAgb3BlblNTTEJ5dGVzPzogVWludDhBcnJheTtcbn07XG5cbmV4cG9ydCB0eXBlIE5vblRTU1JlY292ZXJPcHRpb25zID0ge1xuICBpc1Rzcz86IGZhbHNlIHwgdW5kZWZpbmVkO1xufTtcblxuZXhwb3J0IHR5cGUgVFNTUmVjb3Zlck9wdGlvbnMgPSBSZWNvdmVyT3B0aW9uc1dpdGhCeXRlcyB8IE5vblRTU1JlY292ZXJPcHRpb25zO1xuXG5leHBvcnQgdHlwZSBSZWNvdmVyT3B0aW9ucyA9IHtcbiAgdXNlcktleTogc3RyaW5nO1xuICBiYWNrdXBLZXk6IHN0cmluZztcbiAgd2FsbGV0UGFzc3BocmFzZT86IHN0cmluZztcbiAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiBzdHJpbmc7IC8vIHVzZSB0aGlzIGFzIHdhbGxldEJhc2VBZGRyZXNzIGZvciBUU1NcbiAgcmVjb3ZlcnlEZXN0aW5hdGlvbjogc3RyaW5nO1xuICBrcnNQcm92aWRlcj86IHN0cmluZztcbiAgZ2FzUHJpY2U/OiBudW1iZXI7XG4gIGdhc0xpbWl0PzogbnVtYmVyO1xuICBlaXAxNTU5PzogRUlQMTU1OTtcbiAgcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM/OiBSZXBsYXlQcm90ZWN0aW9uT3B0aW9ucztcbiAgYml0Z29GZWVBZGRyZXNzPzogc3RyaW5nO1xuICBiaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcz86IHN0cmluZztcbiAgdG9rZW5Db250cmFjdEFkZHJlc3M/OiBzdHJpbmc7XG4gIGludGVuZGVkQ2hhaW4/OiBzdHJpbmc7XG4gIGNvbW1vbj86IEV0aExpa2VDb21tb24uZGVmYXVsdDtcbiAgZGVyaXZhdGlvblNlZWQ/OiBzdHJpbmc7XG4gIGFwaUtleT86IHN0cmluZzsgLy8gb3B0aW9uYWwgQVBJIGtleSB0byB1c2UgaW5zdGVhZCBvZiB0aGUgb25lIGZyb20gdGhlIGVudmlyb25tZW50XG4gIGlzVW5zaWduZWRTd2VlcD86IGJvb2xlYW47IC8vIHNwZWNpZnkgaWYgdGhpcyBpcyBhbiB1bnNpZ25lZCByZWNvdmVyeVxuICAvKiogRVJDLTc5ODQ6IHNraXAgYXV0by1xdWVyeSBhbmQgdXNlIHRoaXMgZW5jcnlwdGVkIGJhbGFuY2UgaGFuZGxlIGRpcmVjdGx5IChieXRlczMyLCAweC1wcmVmaXhlZCkgKi9cbiAgZW5jcnlwdGVkSGFuZGxlPzogc3RyaW5nO1xufSAmIFRTU1JlY292ZXJPcHRpb25zO1xuXG5leHBvcnQgdHlwZSBHZXRCYXRjaEV4ZWN1dGlvbkluZm9SVCA9IHtcbiAgdmFsdWVzOiBbc3RyaW5nW10sIHN0cmluZ1tdXTtcbiAgdG90YWxBbW91bnQ6IHN0cmluZztcbn07XG5cbmV4cG9ydCBpbnRlcmZhY2UgQnVpbGRUcmFuc2FjdGlvblBhcmFtcyB7XG4gIHRvOiBzdHJpbmc7XG4gIG5vbmNlPzogbnVtYmVyO1xuICB2YWx1ZTogbnVtYmVyO1xuICBkYXRhPzogQnVmZmVyO1xuICBnYXNQcmljZT86IG51bWJlcjtcbiAgZ2FzTGltaXQ/OiBudW1iZXI7XG4gIGVpcDE1NTk/OiBFSVAxNTU5O1xuICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlY292ZXJ5SW5mbyB7XG4gIGlkOiBzdHJpbmc7XG4gIHR4OiBzdHJpbmc7XG4gIGJhY2t1cEtleT86IHN0cmluZztcbiAgY29pbj86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSZWNvdmVyVG9rZW5UcmFuc2FjdGlvbiB7XG4gIGhhbGZTaWduZWQ6IHtcbiAgICByZWNpcGllbnQ6IFJlY2lwaWVudDtcbiAgICBleHBpcmVUaW1lOiBudW1iZXI7XG4gICAgY29udHJhY3RTZXF1ZW5jZUlkOiBudW1iZXI7XG4gICAgb3BlcmF0aW9uSGFzaDogc3RyaW5nO1xuICAgIHNpZ25hdHVyZTogc3RyaW5nO1xuICAgIGdhc0xpbWl0OiBudW1iZXI7XG4gICAgZ2FzUHJpY2U6IG51bWJlcjtcbiAgICB0b2tlbkNvbnRyYWN0QWRkcmVzczogc3RyaW5nO1xuICAgIHdhbGxldElkOiBzdHJpbmc7XG4gIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVjb3ZlclRva2VuT3B0aW9ucyB7XG4gIHRva2VuQ29udHJhY3RBZGRyZXNzOiBzdHJpbmc7XG4gIHdhbGxldDogV2FsbGV0O1xuICByZWNpcGllbnQ6IHN0cmluZztcbiAgYnJvYWRjYXN0PzogYm9vbGVhbjtcbiAgd2FsbGV0UGFzc3BocmFzZT86IHN0cmluZztcbiAgcHJ2Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEdldFNlbmRNZXRob2RBcmdzT3B0aW9ucyB7XG4gIHJlY2lwaWVudDogUmVjaXBpZW50O1xuICBleHBpcmVUaW1lOiBudW1iZXI7XG4gIGNvbnRyYWN0U2VxdWVuY2VJZDogbnVtYmVyO1xuICBzaWduYXR1cmU6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTZW5kTWV0aG9kQXJncyB7XG4gIG5hbWU6IHN0cmluZztcbiAgdHlwZTogc3RyaW5nO1xuICB2YWx1ZTogYW55O1xufVxuXG5pbnRlcmZhY2UgSG9wVHJhbnNhY3Rpb25CdWlsZE9wdGlvbnMge1xuICB3YWxsZXQ6IFdhbGxldDtcbiAgcmVjaXBpZW50czogUmVjaXBpZW50W107XG4gIHdhbGxldFBhc3NwaHJhc2U6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBCdWlsZE9wdGlvbnMge1xuICBob3A/OiBib29sZWFuO1xuICB3YWxsZXQ/OiBXYWxsZXQ7XG4gIHJlY2lwaWVudHM/OiBSZWNpcGllbnRbXTtcbiAgd2FsbGV0UGFzc3BocmFzZT86IHN0cmluZztcbiAgW2luZGV4OiBzdHJpbmddOiB1bmtub3duO1xufVxuXG5pbnRlcmZhY2UgRmVlRXN0aW1hdGUge1xuICBnYXNMaW1pdEVzdGltYXRlOiBudW1iZXI7XG4gIGZlZUVzdGltYXRlOiBudW1iZXI7XG59XG5cbi8vIFRPRE86IFRoaXMgaW50ZXJmYWNlIHdpbGwgbmVlZCB0byBiZSB1cGRhdGVkIGZvciB0aGUgbmV3IGZlZSBtb2RlbCBpbnRyb2R1Y2VkIGluIHRoZSBMb25kb24gSGFyZCBGb3JrXG5pbnRlcmZhY2UgRXRoVHJhbnNhY3Rpb25QYXJhbXMgZXh0ZW5kcyBUcmFuc2FjdGlvblBhcmFtcyB7XG4gIGdhc1ByaWNlPzogbnVtYmVyO1xuICBnYXNMaW1pdD86IG51bWJlcjtcbiAgaG9wUGFyYW1zPzogSG9wUGFyYW1zO1xuICBob3A/OiBib29sZWFuO1xuICBwcmVidWlsZFR4PzogUHJlYnVpbGRUcmFuc2FjdGlvblJlc3VsdDtcbiAgdG9rZW5OYW1lPzogc3RyaW5nO1xuICBmZWVUb2tlbj86IHN0cmluZztcbiAgZGVmaVBhcmFtcz86IERlZmlJbnRlbnRQYXJhbXM7XG4gIC8qKiBFUkMtNzk4NCBzaGllbGQgaW50ZW50IChgdHlwZTogJ3dyYXBBcHByb3ZlJyB8ICd3cmFwJ2ApOiB3cmFwcGVyIGNvaW4gbmFtZSBhbmQgZXhhY3QgYW1vdW50LiAqL1xuICB3cmFwUGFyYW1zPzogeyB0b2tlbk5hbWU6IHN0cmluZzsgYW1vdW50OiBzdHJpbmcgfTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBWZXJpZnlFdGhUcmFuc2FjdGlvbk9wdGlvbnMgZXh0ZW5kcyBWZXJpZnlUcmFuc2FjdGlvbk9wdGlvbnMge1xuICB0eFByZWJ1aWxkOiBUcmFuc2FjdGlvblByZWJ1aWxkO1xuICB0eFBhcmFtczogRXRoVHJhbnNhY3Rpb25QYXJhbXM7XG59XG5cbmludGVyZmFjZSBQcmVzaWduVHJhbnNhY3Rpb25PcHRpb25zIGV4dGVuZHMgVHJhbnNhY3Rpb25QcmVidWlsZCwgQmFzZVByZXNpZ25UcmFuc2FjdGlvbk9wdGlvbnMge1xuICB3YWxsZXQ6IFdhbGxldDtcbn1cblxuaW50ZXJmYWNlIEV0aEFkZHJlc3NDb2luU3BlY2lmaWNzIGV4dGVuZHMgQWRkcmVzc0NvaW5TcGVjaWZpYyB7XG4gIGZvcndhcmRlclZlcnNpb246IG51bWJlcjtcbiAgc2FsdD86IHN0cmluZztcbiAgZmVlQWRkcmVzcz86IHN0cmluZztcbn1cblxuZXhwb3J0IGNvbnN0IERFRkFVTFRfU0NBTl9GQUNUT1IgPSAyMDtcbmV4cG9ydCBpbnRlcmZhY2UgRXRoQ29uc29saWRhdGlvblJlY292ZXJ5T3B0aW9ucyB7XG4gIGNvaW5OYW1lPzogc3RyaW5nO1xuICB3YWxsZXRDb250cmFjdEFkZHJlc3M/OiBzdHJpbmc7XG4gIGFwaUtleT86IHN0cmluZztcbiAgaXNUc3M/OiBib29sZWFuO1xuICB1c2VyS2V5Pzogc3RyaW5nO1xuICBiYWNrdXBLZXk/OiBzdHJpbmc7XG4gIHdhbGxldFBhc3NwaHJhc2U/OiBzdHJpbmc7XG4gIHJlY292ZXJ5RGVzdGluYXRpb24/OiBzdHJpbmc7XG4gIGtyc1Byb3ZpZGVyPzogc3RyaW5nO1xuICBnYXNQcmljZT86IG51bWJlcjtcbiAgZ2FzTGltaXQ/OiBudW1iZXI7XG4gIGVpcDE1NTk/OiBFSVAxNTU5O1xuICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zO1xuICBiaXRnb0ZlZUFkZHJlc3M/OiBzdHJpbmc7XG4gIGJpdGdvRGVzdGluYXRpb25BZGRyZXNzPzogc3RyaW5nO1xuICB0b2tlbkNvbnRyYWN0QWRkcmVzcz86IHN0cmluZztcbiAgaW50ZW5kZWRDaGFpbj86IHN0cmluZztcbiAgY29tbW9uPzogRXRoTGlrZUNvbW1vbi5kZWZhdWx0O1xuICBkZXJpdmF0aW9uU2VlZD86IHN0cmluZztcbiAgYml0Z29LZXk/OiBzdHJpbmc7XG4gIHN0YXJ0aW5nU2NhbkluZGV4PzogbnVtYmVyO1xuICBlbmRpbmdTY2FuSW5kZXg/OiBudW1iZXI7XG4gIGlnbm9yZUFkZHJlc3NUeXBlcz86IHVua25vd247XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVmVyaWZ5RXRoQWRkcmVzc09wdGlvbnMgZXh0ZW5kcyBCYXNlVmVyaWZ5QWRkcmVzc09wdGlvbnMge1xuICBiYXNlQWRkcmVzczogc3RyaW5nO1xuICBjb2luU3BlY2lmaWM6IEV0aEFkZHJlc3NDb2luU3BlY2lmaWNzO1xuICBmb3J3YXJkZXJWZXJzaW9uPzogbnVtYmVyO1xuICB3YWxsZXRWZXJzaW9uPzogbnVtYmVyO1xufVxuXG5leHBvcnQgdHlwZSBUc3NWZXJpZnlFdGhBZGRyZXNzT3B0aW9ucyA9IFRzc1ZlcmlmeUFkZHJlc3NPcHRpb25zICYgVmVyaWZ5RXRoQWRkcmVzc09wdGlvbnM7XG5cbi8qKlxuICogS2V5Y2hhaW4gd2l0aCBldGhBZGRyZXNzIGZvciBCSVAzMiB3YWxsZXQgdmVyaWZpY2F0aW9uIChWMSwgVjIsIFY0KVxuICogVXNlZCBmb3Igd2FsbGV0cyB0aGF0IGRlcml2ZSBhZGRyZXNzZXMgdXNpbmcgRXRoZXJldW0gYWRkcmVzc2VzIGZyb20ga2V5Y2hhaW5zXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgS2V5Y2hhaW5XaXRoRXRoQWRkcmVzcyB7XG4gIGV0aEFkZHJlc3M6IHN0cmluZztcbiAgcHViOiBzdHJpbmc7XG59XG5cbi8qKlxuICogQklQMzIgd2FsbGV0IGJhc2UgYWRkcmVzcyB2ZXJpZmljYXRpb24gb3B0aW9uc1xuICogU3VwcG9ydHMgVjEsIFYyLCBhbmQgVjQgd2FsbGV0cyB0aGF0IHVzZSBldGhBZGRyZXNzLWJhc2VkIGRlcml2YXRpb25cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBWZXJpZnlDb250cmFjdEJhc2VBZGRyZXNzT3B0aW9ucyBleHRlbmRzIFZlcmlmeUV0aEFkZHJlc3NPcHRpb25zIHtcbiAgd2FsbGV0VmVyc2lvbjogbnVtYmVyO1xuICBrZXljaGFpbnM6IEtleWNoYWluV2l0aEV0aEFkZHJlc3NbXTtcbn1cblxuLyoqXG4gKiBUeXBlIGd1YXJkIHRvIGNoZWNrIGlmIHBhcmFtcyBhcmUgZm9yIEJJUDMyIGJhc2UgYWRkcmVzcyB2ZXJpZmljYXRpb24gKFYxLCBWMiwgVjQpXG4gKiBUaGVzZSB3YWxsZXQgdmVyc2lvbnMgdXNlIGV0aEFkZHJlc3MgZm9yIGFkZHJlc3MgZGVyaXZhdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNWZXJpZnlDb250cmFjdEJhc2VBZGRyZXNzT3B0aW9ucyhcbiAgcGFyYW1zOiBWZXJpZnlFdGhBZGRyZXNzT3B0aW9ucyB8IFRzc1ZlcmlmeUV0aEFkZHJlc3NPcHRpb25zXG4pOiBwYXJhbXMgaXMgVmVyaWZ5Q29udHJhY3RCYXNlQWRkcmVzc09wdGlvbnMge1xuICByZXR1cm4gKFxuICAgIChwYXJhbXMud2FsbGV0VmVyc2lvbiA9PT0gMSB8fCBwYXJhbXMud2FsbGV0VmVyc2lvbiA9PT0gMiB8fCBwYXJhbXMud2FsbGV0VmVyc2lvbiA9PT0gNCkgJiZcbiAgICAna2V5Y2hhaW5zJyBpbiBwYXJhbXMgJiZcbiAgICBBcnJheS5pc0FycmF5KHBhcmFtcy5rZXljaGFpbnMpICYmXG4gICAgcGFyYW1zLmtleWNoYWlucy5sZW5ndGggPT09IDMgJiZcbiAgICBwYXJhbXMua2V5Y2hhaW5zLmV2ZXJ5KChrYzogYW55KSA9PiAnZXRoQWRkcmVzcycgaW4ga2MgJiYgdHlwZW9mIGtjLmV0aEFkZHJlc3MgPT09ICdzdHJpbmcnKVxuICApO1xufVxuXG5jb25zdCBkZWJ1ZyA9IGRlYnVnTGliKCdiaXRnbzp2MjpldGhsaWtlJyk7XG5cbmxldCBlcmM3OTg0V3JhcHBlcnNCeVVuZGVybHlpbmc6IFJlYWRvbmx5TWFwPHN0cmluZywgUmVhZG9ubHlTZXQ8c3RyaW5nPj4gfCB1bmRlZmluZWQ7XG5cbi8qKlxuICogTG93ZXJjYXNlZCB3cmFwcGVyIChzcGVuZGVyKSBhZGRyZXNzZXMgb2YgZXZlcnkgcmVnaXN0ZXJlZCBFUkMtNzk4NCBzaGllbGQgcGFpciwga2V5ZWQgYnkgdGhlIGxvd2VyY2FzZWRcbiAqIGFkZHJlc3Mgb2YgdGhlIHBhaXIncyB1bmRlcmx5aW5nIEVSQy0yMC4gU3RhdGljcyBpcyBzdGF0aWMgZm9yIHRoZSBsaWZldGltZSBvZiB0aGUgcHJvY2Vzcywgc28gdGhpcyBpc1xuICogY29tcHV0ZWQgb25jZS5cbiAqL1xuZnVuY3Rpb24gZ2V0RXJjNzk4NFdyYXBwZXJzQnlVbmRlcmx5aW5nKCk6IFJlYWRvbmx5TWFwPHN0cmluZywgUmVhZG9ubHlTZXQ8c3RyaW5nPj4ge1xuICBpZiAoIWVyYzc5ODRXcmFwcGVyc0J5VW5kZXJseWluZykge1xuICAgIGNvbnN0IGJ5VW5kZXJseWluZyA9IG5ldyBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4oKTtcbiAgICBjb2lucy5mb3JFYWNoKChjb2luKSA9PiB7XG4gICAgICBpZiAoY29pbiBpbnN0YW5jZW9mIEVyYzc5ODRDb2luICYmIGNvaW4udW5kZXJseWluZ0VyYzIwQWRkcmVzcyAmJiBjb2luLmNvbnRyYWN0QWRkcmVzcykge1xuICAgICAgICBjb25zdCB1bmRlcmx5aW5nID0gY29pbi51bmRlcmx5aW5nRXJjMjBBZGRyZXNzLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGNvbnN0IHdyYXBwZXJzID0gYnlVbmRlcmx5aW5nLmdldCh1bmRlcmx5aW5nKSA/PyBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICAgICAgd3JhcHBlcnMuYWRkKGNvaW4uY29udHJhY3RBZGRyZXNzLnRvTG93ZXJDYXNlKCkpO1xuICAgICAgICBieVVuZGVybHlpbmcuc2V0KHVuZGVybHlpbmcsIHdyYXBwZXJzKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBlcmM3OTg0V3JhcHBlcnNCeVVuZGVybHlpbmcgPSBieVVuZGVybHlpbmc7XG4gIH1cbiAgcmV0dXJuIGVyYzc5ODRXcmFwcGVyc0J5VW5kZXJseWluZztcbn1cblxuZXhwb3J0IGNvbnN0IG9wdGlvbmFsRGVwcyA9IHtcbiAgZ2V0IGV0aEFiaSgpIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIHJlcXVpcmUoJ2V0aGVyZXVtanMtYWJpJyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgZGVidWcoJ3VuYWJsZSB0byBsb2FkIGV0aGVyZXVtanMtYWJpOicpO1xuICAgICAgZGVidWcoZS5zdGFjayk7XG4gICAgICB0aHJvdyBuZXcgRXRoZXJldW1MaWJyYXJ5VW5hdmFpbGFibGVFcnJvcihgZXRoZXJldW1qcy1hYmlgKTtcbiAgICB9XG4gIH0sXG5cbiAgZ2V0IGV0aFV0aWwoKSB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiByZXF1aXJlKCdldGhlcmV1bWpzLXV0aWwnKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBkZWJ1ZygndW5hYmxlIHRvIGxvYWQgZXRoZXJldW1qcy11dGlsOicpO1xuICAgICAgZGVidWcoZS5zdGFjayk7XG4gICAgICB0aHJvdyBuZXcgRXRoZXJldW1MaWJyYXJ5VW5hdmFpbGFibGVFcnJvcihgZXRoZXJldW1qcy11dGlsYCk7XG4gICAgfVxuICB9LFxuXG4gIGdldCBFdGhUeCgpOiB0eXBlb2YgRXRoTGlrZVR4TGliIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIHJlcXVpcmUoJ0BldGhlcmV1bWpzL3R4Jyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgZGVidWcoJ3VuYWJsZSB0byBsb2FkIEBldGhlcmV1bWpzL3R4Jyk7XG4gICAgICBkZWJ1ZyhlLnN0YWNrKTtcbiAgICAgIHRocm93IG5ldyBFdGhlcmV1bUxpYnJhcnlVbmF2YWlsYWJsZUVycm9yKGBAZXRoZXJldW1qcy90eGApO1xuICAgIH1cbiAgfSxcblxuICBnZXQgRXRoQ29tbW9uKCk6IHR5cGVvZiBFdGhMaWtlQ29tbW9uIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIHJlcXVpcmUoJ0BldGhlcmV1bWpzL2NvbW1vbicpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGRlYnVnKCd1bmFibGUgdG8gbG9hZCBAZXRoZXJldW1qcy9jb21tb246Jyk7XG4gICAgICBkZWJ1ZyhlLnN0YWNrKTtcbiAgICAgIHRocm93IG5ldyBFdGhlcmV1bUxpYnJhcnlVbmF2YWlsYWJsZUVycm9yKGBAZXRoZXJldW1qcy9jb21tb25gKTtcbiAgICB9XG4gIH0sXG59O1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMgZXh0ZW5kcyBBYnN0cmFjdEV0aExpa2VDb2luIGltcGxlbWVudHMgQ29pbldpdGhTaWduYWJsZUNvbnNpc3RlbmN5IHtcbiAgc3RhdGljIGhvcFRyYW5zYWN0aW9uU2FsdCA9ICdiaXRnb0hvcEFkZHJlc3NSZXF1ZXN0U2FsdCc7XG4gIHByb3RlY3RlZCByZWFkb25seSBzZW5kTWV0aG9kTmFtZTogJ3NlbmRNdWx0aVNpZycgfCAnc2VuZE11bHRpU2lnVG9rZW4nO1xuXG4gIHJlYWRvbmx5IHN0YXRpY3NDb2luPzogUmVhZG9ubHk8U3RhdGljc0Jhc2VDb2luPjtcblxuICBwcm90ZWN0ZWQgY29uc3RydWN0b3IoYml0Z286IEJpdEdvQmFzZSwgc3RhdGljc0NvaW4/OiBSZWFkb25seTxTdGF0aWNzQmFzZUNvaW4+KSB7XG4gICAgc3VwZXIoYml0Z28sIHN0YXRpY3NDb2luKTtcblxuICAgIGlmICghc3RhdGljc0NvaW4pIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyByZXF1aXJlZCBjb25zdHJ1Y3RvciBwYXJhbWV0ZXIgc3RhdGljc0NvaW4nKTtcbiAgICB9XG5cbiAgICB0aGlzLnN0YXRpY3NDb2luID0gc3RhdGljc0NvaW47XG4gICAgdGhpcy5zZW5kTWV0aG9kTmFtZSA9ICdzZW5kTXVsdGlTaWcnO1xuICB9XG5cbiAgLyoqXG4gICAqIE1ldGhvZCB0byByZXR1cm4gdGhlIGNvaW4ncyBuZXR3b3JrIG9iamVjdFxuICAgKiBAcmV0dXJucyB7RXRoTGlrZU5ldHdvcmsgfCB1bmRlZmluZWR9XG4gICAqL1xuICBnZXROZXR3b3JrKCk6IEV0aExpa2VOZXR3b3JrIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5zdGF0aWNzQ29pbj8ubmV0d29yayBhcyBFdGhMaWtlTmV0d29yaztcbiAgfVxuXG4gIC8qKlxuICAgKiBFdmFsdWF0ZXMgd2hldGhlciBhbiBhZGRyZXNzIHN0cmluZyBpcyB2YWxpZCBmb3IgdGhpcyBjb2luXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhZGRyZXNzXG4gICAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIGFkZHJlc3MgaXMgdGhlIHZhbGlkIGV0aGxpa2UgYWRkZXJzc1xuICAgKi9cbiAgaXNWYWxpZEFkZHJlc3MoYWRkcmVzczogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIG9wdGlvbmFsRGVwcy5ldGhVdGlsLmlzVmFsaWRBZGRyZXNzKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmFkZEhleFByZWZpeChhZGRyZXNzKSk7XG4gIH1cblxuICAvKipcbiAgICogRmxhZyBmb3Igc2VuZGluZyBkYXRhIGFsb25nIHdpdGggdHJhbnNhY3Rpb25zXG4gICAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIG9rYXkgdG8gc2VuZCB0eCBkYXRhIChFVEgpLCBmYWxzZSBvdGhlcndpc2VcbiAgICovXG4gIHRyYW5zYWN0aW9uRGF0YUFsbG93ZWQoKSB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKiogQGluaGVyaXREb2MgKi9cbiAgc3VwcG9ydHNNZXNzYWdlU2lnbmluZygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKiBAaW5oZXJpdERvYyAqL1xuICBzdXBwb3J0c1NpZ25pbmdUeXBlZERhdGEoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogRGVmYXVsdCBleHBpcmUgdGltZSBmb3IgYSBjb250cmFjdCBjYWxsICgxIHdlZWspXG4gICAqIEByZXR1cm5zIHtudW1iZXJ9IFRpbWUgaW4gc2Vjb25kc1xuICAgKi9cbiAgZ2V0RGVmYXVsdEV4cGlyZVRpbWUoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gTWF0aC5mbG9vcihuZXcgRGF0ZSgpLmdldFRpbWUoKSAvIDEwMDApICsgNjAgKiA2MCAqIDI0ICogNztcbiAgfVxuXG4gIC8qKlxuICAgKiBNZXRob2QgdG8gZ2V0IHRoZSBjdXN0b20gY2hhaW4gY29tbW9uIG9iamVjdCBiYXNlZCBvbiBwYXJhbXMgZnJvbSByZWNvdmVyeVxuICAgKiBAcGFyYW0ge251bWJlcn0gY2hhaW5JZCAtIHRoZSBjaGFpbiBpZCBvZiB0aGUgY3VzdG9tIGNoYWluXG4gICAqIEByZXR1cm5zIHtFdGhMaWtlQ29tbW9uLmRlZmF1bHR9XG4gICAqL1xuICBzdGF0aWMgZ2V0Q3VzdG9tQ2hhaW5Db21tb24oY2hhaW5JZDogbnVtYmVyKTogRXRoTGlrZUNvbW1vbi5kZWZhdWx0IHtcbiAgICBjb25zdCBjb2luTmFtZSA9IGNvaW5zLmNvaW5OYW1lRnJvbUNoYWluSWQoY2hhaW5JZCk7XG4gICAgaWYgKCFjb2luTmFtZSkge1xuICAgICAgdGhyb3cgbmV3IENoYWluSWROb3RGb3VuZEVycm9yKGNoYWluSWQpO1xuICAgIH1cbiAgICBjb25zdCBjb2luID0gY29pbnMuZ2V0KGNvaW5OYW1lKTtcbiAgICBjb25zdCBldGhMaWtlQ29tbW9uID0gZ2V0Q29tbW9uKGNvaW4ubmV0d29yayBhcyBFdGhMaWtlTmV0d29yayk7XG4gICAgcmV0dXJuIGV0aExpa2VDb21tb247XG4gIH1cblxuICAvKipcbiAgICogR2V0cyBjb3JyZWN0IEV0aCBDb21tb24gb2JqZWN0IGJhc2VkIG9uIHBhcmFtcyBmcm9tIGVpdGhlciByZWNvdmVyeSBvciB0eCBidWlsZGluZ1xuICAgKiBAcGFyYW0ge0VJUDE1NTl9IGVpcDE1NTkgLSBjb25maWdzIHRoYXQgc3BlY2lmeSB3aGV0aGVyIHdlIHNob3VsZCBjb25zdHJ1Y3QgYW4gZWlwMTU1OSB0eFxuICAgKiBAcGFyYW0ge1JlcGxheVByb3RlY3Rpb25PcHRpb25zfSByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyAtIGNoZWNrIGlmIGNoYWluIGlkIHN1cHBvcnRzIHJlcGxheSBwcm90ZWN0aW9uXG4gICAqIEByZXR1cm5zIHtFdGhMaWtlQ29tbW9uLmRlZmF1bHR9XG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBnZXRFdGhMaWtlQ29tbW9uKFxuICAgIGVpcDE1NTk/OiBFSVAxNTU5LFxuICAgIHJlcGxheVByb3RlY3Rpb25PcHRpb25zPzogUmVwbGF5UHJvdGVjdGlvbk9wdGlvbnNcbiAgKTogRXRoTGlrZUNvbW1vbi5kZWZhdWx0IHtcbiAgICAvLyBpZiBlaXAxNTU5IHBhcmFtcyBhcmUgc3BlY2lmaWVkLCBkZWZhdWx0IHRvIGxvbmRvbiBoYXJkZm9yaywgb3RoZXJ3aXNlLFxuICAgIC8vIGRlZmF1bHQgdG8gcGV0ZXJzYnVyZyB0byBhdm9pZCByZXBsYXkgcHJvdGVjdGlvbiBpc3N1ZXNcbiAgICBjb25zdCBkZWZhdWx0SGFyZGZvcmsgPSAhIWVpcDE1NTkgPyAnbG9uZG9uJyA6IG9wdGlvbmFsRGVwcy5FdGhDb21tb24uSGFyZGZvcmsuUGV0ZXJzYnVyZztcbiAgICBjb25zdCBldGhMaWtlQ29tbW9uID0gQWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuZ2V0Q3VzdG9tQ2hhaW5Db21tb24ocmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM/LmNoYWluIGFzIG51bWJlcik7XG4gICAgZXRoTGlrZUNvbW1vbi5zZXRIYXJkZm9yayhyZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz8uaGFyZGZvcmsgPz8gZGVmYXVsdEhhcmRmb3JrKTtcbiAgICByZXR1cm4gZXRoTGlrZUNvbW1vbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBNZXRob2QgdG8gYnVpbGQgdGhlIHR4IG9iamVjdFxuICAgKiBAcGFyYW0ge0J1aWxkVHJhbnNhY3Rpb25QYXJhbXN9IHBhcmFtcyAtIHBhcmFtcyB0byBidWlsZCB0cmFuc2FjdGlvblxuICAgKiBAcmV0dXJucyB7RXRoTGlrZVR4TGliLkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbn1cbiAgICovXG4gIHN0YXRpYyBidWlsZFRyYW5zYWN0aW9uKFxuICAgIHBhcmFtczogQnVpbGRUcmFuc2FjdGlvblBhcmFtc1xuICApOiBFdGhMaWtlVHhMaWIuRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uIHwgRXRoTGlrZVR4TGliLlRyYW5zYWN0aW9uIHtcbiAgICAvLyBpZiBlaXAxNTU5IHBhcmFtcyBhcmUgc3BlY2lmaWVkLCBkZWZhdWx0IHRvIGxvbmRvbiBoYXJkZm9yaywgb3RoZXJ3aXNlLFxuICAgIC8vIGRlZmF1bHQgdG8gdGFuZ2VyaW5lIHdoaXN0bGUgdG8gYXZvaWQgcmVwbGF5IHByb3RlY3Rpb24gaXNzdWVzXG4gICAgY29uc3QgZXRoTGlrZUNvbW1vbiA9IEFic3RyYWN0RXRoTGlrZU5ld0NvaW5zLmdldEV0aExpa2VDb21tb24ocGFyYW1zLmVpcDE1NTksIHBhcmFtcy5yZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyk7XG4gICAgY29uc3QgYmFzZVBhcmFtcyA9IHtcbiAgICAgIHRvOiBwYXJhbXMudG8sXG4gICAgICBub25jZTogcGFyYW1zLm5vbmNlLFxuICAgICAgdmFsdWU6IHBhcmFtcy52YWx1ZSxcbiAgICAgIGRhdGE6IHBhcmFtcy5kYXRhLFxuICAgICAgZ2FzTGltaXQ6IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihwYXJhbXMuZ2FzTGltaXQpLFxuICAgIH07XG5cbiAgICBjb25zdCB1bnNpZ25lZEV0aFR4ID0gISFwYXJhbXMuZWlwMTU1OVxuICAgICAgPyBvcHRpb25hbERlcHMuRXRoVHguRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uLmZyb21UeERhdGEoXG4gICAgICAgICAge1xuICAgICAgICAgICAgLi4uYmFzZVBhcmFtcyxcbiAgICAgICAgICAgIG1heEZlZVBlckdhczogbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHBhcmFtcy5laXAxNTU5Lm1heEZlZVBlckdhcyksXG4gICAgICAgICAgICBtYXhQcmlvcml0eUZlZVBlckdhczogbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHBhcmFtcy5laXAxNTU5Lm1heFByaW9yaXR5RmVlUGVyR2FzKSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHsgY29tbW9uOiBldGhMaWtlQ29tbW9uIH1cbiAgICAgICAgKVxuICAgICAgOiBvcHRpb25hbERlcHMuRXRoVHguVHJhbnNhY3Rpb24uZnJvbVR4RGF0YShcbiAgICAgICAgICB7XG4gICAgICAgICAgICAuLi5iYXNlUGFyYW1zLFxuICAgICAgICAgICAgZ2FzUHJpY2U6IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihwYXJhbXMuZ2FzUHJpY2UpLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgeyBjb21tb246IGV0aExpa2VDb21tb24gfVxuICAgICAgICApO1xuXG4gICAgcmV0dXJuIHVuc2lnbmVkRXRoVHg7XG4gIH1cblxuICAvKipcbiAgICogUXVlcnkgZXhwbG9yZXIgZm9yIHRoZSBiYWxhbmNlIG9mIGFuIGFkZHJlc3NcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFkZHJlc3MgLSB0aGUgRVRITGlrZSBhZGRyZXNzXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhcGlLZXkgLSBvcHRpb25hbCBBUEkga2V5IHRvIHVzZSBpbnN0ZWFkIG9mIHRoZSBvbmUgZnJvbSB0aGUgZW52aXJvbm1lbnRcbiAgICogQHJldHVybnMge0JpZ051bWJlcn0gYWRkcmVzcyBiYWxhbmNlXG4gICAqL1xuICBhc3luYyBxdWVyeUFkZHJlc3NCYWxhbmNlKGFkZHJlc3M6IHN0cmluZywgYXBpS2V5Pzogc3RyaW5nKTogUHJvbWlzZTxhbnk+IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLnJlY292ZXJ5QmxvY2tjaGFpbkV4cGxvcmVyUXVlcnkoXG4gICAgICB7XG4gICAgICAgIGNoYWluaWQ6IHRoaXMuZ2V0Q2hhaW5JZCgpLnRvU3RyaW5nKCksXG4gICAgICAgIG1vZHVsZTogJ2FjY291bnQnLFxuICAgICAgICBhY3Rpb246ICdiYWxhbmNlJyxcbiAgICAgICAgYWRkcmVzczogYWRkcmVzcyxcbiAgICAgIH0sXG4gICAgICBhcGlLZXlcbiAgICApO1xuICAgIC8vIHRocm93IGlmIHRoZSByZXN1bHQgZG9lcyBub3QgZXhpc3Qgb3IgdGhlIHJlc3VsdCBpcyBub3QgYSB2YWxpZCBudW1iZXJcbiAgICBpZiAoIXJlc3VsdCB8fCAhcmVzdWx0LnJlc3VsdCB8fCBpc05hTihyZXN1bHQucmVzdWx0KSkge1xuICAgICAgdGhyb3cgbmV3IFJlY292ZXJ5UHJvdmlkZXJFcnJvcihcbiAgICAgICAgYENvdWxkIG5vdCBvYnRhaW4gYWRkcmVzcyBiYWxhbmNlIGZvciAke2FkZHJlc3N9IGZyb20gdGhlIGV4cGxvcmVyLCBnb3Q6ICR7cmVzdWx0LnJlc3VsdH1gXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHJlc3VsdC5yZXN1bHQsIDEwKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge1JlY2lwaWVudFtdfSByZWNpcGllbnRzIC0gdGhlIHJlY2lwaWVudHMgb2YgdGhlIHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBleHBpcmVUaW1lIC0gdGhlIGV4cGlyZSB0aW1lIG9mIHRoZSB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0ge251bWJlcn0gY29udHJhY3RTZXF1ZW5jZUlkIC0gdGhlIGNvbnRyYWN0IHNlcXVlbmNlIGlkIG9mIHRoZSB0cmFuc2FjdGlvblxuICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgKi9cbiAgZ2V0T3BlcmF0aW9uU2hhM0ZvckV4ZWN1dGVBbmRDb25maXJtKFxuICAgIHJlY2lwaWVudHM6IFJlY2lwaWVudFtdLFxuICAgIGV4cGlyZVRpbWU6IG51bWJlcixcbiAgICBjb250cmFjdFNlcXVlbmNlSWQ6IG51bWJlclxuICApOiBzdHJpbmcge1xuICAgIGlmICghcmVjaXBpZW50cyB8fCAhQXJyYXkuaXNBcnJheShyZWNpcGllbnRzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdleHBlY3RpbmcgYXJyYXkgb2YgcmVjaXBpZW50cycpO1xuICAgIH1cblxuICAgIC8vIFJpZ2h0IG5vdyB3ZSBvbmx5IHN1cHBvcnQgMSByZWNpcGllbnRcbiAgICBpZiAocmVjaXBpZW50cy5sZW5ndGggIT09IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbXVzdCBzZW5kIHRvIGV4YWN0bHkgMSByZWNpcGllbnQnKTtcbiAgICB9XG5cbiAgICBpZiAoIV8uaXNOdW1iZXIoZXhwaXJlVGltZSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignZXhwaXJlVGltZSBtdXN0IGJlIG51bWJlciBvZiBzZWNvbmRzIHNpbmNlIGVwb2NoJyk7XG4gICAgfVxuXG4gICAgaWYgKCFfLmlzTnVtYmVyKGNvbnRyYWN0U2VxdWVuY2VJZCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignY29udHJhY3RTZXF1ZW5jZUlkIG11c3QgYmUgbnVtYmVyJyk7XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgaW5wdXRzXG4gICAgcmVjaXBpZW50cy5mb3JFYWNoKGZ1bmN0aW9uIChyZWNpcGllbnQpIHtcbiAgICAgIGlmIChcbiAgICAgICAgIV8uaXNTdHJpbmcocmVjaXBpZW50LmFkZHJlc3MpIHx8XG4gICAgICAgICFvcHRpb25hbERlcHMuZXRoVXRpbC5pc1ZhbGlkQWRkcmVzcyhvcHRpb25hbERlcHMuZXRoVXRpbC5hZGRIZXhQcmVmaXgocmVjaXBpZW50LmFkZHJlc3MpKVxuICAgICAgKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBhZGRyZXNzOiAnICsgcmVjaXBpZW50LmFkZHJlc3MpO1xuICAgICAgfVxuXG4gICAgICBsZXQgYW1vdW50OiBCaWdOdW1iZXI7XG4gICAgICB0cnkge1xuICAgICAgICBhbW91bnQgPSBuZXcgQmlnTnVtYmVyKHJlY2lwaWVudC5hbW91bnQpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgYW1vdW50IGZvcjogJyArIHJlY2lwaWVudC5hZGRyZXNzICsgJyAtIHNob3VsZCBiZSBudW1lcmljJyk7XG4gICAgICB9XG5cbiAgICAgIHJlY2lwaWVudC5hbW91bnQgPSBhbW91bnQudG9GaXhlZCgwKTtcblxuICAgICAgaWYgKHJlY2lwaWVudC5kYXRhICYmICFfLmlzU3RyaW5nKHJlY2lwaWVudC5kYXRhKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0RhdGEgZm9yIHJlY2lwaWVudCAnICsgcmVjaXBpZW50LmFkZHJlc3MgKyAnIC0gc2hvdWxkIGJlIG9mIHR5cGUgaGV4IHN0cmluZycpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgY29uc3QgcmVjaXBpZW50ID0gcmVjaXBpZW50c1swXTtcbiAgICByZXR1cm4gb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9IZXgoXG4gICAgICBvcHRpb25hbERlcHMuZXRoQWJpLnNvbGlkaXR5U0hBMyguLi50aGlzLmdldE9wZXJhdGlvbihyZWNpcGllbnQsIGV4cGlyZVRpbWUsIGNvbnRyYWN0U2VxdWVuY2VJZCkpXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdHJhbnNmZXIgb3BlcmF0aW9uIGZvciBjb2luXG4gICAqIEBwYXJhbSB7UmVjaXBpZW50fSByZWNpcGllbnQgLSByZWNpcGllbnQgaW5mb1xuICAgKiBAcGFyYW0ge251bWJlcn0gZXhwaXJlVGltZSAtIGV4cGlyeSB0aW1lXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBjb250cmFjdFNlcXVlbmNlSWQgLSBzZXF1ZW5jZSBpZFxuICAgKiBAcmV0dXJucyB7QXJyYXl9IG9wZXJhdGlvbiBhcnJheVxuICAgKi9cbiAgZ2V0T3BlcmF0aW9uKHJlY2lwaWVudDogUmVjaXBpZW50LCBleHBpcmVUaW1lOiBudW1iZXIsIGNvbnRyYWN0U2VxdWVuY2VJZDogbnVtYmVyKTogKHN0cmluZyB8IEJ1ZmZlcilbXVtdIHtcbiAgICBjb25zdCBuZXR3b3JrID0gdGhpcy5nZXROZXR3b3JrKCkgYXMgRXRoTGlrZU5ldHdvcms7XG4gICAgcmV0dXJuIFtcbiAgICAgIFsnc3RyaW5nJywgJ2FkZHJlc3MnLCAndWludCcsICdieXRlcycsICd1aW50JywgJ3VpbnQnXSxcbiAgICAgIFtcbiAgICAgICAgbmV0d29yay5uYXRpdmVDb2luT3BlcmF0aW9uSGFzaFByZWZpeCxcbiAgICAgICAgbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KHJlY2lwaWVudC5hZGRyZXNzKSwgMTYpLFxuICAgICAgICByZWNpcGllbnQuYW1vdW50LFxuICAgICAgICBCdWZmZXIuZnJvbShvcHRpb25hbERlcHMuZXRoVXRpbC5zdHJpcEhleFByZWZpeChvcHRpb25hbERlcHMuZXRoVXRpbC5wYWRUb0V2ZW4ocmVjaXBpZW50LmRhdGEgfHwgJycpKSwgJ2hleCcpLFxuICAgICAgICBleHBpcmVUaW1lLFxuICAgICAgICBjb250cmFjdFNlcXVlbmNlSWQsXG4gICAgICBdLFxuICAgIF07XG4gIH1cblxuICAvKipcbiAgICogUXVlcmllcyB0aGUgY29udHJhY3QgKHZpYSBleHBsb3JlciBBUEkpIGZvciB0aGUgbmV4dCBzZXF1ZW5jZSBJRFxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzcyAtIGFkZHJlc3Mgb2YgdGhlIGNvbnRyYWN0XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhcGlLZXkgLSBvcHRpb25hbCBBUEkga2V5IHRvIHVzZSBpbnN0ZWFkIG9mIHRoZSBvbmUgZnJvbSB0aGUgZW52aXJvbm1lbnRcbiAgICogQHJldHVybnMge1Byb21pc2U8TnVtYmVyPn0gc2VxdWVuY2UgSURcbiAgICovXG4gIGFzeW5jIHF1ZXJ5U2VxdWVuY2VJZChhZGRyZXNzOiBzdHJpbmcsIGFwaUtleT86IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgLy8gR2V0IHNlcXVlbmNlIElEIHVzaW5nIGNvbnRyYWN0IGNhbGxcbiAgICBjb25zdCBzZXF1ZW5jZUlkTWV0aG9kU2lnbmF0dXJlID0gb3B0aW9uYWxEZXBzLmV0aEFiaS5tZXRob2RJRCgnZ2V0TmV4dFNlcXVlbmNlSWQnLCBbXSk7XG4gICAgY29uc3Qgc2VxdWVuY2VJZEFyZ3MgPSBvcHRpb25hbERlcHMuZXRoQWJpLnJhd0VuY29kZShbXSwgW10pO1xuICAgIGNvbnN0IHNlcXVlbmNlSWREYXRhID0gQnVmZmVyLmNvbmNhdChbc2VxdWVuY2VJZE1ldGhvZFNpZ25hdHVyZSwgc2VxdWVuY2VJZEFyZ3NdKS50b1N0cmluZygnaGV4Jyk7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5yZWNvdmVyeUJsb2NrY2hhaW5FeHBsb3JlclF1ZXJ5KFxuICAgICAge1xuICAgICAgICBjaGFpbmlkOiB0aGlzLmdldENoYWluSWQoKS50b1N0cmluZygpLFxuICAgICAgICBtb2R1bGU6ICdwcm94eScsXG4gICAgICAgIGFjdGlvbjogJ2V0aF9jYWxsJyxcbiAgICAgICAgdG86IGFkZHJlc3MsXG4gICAgICAgIGRhdGE6IHNlcXVlbmNlSWREYXRhLFxuICAgICAgICB0YWc6ICdsYXRlc3QnLFxuICAgICAgfSxcbiAgICAgIGFwaUtleVxuICAgICk7XG4gICAgaWYgKCFyZXN1bHQgfHwgIXJlc3VsdC5yZXN1bHQpIHtcbiAgICAgIHRocm93IG5ldyBSZWNvdmVyeVByb3ZpZGVyRXJyb3IoJ0NvdWxkIG5vdCBvYnRhaW4gc2VxdWVuY2UgSUQgZnJvbSBleHBsb3JlciwgZ290OiAnICsgcmVzdWx0LnJlc3VsdCk7XG4gICAgfVxuICAgIGNvbnN0IHNlcXVlbmNlSWRIZXggPSByZXN1bHQucmVzdWx0O1xuICAgIHJldHVybiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4oc2VxdWVuY2VJZEhleC5zbGljZSgyKSwgMTYpLnRvTnVtYmVyKCk7XG4gIH1cblxuICAvKipcbiAgICogUXVlcmllcyB0aGUgd2FsbGV0IGNvbnRyYWN0ICh2aWEgZXhwbG9yZXIgQVBJKSB3aGV0aGVyIGFuIGFkZHJlc3MgaXMgYSBzaWduZXIgb24gaXQuXG4gICAqIFByb2JlcyB0aGUgZXhwbGljaXQgYGlzU2lnbmVyKGFkZHJlc3MpYCBmdW5jdGlvbiBiZWNhdXNlIGl0IGV4aXN0cyBvbiBldmVyeSBkZXBsb3llZFxuICAgKiBXYWxsZXRTaW1wbGUgZ2VuZXJhdGlvbjogdGhlIG5ld2VyIGdlbmVyYXRpb24gZGVjbGFyZXMgaXQgYmVzaWRlIGl0c1xuICAgKiBgbWFwcGluZyhhZGRyZXNzID0+IGJvb2wpIHB1YmxpYyBzaWduZXJzYCwgd2hpbGUgZXRoLW11bHRpc2lnLXYyIChFVEggd2FsbGV0cykgb25seVxuICAgKiBoYXMgYGFkZHJlc3NbXSBwdWJsaWMgc2lnbmVyc2Agd2hvc2UgZ2V0dGVyIHRha2VzIGFuIGluZGV4LiBUaGUgY29udHJhY3RzJ1xuICAgKiBub24tcmV2ZXJ0aW5nIGZhbGxiYWNrIHdvdWxkIHJldHVybiBlbXB0eSBkYXRhIGZvciBhIG1pc3Npbmcgc2VsZWN0b3IsIHNvIHByb2JpbmcgYVxuICAgKiBnZW5lcmF0aW9uLXNwZWNpZmljIGdldHRlciBtdXN0IG5ldmVyIGhhcHBlbi5cbiAgICogQHBhcmFtIHdhbGxldENvbnRyYWN0QWRkcmVzcyAtIGFkZHJlc3Mgb2YgdGhlIHdhbGxldCBjb250cmFjdFxuICAgKiBAcGFyYW0gc2lnbmVyQWRkcmVzcyAtIGFkZHJlc3MgdG8gdGVzdCBmb3IgbWVtYmVyc2hpcFxuICAgKiBAcGFyYW0gYXBpS2V5IC0gb3B0aW9uYWwgQVBJIGtleSB0byB1c2UgaW5zdGVhZCBvZiB0aGUgb25lIGZyb20gdGhlIGVudmlyb25tZW50XG4gICAqIEByZXR1cm5zIHRydWUgaWYgdGhlIGFkZHJlc3MgaXMgYSBzaWduZXIgb24gdGhlIHdhbGxldCBjb250cmFjdFxuICAgKi9cbiAgYXN5bmMgcXVlcnlJc1dhbGxldFNpZ25lcih3YWxsZXRDb250cmFjdEFkZHJlc3M6IHN0cmluZywgc2lnbmVyQWRkcmVzczogc3RyaW5nLCBhcGlLZXk/OiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBzaWduZXJEYXRhID0gdGhpcy5nZXRNZXRob2RDYWxsRGF0YSgnaXNTaWduZXInLCBbJ2FkZHJlc3MnXSwgW3NpZ25lckFkZHJlc3NdKS50b1N0cmluZygnaGV4Jyk7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5yZWNvdmVyeUJsb2NrY2hhaW5FeHBsb3JlclF1ZXJ5KFxuICAgICAge1xuICAgICAgICBjaGFpbmlkOiB0aGlzLmdldENoYWluSWQoKS50b1N0cmluZygpLFxuICAgICAgICBtb2R1bGU6ICdwcm94eScsXG4gICAgICAgIGFjdGlvbjogJ2V0aF9jYWxsJyxcbiAgICAgICAgdG86IHdhbGxldENvbnRyYWN0QWRkcmVzcyxcbiAgICAgICAgZGF0YTogc2lnbmVyRGF0YSxcbiAgICAgICAgdGFnOiAnbGF0ZXN0JyxcbiAgICAgIH0sXG4gICAgICBhcGlLZXlcbiAgICApO1xuICAgIGlmICghcmVzdWx0IHx8ICFyZXN1bHQucmVzdWx0IHx8IG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KHJlc3VsdC5yZXN1bHQpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgQ291bGQgbm90IHJlYWQgdGhlIHNpZ25lciBzZXQgb2Ygd2FsbGV0IGNvbnRyYWN0ICR7d2FsbGV0Q29udHJhY3RBZGRyZXNzfSBmcm9tIHRoZSBleHBsb3JlciwgZ290OiAke3Jlc3VsdD8ucmVzdWx0fWBcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiBvcHRpb25hbERlcHMuZXRoQWJpLnJhd0RlY29kZShbJ2Jvb2wnXSwgb3B0aW9uYWxEZXBzLmV0aFV0aWwudG9CdWZmZXIocmVzdWx0LnJlc3VsdCkpWzBdID09PSB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIEFzc2VydHMgdGhlIHVzZXIgYW5kIGJhY2t1cCByZWNvdmVyeSBrZXlzIGFyZSBzaWduZXJzIG9uIHRoZSB3YWxsZXQgY29udHJhY3QuXG4gICAqIFJ1bnMgYmVmb3JlIGFueSBiYWxhbmNlL2ZlZSBjaGVjayBzbyB3cm9uZyBwYWlyaW5ncyBmYWlsIGNsb3NlZCB3aXRoIFJlY292ZXJ5S2V5TWlzbWF0Y2hFcnJvci5cbiAgICovXG4gIHByb3RlY3RlZCBhc3luYyBhc3NlcnRSZWNvdmVyeUtleXNBcmVXYWxsZXRTaWduZXJzKFxuICAgIHVzZXJLZXk6IHN0cmluZyxcbiAgICBiYWNrdXBLZXlBZGRyZXNzOiBzdHJpbmcsXG4gICAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiBzdHJpbmcsXG4gICAgYXBpS2V5Pzogc3RyaW5nXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHVzZXJLZXlBZGRyZXNzID0gYDB4JHtvcHRpb25hbERlcHMuZXRoVXRpbFxuICAgICAgLnB1YmxpY1RvQWRkcmVzcyhiaXAzMi5mcm9tQmFzZTU4KHVzZXJLZXkpLnB1YmxpY0tleSwgdHJ1ZSlcbiAgICAgIC50b1N0cmluZygnaGV4Jyl9YDtcbiAgICBjb25zdCBpc1VzZXJTaWduZXIgPSBhd2FpdCB0aGlzLnF1ZXJ5SXNXYWxsZXRTaWduZXIod2FsbGV0Q29udHJhY3RBZGRyZXNzLCB1c2VyS2V5QWRkcmVzcywgYXBpS2V5KTtcbiAgICAvLyB3ZSBuZWVkIHRvIHdhaXQgYmV0d2VlbiBtYWtpbmcgdHdvIGV4cGxvcmVyIGFwaSBjYWxscyB0byBhdm9pZCBnZXR0aW5nIGJhbm5lZFxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDEwMDApKTtcbiAgICBjb25zdCBpc0JhY2t1cFNpZ25lciA9IGF3YWl0IHRoaXMucXVlcnlJc1dhbGxldFNpZ25lcih3YWxsZXRDb250cmFjdEFkZHJlc3MsIGJhY2t1cEtleUFkZHJlc3MsIGFwaUtleSk7XG4gICAgY29uc3QgbWlzc2luZ1NpZ25lcnMgPSBbXG4gICAgICAuLi4oaXNVc2VyU2lnbmVyID8gW10gOiBbYHVzZXIga2V5ICR7dXNlcktleUFkZHJlc3N9YF0pLFxuICAgICAgLi4uKGlzQmFja3VwU2lnbmVyID8gW10gOiBbYGJhY2t1cCBrZXkgJHtiYWNrdXBLZXlBZGRyZXNzfWBdKSxcbiAgICBdO1xuICAgIGlmIChtaXNzaW5nU2lnbmVycy5sZW5ndGggPiAwKSB7XG4gICAgICB0aHJvdyBuZXcgUmVjb3ZlcnlLZXlNaXNtYXRjaEVycm9yKFxuICAgICAgICBgJHttaXNzaW5nU2lnbmVycy5qb2luKCcgYW5kICcpfSAke1xuICAgICAgICAgIG1pc3NpbmdTaWduZXJzLmxlbmd0aCA9PT0gMSA/ICdpcyBub3QgYSBzaWduZXInIDogJ2FyZSBub3Qgc2lnbmVycydcbiAgICAgICAgfSBvZiB3YWxsZXQgY29udHJhY3QgJHt3YWxsZXRDb250cmFjdEFkZHJlc3N9YFxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVjb3ZlciBhbiB1bnN1cHBvcnRlZCB0b2tlbiBmcm9tIGEgQml0R28gbXVsdGlzaWcgd2FsbGV0XG4gICAqIFRoaXMgYnVpbGRzIGEgaGFsZi1zaWduZWQgdHJhbnNhY3Rpb24sIGZvciB3aGljaCB0aGVyZSB3aWxsIGJlIGFuIGFkbWluIHJvdXRlIHRvIGNvLXNpZ24gYW5kIGJyb2FkY2FzdC4gT3B0aW9uYWxseVxuICAgKiB0aGUgdXNlciBjYW4gc2V0IHBhcmFtcy5icm9hZGNhc3QgPSB0cnVlIGFuZCB0aGUgaGFsZi1zaWduZWQgdHggd2lsbCBiZSBzZW50IHRvIEJpdEdvIGZvciBjb3NpZ25pbmcgYW5kIGJyb2FkY2FzdGluZ1xuICAgKiBAcGFyYW0ge1JlY292ZXJUb2tlbk9wdGlvbnN9IHBhcmFtc1xuICAgKiBAcGFyYW0ge1dhbGxldH0gcGFyYW1zLndhbGxldCAtIHRoZSB3YWxsZXQgdG8gcmVjb3ZlciB0aGUgdG9rZW4gZnJvbVxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzIC0gdGhlIGNvbnRyYWN0IGFkZHJlc3Mgb2YgdGhlIHVuc3VwcG9ydGVkIHRva2VuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMucmVjaXBpZW50IC0gdGhlIGRlc3RpbmF0aW9uIGFkZHJlc3MgcmVjb3ZlcmVkIHRva2VucyBzaG91bGQgYmUgc2VudCB0b1xuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLndhbGxldFBhc3NwaHJhc2UgLSB0aGUgd2FsbGV0IHBhc3NwaHJhc2VcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5wcnYgLSB0aGUgeHBydlxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IHBhcmFtcy5icm9hZGNhc3QgLSBpZiB0cnVlLCB3ZSB3aWxsIGF1dG9tYXRpY2FsbHkgc3VibWl0IHRoZSBoYWxmLXNpZ25lZCB0eCB0byBCaXRHbyBmb3IgY29zaWduaW5nIGFuZCBicm9hZGNhc3RpbmdcbiAgICogQHJldHVybnMge1Byb21pc2U8UmVjb3ZlclRva2VuVHJhbnNhY3Rpb24+fVxuICAgKi9cbiAgYXN5bmMgcmVjb3ZlclRva2VuKHBhcmFtczogUmVjb3ZlclRva2VuT3B0aW9ucyk6IFByb21pc2U8UmVjb3ZlclRva2VuVHJhbnNhY3Rpb24+IHtcbiAgICBjb25zdCBuZXR3b3JrID0gdGhpcy5nZXROZXR3b3JrKCkgYXMgRXRoTGlrZU5ldHdvcms7XG4gICAgaWYgKCFfLmlzT2JqZWN0KHBhcmFtcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgcmVjb3ZlclRva2VuIG11c3QgYmUgcGFzc2VkIGEgcGFyYW1zIG9iamVjdC4gR290ICR7cGFyYW1zfSAodHlwZSAke3R5cGVvZiBwYXJhbXN9KWApO1xuICAgIH1cblxuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcykgfHwgIV8uaXNTdHJpbmcocGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgdG9rZW5Db250cmFjdEFkZHJlc3MgbXVzdCBiZSBhIHN0cmluZywgZ290ICR7XG4gICAgICAgICAgcGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzXG4gICAgICAgIH0gKHR5cGUgJHt0eXBlb2YgcGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzfSlgXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5pc1ZhbGlkQWRkcmVzcyhwYXJhbXMudG9rZW5Db250cmFjdEFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3Rva2VuQ29udHJhY3RBZGRyZXNzIG5vdCBhIHZhbGlkIGFkZHJlc3MnKTtcbiAgICB9XG5cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMud2FsbGV0KSB8fCAhKHBhcmFtcy53YWxsZXQgaW5zdGFuY2VvZiBXYWxsZXQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYHdhbGxldCBtdXN0IGJlIGEgd2FsbGV0IGluc3RhbmNlLCBnb3QgJHtwYXJhbXMud2FsbGV0fSAodHlwZSAke3R5cGVvZiBwYXJhbXMud2FsbGV0fSlgKTtcbiAgICB9XG5cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMucmVjaXBpZW50KSB8fCAhXy5pc1N0cmluZyhwYXJhbXMucmVjaXBpZW50KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGByZWNpcGllbnQgbXVzdCBiZSBhIHN0cmluZywgZ290ICR7cGFyYW1zLnJlY2lwaWVudH0gKHR5cGUgJHt0eXBlb2YgcGFyYW1zLnJlY2lwaWVudH0pYCk7XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmlzVmFsaWRBZGRyZXNzKHBhcmFtcy5yZWNpcGllbnQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3JlY2lwaWVudCBub3QgYSB2YWxpZCBhZGRyZXNzJyk7XG4gICAgfVxuXG4gICAgaWYgKCFvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0hleCB8fCAhb3B0aW9uYWxEZXBzLmV0aEFiaS5zb2xpZGl0eVNIQTMpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignZXRoZXJldW0gbm90IGZ1bGx5IHN1cHBvcnRlZCBpbiB0aGlzIGVudmlyb25tZW50Jyk7XG4gICAgfVxuXG4gICAgLy8gR2V0IHRva2VuIGJhbGFuY2UgZnJvbSBleHRlcm5hbCBBUElcbiAgICBjb25zdCBjb2luU3BlY2lmaWMgPSBwYXJhbXMud2FsbGV0LmNvaW5TcGVjaWZpYygpO1xuICAgIGlmICghY29pblNwZWNpZmljIHx8ICFfLmlzU3RyaW5nKGNvaW5TcGVjaWZpYy5iYXNlQWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyByZXF1aXJlZCBjb2luIHNwZWNpZmljIHByb3BlcnR5IGJhc2VBZGRyZXNzJyk7XG4gICAgfVxuICAgIGNvbnN0IHJlY292ZXJ5QW1vdW50ID0gYXdhaXQgdGhpcy5xdWVyeUFkZHJlc3NUb2tlbkJhbGFuY2UocGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzLCBjb2luU3BlY2lmaWMuYmFzZUFkZHJlc3MpO1xuXG4gICAgaWYgKHBhcmFtcy5icm9hZGNhc3QpIHtcbiAgICAgIC8vIFdlJ3JlIGdvaW5nIHRvIGNyZWF0ZSBhIG5vcm1hbCBFVEggdHJhbnNhY3Rpb24gdGhhdCBzZW5kcyBhbiBhbW91bnQgb2YgMCBFVEggdG8gdGhlXG4gICAgICAvLyB0b2tlbkNvbnRyYWN0QWRkcmVzcyBhbmQgZW5jb2RlIHRoZSB1bnN1cHBvcnRlZC10b2tlbi1zZW5kIGRhdGEgaW4gdGhlIGRhdGEgZmllbGRcbiAgICAgIC8vICN0cmlja3N5XG4gICAgICBjb25zdCBzZW5kTWV0aG9kQXJncyA9IFtcbiAgICAgICAge1xuICAgICAgICAgIG5hbWU6ICdfdG8nLFxuICAgICAgICAgIHR5cGU6ICdhZGRyZXNzJyxcbiAgICAgICAgICB2YWx1ZTogcGFyYW1zLnJlY2lwaWVudCxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIG5hbWU6ICdfdmFsdWUnLFxuICAgICAgICAgIHR5cGU6ICd1aW50MjU2JyxcbiAgICAgICAgICB2YWx1ZTogcmVjb3ZlcnlBbW91bnQudG9TdHJpbmcoMTApLFxuICAgICAgICB9LFxuICAgICAgXTtcbiAgICAgIGNvbnN0IG1ldGhvZFNpZ25hdHVyZSA9IG9wdGlvbmFsRGVwcy5ldGhBYmkubWV0aG9kSUQoJ3RyYW5zZmVyJywgXy5tYXAoc2VuZE1ldGhvZEFyZ3MsICd0eXBlJykpO1xuICAgICAgY29uc3QgZW5jb2RlZEFyZ3MgPSBvcHRpb25hbERlcHMuZXRoQWJpLnJhd0VuY29kZShfLm1hcChzZW5kTWV0aG9kQXJncywgJ3R5cGUnKSwgXy5tYXAoc2VuZE1ldGhvZEFyZ3MsICd2YWx1ZScpKTtcbiAgICAgIGNvbnN0IHNlbmREYXRhID0gQnVmZmVyLmNvbmNhdChbbWV0aG9kU2lnbmF0dXJlLCBlbmNvZGVkQXJnc10pO1xuXG4gICAgICBjb25zdCBicm9hZGNhc3RQYXJhbXM6IGFueSA9IHtcbiAgICAgICAgYWRkcmVzczogcGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzLFxuICAgICAgICBhbW91bnQ6ICcwJyxcbiAgICAgICAgZGF0YTogc2VuZERhdGEudG9TdHJpbmcoJ2hleCcpLFxuICAgICAgfTtcblxuICAgICAgaWYgKHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKSB7XG4gICAgICAgIGJyb2FkY2FzdFBhcmFtcy53YWxsZXRQYXNzcGhyYXNlID0gcGFyYW1zLndhbGxldFBhc3NwaHJhc2U7XG4gICAgICB9IGVsc2UgaWYgKHBhcmFtcy5wcnYpIHtcbiAgICAgICAgYnJvYWRjYXN0UGFyYW1zLnBydiA9IHBhcmFtcy5wcnY7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBhd2FpdCBwYXJhbXMud2FsbGV0LnNlbmQoYnJvYWRjYXN0UGFyYW1zKTtcbiAgICB9XG5cbiAgICBjb25zdCByZWNpcGllbnQgPSB7XG4gICAgICBhZGRyZXNzOiBwYXJhbXMucmVjaXBpZW50LFxuICAgICAgYW1vdW50OiByZWNvdmVyeUFtb3VudC50b1N0cmluZygxMCksXG4gICAgfTtcblxuICAgIC8vIFRoaXMgc2lnbmF0dXJlIHdpbGwgYmUgdmFsaWQgZm9yIG9uZSB3ZWVrXG4gICAgY29uc3QgZXhwaXJlVGltZSA9IE1hdGguZmxvb3IobmV3IERhdGUoKS5nZXRUaW1lKCkgLyAxMDAwKSArIDYwICogNjAgKiAyNCAqIDc7XG5cbiAgICAvLyBHZXQgc2VxdWVuY2UgSUQuIFdlIGRvIHRoaXMgYnkgYnVpbGRpbmcgYSAnZmFrZScgZXRoIHRyYW5zYWN0aW9uLCBzbyB0aGUgcGxhdGZvcm0gd2lsbCBpbmNyZW1lbnQgYW5kIHJldHVybiB1cyB0aGUgbmV3IHNlcXVlbmNlIGlkXG4gICAgLy8gVGhpcyBfZG9lc18gcmVxdWlyZSB0aGUgdXNlciB0byBoYXZlIGEgbm9uLXplcm8gd2FsbGV0IGJhbGFuY2VcbiAgICBjb25zdCB7IG5leHRDb250cmFjdFNlcXVlbmNlSWQsIGdhc1ByaWNlLCBnYXNMaW1pdCB9ID0gKGF3YWl0IHBhcmFtcy53YWxsZXQucHJlYnVpbGRUcmFuc2FjdGlvbih7XG4gICAgICByZWNpcGllbnRzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBhZGRyZXNzOiBwYXJhbXMucmVjaXBpZW50LFxuICAgICAgICAgIGFtb3VudDogJzEnLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KSkgYXMgYW55O1xuXG4gICAgLy8gdGhlc2UgcmVjb3ZlcmllcyBuZWVkIHRvIGJlIHByb2Nlc3NlZCBieSBzdXBwb3J0LCBidXQgaWYgdGhlIGN1c3RvbWVyIHNlbmRzIGFueSB0cmFuc2FjdGlvbnMgYmVmb3JlIHJlY292ZXJ5IGlzXG4gICAgLy8gY29tcGxldGUgdGhlIHNlcXVlbmNlIElEIHdpbGwgYmUgaW52YWxpZC4gYXJ0aWZpY2lhbGx5IGluZmxhdGUgdGhlIHNlcXVlbmNlIElEIHRvIGFsbG93IG1vcmUgdGltZSBmb3IgcHJvY2Vzc2luZ1xuICAgIGNvbnN0IHNhZmVTZXF1ZW5jZUlkID0gbmV4dENvbnRyYWN0U2VxdWVuY2VJZCArIDEwMDA7XG5cbiAgICAvLyBCdWlsZCBzZW5kRGF0YSBmb3IgZXRoZXJldW0gdHhcbiAgICBjb25zdCBvcGVyYXRpb25UeXBlcyA9IFsnc3RyaW5nJywgJ2FkZHJlc3MnLCAndWludCcsICdhZGRyZXNzJywgJ3VpbnQnLCAndWludCddO1xuICAgIGNvbnN0IG9wZXJhdGlvbkFyZ3MgPSBbXG4gICAgICAvLyBUb2tlbiBvcGVyYXRpb24gaGFzIHByZWZpeCBoYXMgYmVlbiBhZGRlZCBoZXJlIHNvIHRoYXQgZXRoZXIgb3BlcmF0aW9uIGhhc2hlcywgc2lnbmF0dXJlcyBjYW5ub3QgYmUgcmUtdXNlZCBmb3IgdG9rZW5TZW5kaW5nXG4gICAgICBuZXR3b3JrLnRva2VuT3BlcmF0aW9uSGFzaFByZWZpeCxcbiAgICAgIG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihvcHRpb25hbERlcHMuZXRoVXRpbC5zdHJpcEhleFByZWZpeChyZWNpcGllbnQuYWRkcmVzcyksIDE2KSxcbiAgICAgIHJlY2lwaWVudC5hbW91bnQsXG4gICAgICBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ob3B0aW9uYWxEZXBzLmV0aFV0aWwuc3RyaXBIZXhQcmVmaXgocGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzKSwgMTYpLFxuICAgICAgZXhwaXJlVGltZSxcbiAgICAgIHNhZmVTZXF1ZW5jZUlkLFxuICAgIF07XG5cbiAgICBjb25zdCBvcGVyYXRpb25IYXNoID0gb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9IZXgoXG4gICAgICBvcHRpb25hbERlcHMuZXRoQWJpLnNvbGlkaXR5U0hBMyhvcGVyYXRpb25UeXBlcywgb3BlcmF0aW9uQXJncylcbiAgICApO1xuXG4gICAgY29uc3QgdXNlclBydiA9IGF3YWl0IHBhcmFtcy53YWxsZXQuZ2V0UHJ2KHtcbiAgICAgIHBydjogcGFyYW1zLnBydixcbiAgICAgIHdhbGxldFBhc3NwaHJhc2U6IHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlLFxuICAgIH0pO1xuXG4gICAgY29uc3Qgc2lnbmF0dXJlID0gVXRpbC5ldGhTaWduTXNnSGFzaChvcGVyYXRpb25IYXNoLCBVdGlsLnhwcnZUb0V0aFByaXZhdGVLZXkodXNlclBydikpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIGhhbGZTaWduZWQ6IHtcbiAgICAgICAgcmVjaXBpZW50OiByZWNpcGllbnQsXG4gICAgICAgIGV4cGlyZVRpbWU6IGV4cGlyZVRpbWUsXG4gICAgICAgIGNvbnRyYWN0U2VxdWVuY2VJZDogc2FmZVNlcXVlbmNlSWQsXG4gICAgICAgIG9wZXJhdGlvbkhhc2g6IG9wZXJhdGlvbkhhc2gsXG4gICAgICAgIHNpZ25hdHVyZTogc2lnbmF0dXJlLFxuICAgICAgICBnYXNMaW1pdDogZ2FzTGltaXQsXG4gICAgICAgIGdhc1ByaWNlOiBnYXNQcmljZSxcbiAgICAgICAgdG9rZW5Db250cmFjdEFkZHJlc3M6IHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcyxcbiAgICAgICAgd2FsbGV0SWQ6IHBhcmFtcy53YWxsZXQuaWQoKSxcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbnN1cmUgZWl0aGVyIGVudGVycHJpc2Ugb3IgbmV3RmVlQWRkcmVzcyBpcyBwYXNzZWQsIHRvIGtub3cgd2hldGhlciB0byBjcmVhdGUgbmV3IGtleSBvciB1c2UgZW50ZXJwcmlzZSBrZXlcbiAgICogQHBhcmFtIHtQcmVjcmVhdGVCaXRHb09wdGlvbnN9IHBhcmFtc1xuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmVudGVycHJpc2Uge1N0cmluZ30gdGhlIGVudGVycHJpc2UgaWQgdG8gYXNzb2NpYXRlIHdpdGggdGhpcyBrZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5uZXdGZWVBZGRyZXNzIHtCb29sZWFufSBjcmVhdGUgYSBuZXcgZmVlIGFkZHJlc3MgKGVudGVycHJpc2Ugbm90IG5lZWRlZCBpbiB0aGlzIGNhc2UpXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgcHJlQ3JlYXRlQml0R28ocGFyYW1zOiBQcmVjcmVhdGVCaXRHb09wdGlvbnMpOiB2b2lkIHtcbiAgICAvLyBXZSBhbHdheXMgbmVlZCBwYXJhbXMgb2JqZWN0LCBzaW5jZSBlaXRoZXIgZW50ZXJwcmlzZSBvciBuZXdGZWVBZGRyZXNzIGlzIHJlcXVpcmVkXG4gICAgaWYgKCFfLmlzT2JqZWN0KHBhcmFtcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgcHJlQ3JlYXRlQml0R28gbXVzdCBiZSBwYXNzZWQgYSBwYXJhbXMgb2JqZWN0LiBHb3QgJHtwYXJhbXN9ICh0eXBlICR7dHlwZW9mIHBhcmFtc30pYCk7XG4gICAgfVxuXG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLmVudGVycHJpc2UpICYmIF8uaXNVbmRlZmluZWQocGFyYW1zLm5ld0ZlZUFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdleHBlY3RpbmcgZW50ZXJwcmlzZSB3aGVuIGFkZGluZyBCaXRHbyBrZXkuIElmIHlvdSB3YW50IHRvIGNyZWF0ZSBhIG5ldyBFVEggYml0Z28ga2V5LCBzZXQgdGhlIG5ld0ZlZUFkZHJlc3MgcGFyYW1ldGVyIHRvIHRydWUuJ1xuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBDaGVjayB3aGV0aGVyIGtleSBzaG91bGQgYmUgYW4gZW50ZXJwcmlzZSBrZXkgb3IgYSBCaXRHbyBrZXkgZm9yIGEgbmV3IGZlZSBhZGRyZXNzXG4gICAgaWYgKCFfLmlzVW5kZWZpbmVkKHBhcmFtcy5lbnRlcnByaXNlKSAmJiAhXy5pc1VuZGVmaW5lZChwYXJhbXMubmV3RmVlQWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW5jb21wYXRpYmxlIGFyZ3VtZW50cyAtIGNhbm5vdCBwYXNzIGJvdGggZW50ZXJwcmlzZSBhbmQgbmV3RmVlQWRkcmVzcyBwYXJhbWV0ZXIuYCk7XG4gICAgfVxuXG4gICAgaWYgKCFfLmlzVW5kZWZpbmVkKHBhcmFtcy5lbnRlcnByaXNlKSAmJiAhXy5pc1N0cmluZyhwYXJhbXMuZW50ZXJwcmlzZSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgZW50ZXJwcmlzZSBzaG91bGQgYmUgYSBzdHJpbmcgLSBnb3QgJHtwYXJhbXMuZW50ZXJwcmlzZX0gKHR5cGUgJHt0eXBlb2YgcGFyYW1zLmVudGVycHJpc2V9KWApO1xuICAgIH1cblxuICAgIGlmICghXy5pc1VuZGVmaW5lZChwYXJhbXMubmV3RmVlQWRkcmVzcykgJiYgIV8uaXNCb29sZWFuKHBhcmFtcy5uZXdGZWVBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgbmV3RmVlQWRkcmVzcyBzaG91bGQgYmUgYSBib29sZWFuIC0gZ290ICR7cGFyYW1zLm5ld0ZlZUFkZHJlc3N9ICh0eXBlICR7dHlwZW9mIHBhcmFtcy5uZXdGZWVBZGRyZXNzfSlgXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBRdWVyaWVzIHB1YmxpYyBibG9jayBleHBsb3JlciB0byBnZXQgdGhlIG5leHQgRVRITGlrZSBjb2luJ3Mgbm9uY2UgdGhhdCBzaG91bGQgYmUgdXNlZCBmb3IgdGhlIGdpdmVuIEVUSCBhZGRyZXNzXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhZGRyZXNzXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcGlLZXkgLSBvcHRpb25hbCBBUEkga2V5IHRvIHVzZSBpbnN0ZWFkIG9mIHRoZSBvbmUgZnJvbSB0aGUgZW52aXJvbm1lbnRcbiAgICogQHJldHVybnMge1Byb21pc2U8bnVtYmVyPn1cbiAgICovXG4gIGFzeW5jIGdldEFkZHJlc3NOb25jZShhZGRyZXNzOiBzdHJpbmcsIGFwaUtleT86IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgLy8gR2V0IG5vbmNlIGZvciBiYWNrdXAga2V5IChzaG91bGQgYmUgMClcbiAgICBsZXQgbm9uY2UgPSAwO1xuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5yZWNvdmVyeUJsb2NrY2hhaW5FeHBsb3JlclF1ZXJ5KFxuICAgICAge1xuICAgICAgICBjaGFpbmlkOiB0aGlzLmdldENoYWluSWQoKS50b1N0cmluZygpLFxuICAgICAgICBtb2R1bGU6ICdhY2NvdW50JyxcbiAgICAgICAgYWN0aW9uOiAndHhsaXN0JyxcbiAgICAgICAgYWRkcmVzcyxcbiAgICAgIH0sXG4gICAgICBhcGlLZXlcbiAgICApO1xuXG4gICAgaWYgKHJlc3VsdCAmJiB0eXBlb2YgcmVzdWx0Py5ub25jZSA9PT0gJ251bWJlcicpIHtcbiAgICAgIHJldHVybiBOdW1iZXIocmVzdWx0Lm5vbmNlKTtcbiAgICB9XG5cbiAgICBpZiAoIXJlc3VsdCB8fCAhQXJyYXkuaXNBcnJheShyZXN1bHQucmVzdWx0KSkge1xuICAgICAgdGhyb3cgbmV3IFJlY292ZXJ5UHJvdmlkZXJFcnJvcignVW5hYmxlIHRvIGZpbmQgbmV4dCBub25jZSBmcm9tIEV0aGVyc2NhbiwgZ290OiAnICsgSlNPTi5zdHJpbmdpZnkocmVzdWx0KSk7XG4gICAgfVxuICAgIGNvbnN0IGJhY2t1cEtleVR4TGlzdCA9IHJlc3VsdC5yZXN1bHQ7XG4gICAgaWYgKGJhY2t1cEtleVR4TGlzdC5sZW5ndGggPiAwKSB7XG4gICAgICAvLyBDYWxjdWxhdGUgbGFzdCBub25jZSB1c2VkXG4gICAgICBjb25zdCBvdXRnb2luZ1R4cyA9IGJhY2t1cEtleVR4TGlzdC5maWx0ZXIoKHR4KSA9PiB0eC5mcm9tID09PSBhZGRyZXNzKTtcbiAgICAgIG5vbmNlID0gb3V0Z29pbmdUeHMubGVuZ3RoO1xuICAgIH1cbiAgICByZXR1cm4gbm9uY2U7XG4gIH1cblxuICAvKipcbiAgICogSGVscGVyIGZ1bmN0aW9uIGZvciByZWNvdmVyKClcbiAgICogVGhpcyB0cmFuc2Zvcm1zIHRoZSB1bnNpZ25lZCB0cmFuc2FjdGlvbiBpbmZvcm1hdGlvbiBpbnRvIGEgZm9ybWF0IHRoZSBCaXRHbyBvZmZsaW5lIHZhdWx0IGV4cGVjdHNcbiAgICogQHBhcmFtIHtVbmZvcm1hdHRlZFR4SW5mb30gdHhJbmZvIC0gdHggaW5mb1xuICAgKiBAcGFyYW0ge0V0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb259IGV0aFR4IC0gdGhlIGV0aGVyZXVtanMgdHggb2JqZWN0XG4gICAqIEBwYXJhbSB7c3RyaW5nfSB1c2VyS2V5IC0gdGhlIHVzZXIncyBrZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IGJhY2t1cEtleSAtIHRoZSBiYWNrdXAga2V5XG4gICAqIEBwYXJhbSB7QnVmZmVyfSBnYXNQcmljZSAtIGdhcyBwcmljZSBmb3IgdGhlIHR4XG4gICAqIEBwYXJhbSB7bnVtYmVyfSBnYXNMaW1pdCAtIGdhcyBsaW1pdCBmb3IgdGhlIHR4XG4gICAqIEBwYXJhbSB7RUlQMTU1OX0gZWlwMTU1OSAtIGVpcDE1NTkgcGFyYW1zXG4gICAqIEBwYXJhbSB7UmVwbGF5UHJvdGVjdGlvbk9wdGlvbnN9IHJlcGxheVByb3RlY3Rpb25PcHRpb25zIC0gcmVwbGF5IHByb3RlY3Rpb24gb3B0aW9uc1xuICAgKiBAcGFyYW0ge2FwaUtleX0gYXBpS2V5IC0gb3B0aW9uYWwgYXBpS2V5IHRvIHVzZSB3aGVuIHJldHJpZXZpbmcgYmxvY2sgY2hhaW4gZGF0YVxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPZmZsaW5lVmF1bHRUeEluZm8+fVxuICAgKi9cbiAgYXN5bmMgZm9ybWF0Rm9yT2ZmbGluZVZhdWx0KFxuICAgIHR4SW5mbzogVW5mb3JtYXR0ZWRUeEluZm8sXG4gICAgZXRoVHg6IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24sXG4gICAgdXNlcktleTogc3RyaW5nLFxuICAgIGJhY2t1cEtleTogc3RyaW5nLFxuICAgIGdhc1ByaWNlOiBCdWZmZXIsXG4gICAgZ2FzTGltaXQ6IG51bWJlcixcbiAgICBlaXAxNTU5PzogRUlQMTU1OSxcbiAgICByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz86IFJlcGxheVByb3RlY3Rpb25PcHRpb25zLFxuICAgIGFwaUtleT86IHN0cmluZ1xuICApOiBQcm9taXNlPE9mZmxpbmVWYXVsdFR4SW5mbz4ge1xuICAgIGlmICghZXRoVHgudG8pIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRXRoIHR4IG11c3QgaGF2ZSBhIGB0b2AgYWRkcmVzcycpO1xuICAgIH1cbiAgICBjb25zdCBiYWNrdXBIRE5vZGUgPSBiaXAzMi5mcm9tQmFzZTU4KGJhY2t1cEtleSk7XG4gICAgY29uc3QgYmFja3VwU2lnbmluZ0tleSA9IGJhY2t1cEhETm9kZS5wdWJsaWNLZXk7XG4gICAgY29uc3QgcmVzcG9uc2U6IE9mZmxpbmVWYXVsdFR4SW5mbyA9IHtcbiAgICAgIHR4OiBldGhUeC5zZXJpYWxpemUoKS50b1N0cmluZygnaGV4JyksXG4gICAgICB1c2VyS2V5LFxuICAgICAgYmFja3VwS2V5LFxuICAgICAgY29pbjogdGhpcy5nZXRDaGFpbigpLFxuICAgICAgZ2FzUHJpY2U6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSW50KGdhc1ByaWNlKS50b0ZpeGVkKCksXG4gICAgICBnYXNMaW1pdCxcbiAgICAgIHJlY2lwaWVudHM6IFt0eEluZm8ucmVjaXBpZW50XSxcbiAgICAgIHdhbGxldENvbnRyYWN0QWRkcmVzczogZXRoVHgudG8udG9TdHJpbmcoKSxcbiAgICAgIGFtb3VudDogdHhJbmZvLnJlY2lwaWVudC5hbW91bnQgYXMgc3RyaW5nLFxuICAgICAgYmFja3VwS2V5Tm9uY2U6IGF3YWl0IHRoaXMuZ2V0QWRkcmVzc05vbmNlKFxuICAgICAgICBgMHgke29wdGlvbmFsRGVwcy5ldGhVdGlsLnB1YmxpY1RvQWRkcmVzcyhiYWNrdXBTaWduaW5nS2V5LCB0cnVlKS50b1N0cmluZygnaGV4Jyl9YCxcbiAgICAgICAgYXBpS2V5XG4gICAgICApLFxuICAgICAgZWlwMTU1OSxcbiAgICAgIHJlcGxheVByb3RlY3Rpb25PcHRpb25zLFxuICAgIH07XG4gICAgXy5leHRlbmQocmVzcG9uc2UsIHR4SW5mbyk7XG4gICAgcmVzcG9uc2UubmV4dENvbnRyYWN0U2VxdWVuY2VJZCA9IHJlc3BvbnNlLmNvbnRyYWN0U2VxdWVuY2VJZDtcbiAgICByZXR1cm4gcmVzcG9uc2U7XG4gIH1cblxuICAvKipcbiAgICogSGVscGVyIGZ1bmN0aW9uIGZvciByZWNvdmVyKClcbiAgICogVGhpcyB0cmFuc2Zvcm1zIHRoZSB1bnNpZ25lZCB0cmFuc2FjdGlvbiBpbmZvcm1hdGlvbiBpbnRvIGEgZm9ybWF0IHRoZSBCaXRHbyBvZmZsaW5lIHZhdWx0IGV4cGVjdHNcbiAgICogQHBhcmFtIHtVbmZvcm1hdHRlZFR4SW5mb30gdHhJbmZvIC0gdHggaW5mb1xuICAgKiBAcGFyYW0ge0V0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb259IGV0aFR4IC0gdGhlIGV0aGVyZXVtanMgdHggb2JqZWN0XG4gICAqIEBwYXJhbSB7c3RyaW5nfSB1c2VyS2V5IC0gdGhlIHVzZXIncyBrZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IGJhY2t1cEtleSAtIHRoZSBiYWNrdXAga2V5XG4gICAqIEBwYXJhbSB7QnVmZmVyfSBnYXNQcmljZSAtIGdhcyBwcmljZSBmb3IgdGhlIHR4XG4gICAqIEBwYXJhbSB7bnVtYmVyfSBnYXNMaW1pdCAtIGdhcyBsaW1pdCBmb3IgdGhlIHR4XG4gICAqIEBwYXJhbSB7bnVtYmVyfSBiYWNrdXBLZXlOb25jZSAtIHRoZSBub25jZSBvZiB0aGUgYmFja3VwIGtleSBhZGRyZXNzXG4gICAqIEBwYXJhbSB7RUlQMTU1OX0gZWlwMTU1OSAtIGVpcDE1NTkgcGFyYW1zXG4gICAqIEBwYXJhbSB7UmVwbGF5UHJvdGVjdGlvbk9wdGlvbnN9IHJlcGxheVByb3RlY3Rpb25PcHRpb25zIC0gcmVwbGF5IHByb3RlY3Rpb24gb3B0aW9uc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPZmZsaW5lVmF1bHRUeEluZm8+fVxuICAgKi9cbiAgZm9ybWF0Rm9yT2ZmbGluZVZhdWx0VFNTKFxuICAgIHR4SW5mbzogVW5mb3JtYXR0ZWRUeEluZm8sXG4gICAgZXRoVHg6IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24sXG4gICAgdXNlcktleTogc3RyaW5nLFxuICAgIGJhY2t1cEtleTogc3RyaW5nLFxuICAgIGdhc1ByaWNlOiBCdWZmZXIsXG4gICAgZ2FzTGltaXQ6IG51bWJlcixcbiAgICBiYWNrdXBLZXlOb25jZTogbnVtYmVyLFxuICAgIGVpcDE1NTk/OiBFSVAxNTU5LFxuICAgIHJlcGxheVByb3RlY3Rpb25PcHRpb25zPzogUmVwbGF5UHJvdGVjdGlvbk9wdGlvbnNcbiAgKTogT2ZmbGluZVZhdWx0VHhJbmZvIHtcbiAgICBpZiAoIWV0aFR4LnRvKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0V0aCB0eCBtdXN0IGhhdmUgYSBgdG9gIGFkZHJlc3MnKTtcbiAgICB9XG4gICAgY29uc3QgcmVzcG9uc2U6IE9mZmxpbmVWYXVsdFR4SW5mbyA9IHtcbiAgICAgIHR4OiBldGhUeC5zZXJpYWxpemUoKS50b1N0cmluZygnaGV4JyksXG4gICAgICB0eEhleDogZXRoVHguZ2V0TWVzc2FnZVRvU2lnbihmYWxzZSkudG9TdHJpbmcoKSxcbiAgICAgIHVzZXJLZXksXG4gICAgICBiYWNrdXBLZXksXG4gICAgICBjb2luOiB0aGlzLmdldENoYWluKCksXG4gICAgICBnYXNQcmljZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKSxcbiAgICAgIGdhc0xpbWl0LFxuICAgICAgcmVjaXBpZW50czogW3R4SW5mby5yZWNpcGllbnRdLFxuICAgICAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiBldGhUeC50by50b1N0cmluZygpLFxuICAgICAgYW1vdW50OiB0eEluZm8ucmVjaXBpZW50LmFtb3VudCBhcyBzdHJpbmcsXG4gICAgICBiYWNrdXBLZXlOb25jZTogYmFja3VwS2V5Tm9uY2UsXG4gICAgICBlaXAxNTU5LFxuICAgICAgcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnMsXG4gICAgfTtcbiAgICBfLmV4dGVuZChyZXNwb25zZSwgdHhJbmZvKTtcbiAgICByZXR1cm4gcmVzcG9uc2U7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgd2hldGhlciB0aGUgZ2FzIHByaWNlIHBhc3NlZCBpbiBieSB1c2VyIGFyZSB3aXRoaW4gb3VyIG1heCBhbmQgbWluIGJvdW5kc1xuICAgKiBJZiB0aGV5IGFyZSBub3Qgc2V0LCBzZXQgdGhlbSB0byB0aGUgZGVmYXVsdHNcbiAgICogQHBhcmFtIHtudW1iZXJ9IHVzZXJHYXNQcmljZSAtIHVzZXIgZGVmaW5lZCBnYXMgcHJpY2VcbiAgICogQHJldHVybnMge251bWJlcn0gdGhlIGdhcyBwcmljZSB0byB1c2UgZm9yIHRoaXMgdHJhbnNhY3Rpb25cbiAgICovXG4gIHNldEdhc1ByaWNlKHVzZXJHYXNQcmljZT86IG51bWJlcik6IG51bWJlciB7XG4gICAgaWYgKCF1c2VyR2FzUHJpY2UpIHtcbiAgICAgIHJldHVybiBldGhHYXNDb25maWdzLmRlZmF1bHRHYXNQcmljZTtcbiAgICB9XG5cbiAgICBjb25zdCBnYXNQcmljZU1heCA9IGV0aEdhc0NvbmZpZ3MubWF4aW11bUdhc1ByaWNlO1xuICAgIGNvbnN0IGdhc1ByaWNlTWluID0gZXRoR2FzQ29uZmlncy5taW5pbXVtR2FzUHJpY2U7XG4gICAgaWYgKHVzZXJHYXNQcmljZSA8IGdhc1ByaWNlTWluIHx8IHVzZXJHYXNQcmljZSA+IGdhc1ByaWNlTWF4KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEdhcyBwcmljZSBtdXN0IGJlIGJldHdlZW4gJHtnYXNQcmljZU1pbn0gYW5kICR7Z2FzUHJpY2VNYXh9YCk7XG4gICAgfVxuICAgIHJldHVybiB1c2VyR2FzUHJpY2U7XG4gIH1cbiAgLyoqXG4gICAqIENoZWNrIHdoZXRoZXIgZ2FzIGxpbWl0IHBhc3NlZCBpbiBieSB1c2VyIGFyZSB3aXRoaW4gb3VyIG1heCBhbmQgbWluIGJvdW5kc1xuICAgKiBJZiB0aGV5IGFyZSBub3Qgc2V0LCBzZXQgdGhlbSB0byB0aGUgZGVmYXVsdHNcbiAgICogQHBhcmFtIHtudW1iZXJ9IHVzZXJHYXNMaW1pdCB1c2VyIGRlZmluZWQgZ2FzIGxpbWl0XG4gICAqIEByZXR1cm5zIHtudW1iZXJ9IHRoZSBnYXMgbGltaXQgdG8gdXNlIGZvciB0aGlzIHRyYW5zYWN0aW9uXG4gICAqL1xuICBzZXRHYXNMaW1pdCh1c2VyR2FzTGltaXQ/OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGlmICghdXNlckdhc0xpbWl0KSB7XG4gICAgICByZXR1cm4gZXRoR2FzQ29uZmlncy5kZWZhdWx0R2FzTGltaXQ7XG4gICAgfVxuICAgIGNvbnN0IGdhc0xpbWl0TWF4ID0gZXRoR2FzQ29uZmlncy5tYXhpbXVtR2FzTGltaXQ7XG4gICAgY29uc3QgZ2FzTGltaXRNaW4gPSBldGhHYXNDb25maWdzLm1pbmltdW1HYXNMaW1pdDtcbiAgICBpZiAodXNlckdhc0xpbWl0IDwgZ2FzTGltaXRNaW4gfHwgdXNlckdhc0xpbWl0ID4gZ2FzTGltaXRNYXgpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgR2FzIGxpbWl0IG11c3QgYmUgYmV0d2VlbiAke2dhc0xpbWl0TWlufSBhbmQgJHtnYXNMaW1pdE1heH1gKTtcbiAgICB9XG4gICAgcmV0dXJuIHVzZXJHYXNMaW1pdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBIZWxwZXIgZnVuY3Rpb24gZm9yIHNpZ25UcmFuc2FjdGlvbiBmb3IgdGhlIHJhcmUgY2FzZSB0aGF0IFNESyBpcyBkb2luZyB0aGUgc2Vjb25kIHNpZ25hdHVyZVxuICAgKiBOb3RlOiB3ZSBhcmUgZXhwZWN0aW5nIHRoaXMgdG8gYmUgY2FsbGVkIGZyb20gdGhlIG9mZmxpbmUgdmF1bHRcbiAgICogQHBhcmFtIHtTaWduRmluYWxPcHRpb25zLnR4UHJlYnVpbGR9IHBhcmFtcy50eFByZWJ1aWxkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMucHJ2XG4gICAqIEByZXR1cm5zIHt7dHhIZXg6IHN0cmluZ319XG4gICAqL1xuICBhc3luYyBzaWduRmluYWxFdGhMaWtlKHBhcmFtczogU2lnbkZpbmFsT3B0aW9ucyk6IFByb21pc2U8RnVsbHlTaWduZWRUcmFuc2FjdGlvbj4ge1xuICAgIGNvbnN0IHNpZ25pbmdLZXkgPSBuZXcgS2V5UGFpckxpYih7IHBydjogcGFyYW1zLnBydiB9KS5nZXRLZXlzKCkucHJ2O1xuICAgIGlmIChfLmlzVW5kZWZpbmVkKHNpZ25pbmdLZXkpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3NpbmcgcHJpdmF0ZSBrZXknKTtcbiAgICB9XG4gICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIocGFyYW1zLmNvbW1vbik7XG4gICAgdHJ5IHtcbiAgICAgIHR4QnVpbGRlci5mcm9tKHBhcmFtcy50eFByZWJ1aWxkLmhhbGZTaWduZWQ/LnR4SGV4KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgaGFsZi1zaWduZWQgdHJhbnNhY3Rpb24nKTtcbiAgICB9XG4gICAgdHhCdWlsZGVyLnNpZ24oeyBrZXk6IHNpZ25pbmdLZXkgfSk7XG4gICAgY29uc3QgdHggPSBhd2FpdCB0eEJ1aWxkZXIuYnVpbGQoKTtcbiAgICByZXR1cm4ge1xuICAgICAgdHhIZXg6IHR4LnRvQnJvYWRjYXN0Rm9ybWF0KCksXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBc3NlbWJsZSBoYWxmLXNpZ24gcHJlYnVpbHQgdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtTaWduVHJhbnNhY3Rpb25PcHRpb25zfSBwYXJhbXNcbiAgICovXG4gIGFzeW5jIHNpZ25UcmFuc2FjdGlvbihwYXJhbXM6IFNpZ25UcmFuc2FjdGlvbk9wdGlvbnMpOiBQcm9taXNlPFNpZ25lZFRyYW5zYWN0aW9uPiB7XG4gICAgLy8gTm9ybWFsbHkgdGhlIFNESyBwcm92aWRlcyB0aGUgZmlyc3Qgc2lnbmF0dXJlIGZvciBhbiBFdGhMaWtlIHR4LCBidXQgb2NjYXNpb25hbGx5IGl0IHByb3ZpZGVzIHRoZSBzZWNvbmQgYW5kIGZpbmFsIG9uZS5cbiAgICBpZiAocGFyYW1zLmlzTGFzdFNpZ25hdHVyZSkge1xuICAgICAgLy8gSW4gdGhpcyBjYXNlIHdoZW4gd2UncmUgZG9pbmcgdGhlIHNlY29uZCAoZmluYWwpIHNpZ25hdHVyZSwgdGhlIGxvZ2ljIGlzIGRpZmZlcmVudC5cbiAgICAgIHJldHVybiBhd2FpdCB0aGlzLnNpZ25GaW5hbEV0aExpa2UocGFyYW1zKTtcbiAgICB9XG4gICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIocGFyYW1zLmNvbW1vbik7XG4gICAgdHhCdWlsZGVyLmZyb20ocGFyYW1zLnR4UHJlYnVpbGQudHhIZXgpO1xuICAgIHR4QnVpbGRlclxuICAgICAgLnRyYW5zZmVyKClcbiAgICAgIC5jb2luKHRoaXMuc3RhdGljc0NvaW4/Lm5hbWUgYXMgc3RyaW5nKVxuICAgICAgLmtleShuZXcgS2V5UGFpckxpYih7IHBydjogcGFyYW1zLnBydiB9KS5nZXRLZXlzKCkucHJ2ISk7XG4gICAgaWYgKHBhcmFtcy53YWxsZXRWZXJzaW9uKSB7XG4gICAgICB0eEJ1aWxkZXIud2FsbGV0VmVyc2lvbihwYXJhbXMud2FsbGV0VmVyc2lvbik7XG4gICAgfVxuICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCk7XG5cbiAgICAvLyBJbiBjYXNlIG9mIHR4IHdpdGggY29udHJhY3QgZGF0YSBmcm9tIGEgY3VzdG9kaWFsIHdhbGxldCwgd2UgYXJlIHJ1bm5pbmcgaW50byBhbiBpc3N1ZVxuICAgIC8vIGFzIGhhbGZTaWduZWQgaXMgbm90IGhhdmluZyB0aGUgZGF0YSBmaWVsZC4gU28sIHdlIGFyZSBhZGRpbmcgdGhlIGRhdGEgZmllbGQgdG8gdGhlIGhhbGZTaWduZWQgdHhcbiAgICBsZXQgcmVjaXBpZW50cyA9IHBhcmFtcy50eFByZWJ1aWxkLnJlY2lwaWVudHMgfHwgcGFyYW1zLnJlY2lwaWVudHM7XG4gICAgaWYgKHJlY2lwaWVudHMgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmVjaXBpZW50cyA9IHRyYW5zYWN0aW9uLm91dHB1dHMubWFwKChvdXRwdXQpID0+ICh7IGFkZHJlc3M6IG91dHB1dC5hZGRyZXNzLCBhbW91bnQ6IG91dHB1dC52YWx1ZSB9KSk7XG4gICAgfVxuXG4gICAgY29uc3QgdHhQYXJhbXMgPSB7XG4gICAgICBlaXAxNTU5OiBwYXJhbXMudHhQcmVidWlsZC5laXAxNTU5LFxuICAgICAgdHhIZXg6IHRyYW5zYWN0aW9uLnRvQnJvYWRjYXN0Rm9ybWF0KCksXG4gICAgICByZWNpcGllbnRzOiByZWNpcGllbnRzLFxuICAgICAgZXhwaXJhdGlvbjogcGFyYW1zLnR4UHJlYnVpbGQuZXhwaXJlVGltZSxcbiAgICAgIGhvcFRyYW5zYWN0aW9uOiBwYXJhbXMudHhQcmVidWlsZC5ob3BUcmFuc2FjdGlvbixcbiAgICAgIGN1c3RvZGlhblRyYW5zYWN0aW9uSWQ6IHBhcmFtcy5jdXN0b2RpYW5UcmFuc2FjdGlvbklkLFxuICAgICAgZXhwaXJlVGltZTogcGFyYW1zLmV4cGlyZVRpbWUsXG4gICAgICBjb250cmFjdFNlcXVlbmNlSWQ6IHBhcmFtcy50eFByZWJ1aWxkLm5leHRDb250cmFjdFNlcXVlbmNlSWQgYXMgbnVtYmVyLFxuICAgICAgc2VxdWVuY2VJZDogcGFyYW1zLnNlcXVlbmNlSWQsXG4gICAgICAuLi4ocGFyYW1zLnR4UHJlYnVpbGQuaXNCYXRjaCA/IHsgaXNCYXRjaDogcGFyYW1zLnR4UHJlYnVpbGQuaXNCYXRjaCB9IDoge30pLFxuICAgIH07XG5cbiAgICByZXR1cm4geyBoYWxmU2lnbmVkOiB0eFBhcmFtcyB9O1xuICB9XG5cbiAgLyoqXG4gICAqIE1ldGhvZCB0byB2YWxpZGF0ZSByZWNvdmVyeSBwYXJhbXNcbiAgICogQHBhcmFtIHtSZWNvdmVyT3B0aW9uc30gcGFyYW1zXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgdmFsaWRhdGVSZWNvdmVyeVBhcmFtcyhwYXJhbXM6IFJlY292ZXJPcHRpb25zKTogdm9pZCB7XG4gICAgaWYgKHBhcmFtcy51c2VyS2V5ID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyB1c2VyS2V5Jyk7XG4gICAgfVxuXG4gICAgaWYgKHBhcmFtcy5iYWNrdXBLZXkgPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtaXNzaW5nIGJhY2t1cEtleScpO1xuICAgIH1cblxuICAgIGlmIChcbiAgICAgICFwYXJhbXMuaXNVbnNpZ25lZFN3ZWVwICYmXG4gICAgICBwYXJhbXMud2FsbGV0UGFzc3BocmFzZSA9PT0gdW5kZWZpbmVkICYmXG4gICAgICAhcGFyYW1zLnVzZXJLZXkuc3RhcnRzV2l0aCgneHB1YicpICYmXG4gICAgICAhcGFyYW1zLmlzVHNzXG4gICAgKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3Npbmcgd2FsbGV0IHBhc3NwaHJhc2UnKTtcbiAgICB9XG5cbiAgICBpZiAocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyA9PT0gdW5kZWZpbmVkIHx8ICF0aGlzLmlzVmFsaWRBZGRyZXNzKHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgd2FsbGV0Q29udHJhY3RBZGRyZXNzJyk7XG4gICAgfVxuXG4gICAgaWYgKHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uID09PSB1bmRlZmluZWQgfHwgIXRoaXMuaXNWYWxpZEFkZHJlc3MocGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24pKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgcmVjb3ZlcnlEZXN0aW5hdGlvbicpO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5zdGF0aWNzQ29pbj8uZmVhdHVyZXMuaW5jbHVkZXMoQ29pbkZlYXR1cmUuRUlQMTU1OSkpIHtcbiAgICAgIGlmIChwYXJhbXMuZWlwMTU1OSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgZmVlIHBhcmFtcy4gRUlQMTU1OSBub3Qgc3VwcG9ydGVkJyk7XG4gICAgICB9XG4gICAgICBpZiAocGFyYW1zLnJlcGxheVByb3RlY3Rpb25PcHRpb25zPy5oYXJkZm9yayA9PT0gJ2xvbmRvbicpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHJlcGxheVByb3RlY3Rpb24gb3B0aW9ucy4gQ2Fubm90IHVzZSB0aGUgaGFyZGZvcmsgXCJsb25kb25cIiBmb3IgdGhpcyBjaGFpbicpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBIZWxwZXIgd2hpY2ggQWRkcyBzaWduYXR1cmVzIHRvIHR4IG9iamVjdCBhbmQgcmUtc2VyaWFsaXplcyB0eFxuICAgKiBAcGFyYW0ge0V0aExpa2VDb21tb24uZGVmYXVsdH0gZXRoQ29tbW9uXG4gICAqIEBwYXJhbSB7RXRoTGlrZVR4TGliLkZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbiB8IEV0aExpa2VUeExpYi5UcmFuc2FjdGlvbn0gdHhcbiAgICogQHBhcmFtIHtFQ0RTQU1ldGhvZFR5cGVzLlNpZ25hdHVyZX0gc2lnbmF0dXJlXG4gICAqIEByZXR1cm5zIHtFdGhMaWtlVHhMaWIuRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uIHwgRXRoTGlrZVR4TGliLlRyYW5zYWN0aW9ufVxuICAgKi9cbiAgcHJpdmF0ZSBnZXRTaWduZWRUeEZyb21TaWduYXR1cmUoXG4gICAgZXRoQ29tbW9uOiBFdGhMaWtlQ29tbW9uLmRlZmF1bHQsXG4gICAgdHg6IEV0aExpa2VUeExpYi5GZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb24gfCBFdGhMaWtlVHhMaWIuVHJhbnNhY3Rpb24sXG4gICAgc2lnbmF0dXJlOiBFQ0RTQU1ldGhvZFR5cGVzLlNpZ25hdHVyZVxuICApIHtcbiAgICAvLyBnZXQgc2lnbmVkIFR4IGZyb20gc2lnbmF0dXJlXG4gICAgY29uc3QgdHhEYXRhID0gdHgudG9KU09OKCk7XG4gICAgY29uc3QgeVBhcml0eSA9IHNpZ25hdHVyZS5yZWNpZDtcbiAgICBjb25zdCBiYXNlUGFyYW1zID0ge1xuICAgICAgdG86IHR4RGF0YS50byxcbiAgICAgIG5vbmNlOiBuZXcgQk4oc3RyaXBIZXhQcmVmaXgodHhEYXRhLm5vbmNlISksICdoZXgnKSxcbiAgICAgIHZhbHVlOiBuZXcgQk4oc3RyaXBIZXhQcmVmaXgodHhEYXRhLnZhbHVlISksICdoZXgnKSxcbiAgICAgIGdhc0xpbWl0OiBuZXcgQk4oc3RyaXBIZXhQcmVmaXgodHhEYXRhLmdhc0xpbWl0ISksICdoZXgnKSxcbiAgICAgIGRhdGE6IHR4RGF0YS5kYXRhLFxuICAgICAgcjogYWRkSGV4UHJlZml4KHNpZ25hdHVyZS5yKSxcbiAgICAgIHM6IGFkZEhleFByZWZpeChzaWduYXR1cmUucyksXG4gICAgfTtcblxuICAgIGxldCBmaW5hbFR4O1xuICAgIGlmICh0eERhdGEubWF4RmVlUGVyR2FzICYmIHR4RGF0YS5tYXhQcmlvcml0eUZlZVBlckdhcykge1xuICAgICAgZmluYWxUeCA9IEZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbi5mcm9tVHhEYXRhKFxuICAgICAgICB7XG4gICAgICAgICAgLi4uYmFzZVBhcmFtcyxcbiAgICAgICAgICBtYXhQcmlvcml0eUZlZVBlckdhczogbmV3IEJOKHN0cmlwSGV4UHJlZml4KHR4RGF0YS5tYXhQcmlvcml0eUZlZVBlckdhcyEpLCAnaGV4JyksXG4gICAgICAgICAgbWF4RmVlUGVyR2FzOiBuZXcgQk4oc3RyaXBIZXhQcmVmaXgodHhEYXRhLm1heEZlZVBlckdhcyEpLCAnaGV4JyksXG4gICAgICAgICAgdjogbmV3IEJOKHlQYXJpdHkudG9TdHJpbmcoKSksXG4gICAgICAgIH0sXG4gICAgICAgIHsgY29tbW9uOiBldGhDb21tb24gfVxuICAgICAgKTtcbiAgICB9IGVsc2UgaWYgKHR4RGF0YS5nYXNQcmljZSkge1xuICAgICAgY29uc3QgdiA9IEJpZ0ludCgzNSkgKyBCaWdJbnQoeVBhcml0eSkgKyBCaWdJbnQoZXRoQ29tbW9uLmNoYWluSWRCTigpLnRvTnVtYmVyKCkpICogQmlnSW50KDIpO1xuICAgICAgZmluYWxUeCA9IExlZ2FjeVRyYW5zYWN0aW9uLmZyb21UeERhdGEoXG4gICAgICAgIHtcbiAgICAgICAgICAuLi5iYXNlUGFyYW1zLFxuICAgICAgICAgIHY6IG5ldyBCTih2LnRvU3RyaW5nKCkpLFxuICAgICAgICAgIGdhc1ByaWNlOiBuZXcgQk4oc3RyaXBIZXhQcmVmaXgodHhEYXRhLmdhc1ByaWNlIS50b1N0cmluZygpKSwgJ2hleCcpLFxuICAgICAgICB9LFxuICAgICAgICB7IGNvbW1vbjogZXRoQ29tbW9uIH1cbiAgICAgICk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGZpbmFsVHg7XG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIGEgZnVuZHMgcmVjb3ZlcnkgdHJhbnNhY3Rpb24gd2l0aG91dCBCaXRHb1xuICAgKiBAcGFyYW0gcGFyYW1zXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMudXNlcktleSAtIFtlbmNyeXB0ZWRdIHhwcnZcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iYWNrdXBLZXkgLSBbZW5jcnlwdGVkXSB4cHJ2IG9yIHhwdWIgaWYgdGhlIHhwcnYgaXMgaGVsZCBieSBhIEtSUyBwcm92aWRlclxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLndhbGxldFBhc3NwaHJhc2UgLSB1c2VkIHRvIGRlY3J5cHQgdXNlcktleSBhbmQgYmFja3VwS2V5XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzIC0gdGhlIEVUSCBhZGRyZXNzIG9mIHRoZSB3YWxsZXQgY29udHJhY3RcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5rcnNQcm92aWRlciAtIG5lY2Vzc2FyeSBpZiBiYWNrdXAga2V5IGlzIGhlbGQgYnkgS1JTXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbiAtIHRhcmdldCBhZGRyZXNzIHRvIHNlbmQgcmVjb3ZlcmVkIGZ1bmRzIHRvXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuYml0Z29GZWVBZGRyZXNzIC0gd3JvbmcgY2hhaW4gd2FsbGV0IGZlZSBhZGRyZXNzIGZvciBldm0gYmFzZWQgY3Jvc3MgY2hhaW4gcmVjb3ZlcnkgdHhuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuYml0Z29EZXN0aW5hdGlvbkFkZHJlc3MgLSB0YXJnZXQgYml0Z28gYWRkcmVzcyB3aGVyZSBmZWUgd2lsbCBiZSBzZW50IGZvciBldm0gYmFzZWQgY3Jvc3MgY2hhaW4gcmVjb3ZlcnkgdHhuXG4gICAqL1xuICBhc3luYyByZWNvdmVyKHBhcmFtczogUmVjb3Zlck9wdGlvbnMpOiBQcm9taXNlPFJlY292ZXJ5SW5mbyB8IE9mZmxpbmVWYXVsdFR4SW5mbyB8IFVuc2lnbmVkU3dlZXBUeE1QQ3YyPiB7XG4gICAgaWYgKHBhcmFtcy5pc1RzcyA9PT0gdHJ1ZSkge1xuICAgICAgcmV0dXJuIHRoaXMucmVjb3ZlclRTUyhwYXJhbXMpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5yZWNvdmVyRXRoTGlrZShwYXJhbXMpO1xuICB9XG5cbiAgZ2VuZXJhdGVGb3J3YXJkZXJBZGRyZXNzKFxuICAgIGJhc2VBZGRyZXNzOiBzdHJpbmcsXG4gICAgZmVlQWRkcmVzczogc3RyaW5nLFxuICAgIGZvcndhcmRlckZhY3RvcnlBZGRyZXNzOiBzdHJpbmcsXG4gICAgZm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzOiBzdHJpbmcsXG4gICAgaW5kZXg6IG51bWJlclxuICApOiBzdHJpbmcge1xuICAgIGNvbnN0IHNhbHQgPSBhZGRIZXhQcmVmaXgoaW5kZXgudG9TdHJpbmcoMTYpKTtcbiAgICBjb25zdCBzYWx0QnVmZmVyID0gc2V0TGVuZ3RoTGVmdCh0b0J1ZmZlcihzYWx0KSwgMzIpO1xuXG4gICAgY29uc3QgeyBjcmVhdGVGb3J3YXJkZXJQYXJhbXMsIGNyZWF0ZUZvcndhcmRlclR5cGVzIH0gPSBnZXRDcmVhdGVGb3J3YXJkZXJQYXJhbXNBbmRUeXBlcyhcbiAgICAgIGJhc2VBZGRyZXNzLFxuICAgICAgc2FsdEJ1ZmZlcixcbiAgICAgIGZlZUFkZHJlc3NcbiAgICApO1xuXG4gICAgY29uc3QgY2FsY3VsYXRpb25TYWx0ID0gYnVmZmVyVG9IZXgob3B0aW9uYWxEZXBzLmV0aEFiaS5zb2xpZGl0eVNIQTMoY3JlYXRlRm9yd2FyZGVyVHlwZXMsIGNyZWF0ZUZvcndhcmRlclBhcmFtcykpO1xuXG4gICAgY29uc3QgaW5pdENvZGUgPSBnZXRQcm94eUluaXRjb2RlKGZvcndhcmRlckltcGxlbWVudGF0aW9uQWRkcmVzcyk7XG4gICAgcmV0dXJuIGNhbGN1bGF0ZUZvcndhcmRlclYxQWRkcmVzcyhmb3J3YXJkZXJGYWN0b3J5QWRkcmVzcywgY2FsY3VsYXRpb25TYWx0LCBpbml0Q29kZSk7XG4gIH1cblxuICBkZXJpdmVBZGRyZXNzRnJvbVB1YmxpY0tleShjb21tb25LZXljaGFpbjogc3RyaW5nLCBpbmRleDogbnVtYmVyKTogc3RyaW5nIHtcbiAgICBjb25zdCBkZXJpdmF0aW9uUGF0aCA9IGBtLyR7aW5kZXh9YDtcbiAgICBjb25zdCBwdWJrZXlTaXplID0gMzM7XG5cbiAgICBjb25zdCBlY2RzYU1wYyA9IG5ldyBFY2RzYSgpO1xuICAgIGNvbnN0IGRlcml2ZWRQdWJsaWNLZXkgPSBCdWZmZXIuZnJvbShlY2RzYU1wYy5kZXJpdmVVbmhhcmRlbmVkKGNvbW1vbktleWNoYWluLCBkZXJpdmF0aW9uUGF0aCksICdoZXgnKVxuICAgICAgLnN1YmFycmF5KDAsIHB1YmtleVNpemUpXG4gICAgICAudG9TdHJpbmcoJ2hleCcpO1xuXG4gICAgY29uc3QgcHVibGljS2V5ID0gQnVmZmVyLmZyb20oZGVyaXZlZFB1YmxpY0tleSwgJ2hleCcpLnNsaWNlKDAsIDY2KS50b1N0cmluZygnaGV4Jyk7XG5cbiAgICBjb25zdCBrZXlQYWlyID0gbmV3IEtleVBhaXJMaWIoeyBwdWI6IHB1YmxpY0tleSB9KTtcbiAgICBjb25zdCBhZGRyZXNzID0ga2V5UGFpci5nZXRBZGRyZXNzKCk7XG4gICAgcmV0dXJuIGFkZHJlc3M7XG4gIH1cblxuICBnZXRDb25zb2xpZGF0aW9uQWRkcmVzcyhwYXJhbXM6IEV0aENvbnNvbGlkYXRpb25SZWNvdmVyeU9wdGlvbnMsIGluZGV4OiBudW1iZXIpOiBzdHJpbmdbXSB7XG4gICAgY29uc3QgcG9zc2libGVDb25zb2xpZGF0aW9uQWRkcmVzc2VzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGlmIChwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzICYmIHBhcmFtcy5iaXRnb0ZlZUFkZHJlc3MpIHtcbiAgICAgIGNvbnN0IGV0aE5ldHdvcmsgPSB0aGlzLmdldE5ldHdvcmsoKTtcbiAgICAgIGNvbnN0IGZvcndhcmRlckZhY3RvcnlBZGRyZXNzID0gZXRoTmV0d29yaz8ud2FsbGV0VjRGb3J3YXJkZXJGYWN0b3J5QWRkcmVzcyBhcyBzdHJpbmc7XG4gICAgICBjb25zdCBmb3J3YXJkZXJJbXBsZW1lbnRhdGlvbkFkZHJlc3MgPSBldGhOZXR3b3JrPy53YWxsZXRWNEZvcndhcmRlckltcGxlbWVudGF0aW9uQWRkcmVzcyBhcyBzdHJpbmc7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBmb3J3YXJkZXJBZGRyZXNzID0gdGhpcy5nZW5lcmF0ZUZvcndhcmRlckFkZHJlc3MoXG4gICAgICAgICAgcGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyxcbiAgICAgICAgICBwYXJhbXMuYml0Z29GZWVBZGRyZXNzLFxuICAgICAgICAgIGZvcndhcmRlckZhY3RvcnlBZGRyZXNzLFxuICAgICAgICAgIGZvcndhcmRlckltcGxlbWVudGF0aW9uQWRkcmVzcyxcbiAgICAgICAgICBpbmRleFxuICAgICAgICApO1xuICAgICAgICBwb3NzaWJsZUNvbnNvbGlkYXRpb25BZGRyZXNzZXMucHVzaChmb3J3YXJkZXJBZGRyZXNzKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5sb2coYEZhaWxlZCB0byBnZW5lcmF0ZSBmb3J3YXJkZXIgYWRkcmVzczogJHtlLm1lc3NhZ2V9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHBhcmFtcy51c2VyS2V5KSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBkZXJpdmVkQWRkcmVzcyA9IHRoaXMuZGVyaXZlQWRkcmVzc0Zyb21QdWJsaWNLZXkocGFyYW1zLnVzZXJLZXksIGluZGV4KTtcbiAgICAgICAgcG9zc2libGVDb25zb2xpZGF0aW9uQWRkcmVzc2VzLnB1c2goZGVyaXZlZEFkZHJlc3MpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjb25zb2xlLmxvZyhgRmFpbGVkIHRvIGdlbmVyYXRlIGRlcml2ZWQgYWRkcmVzczogJHtlfWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChwb3NzaWJsZUNvbnNvbGlkYXRpb25BZGRyZXNzZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdVbmFibGUgdG8gZ2VuZXJhdGUgY29uc29saWRhdGlvbiBhZGRyZXNzLiBDaGVjayB0aGF0IHdhbGxldCBjb250cmFjdCBhZGRyZXNzLCBmZWUgYWRkcmVzcywgb3IgdXNlciBrZXkgaXMgdmFsaWQuJ1xuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHBvc3NpYmxlQ29uc29saWRhdGlvbkFkZHJlc3NlcztcbiAgfVxuXG4gIGFzeW5jIHJlY292ZXJDb25zb2xpZGF0aW9ucyhwYXJhbXM6IEV0aENvbnNvbGlkYXRpb25SZWNvdmVyeU9wdGlvbnMpOiBQcm9taXNlPFVuc2lnbmVkQnVpbENvbnNvbGlkYXRpb24+IHtcbiAgICBjb25zdCBpc1Vuc2lnbmVkU3dlZXAgPSAhcGFyYW1zLnVzZXJLZXkgJiYgIXBhcmFtcy5iYWNrdXBLZXkgJiYgIXBhcmFtcy53YWxsZXRQYXNzcGhyYXNlO1xuICAgIGNvbnN0IHN0YXJ0SWR4ID0gcGFyYW1zLnN0YXJ0aW5nU2NhbkluZGV4IHx8IDE7XG4gICAgY29uc3QgZW5kSWR4ID0gcGFyYW1zLmVuZGluZ1NjYW5JbmRleCB8fCBzdGFydElkeCArIERFRkFVTFRfU0NBTl9GQUNUT1I7XG5cbiAgICBpZiAoIXBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MgfHwgcGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyA9PT0gJycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCB3YWxsZXQgY29udHJhY3QgYWRkcmVzcyAke3BhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3N9YCk7XG4gICAgfVxuXG4gICAgaWYgKCFwYXJhbXMuYml0Z29GZWVBZGRyZXNzIHx8IHBhcmFtcy5iaXRnb0ZlZUFkZHJlc3MgPT09ICcnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZmVlIGFkZHJlc3MgJHtwYXJhbXMuYml0Z29GZWVBZGRyZXNzfWApO1xuICAgIH1cblxuICAgIGlmIChzdGFydElkeCA8IDEgfHwgZW5kSWR4IDw9IHN0YXJ0SWR4IHx8IGVuZElkeCAtIHN0YXJ0SWR4ID4gMTAgKiBERUZBVUxUX1NDQU5fRkFDVE9SKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBJbnZhbGlkIHN0YXJ0aW5nIG9yIGVuZGluZyBpbmRleCB0byBzY2FuIGZvciBhZGRyZXNzZXMuIHN0YXJ0aW5nU2NhbkluZGV4OiAke3N0YXJ0SWR4fSwgZW5kaW5nU2NhbkluZGV4OiAke2VuZElkeH0uYFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBjb25zdCBjb25zb2xpZGF0ZWRUcmFuc2FjdGlvbnM6IGFueVtdID0gW107XG4gICAgbGV0IGxhc3RTY2FuSW5kZXggPSBzdGFydElkeDtcblxuICAgIGZvciAobGV0IGkgPSBzdGFydElkeDsgaSA8IGVuZElkeDsgaSsrKSB7XG4gICAgICBjb25zdCBjb25zb2xpZGF0aW9uQWRkcmVzcyA9IHRoaXMuZ2V0Q29uc29saWRhdGlvbkFkZHJlc3MocGFyYW1zLCBpKTtcbiAgICAgIGZvciAoY29uc3QgYWRkcmVzcyBvZiBjb25zb2xpZGF0aW9uQWRkcmVzcykge1xuICAgICAgICBjb25zdCByZWNvdmVyUGFyYW1zID0ge1xuICAgICAgICAgIGFwaUtleTogcGFyYW1zLmFwaUtleSxcbiAgICAgICAgICBiYWNrdXBLZXk6IHBhcmFtcy5iYWNrdXBLZXkgfHwgJycsXG4gICAgICAgICAgZ2FzTGltaXQ6IHBhcmFtcy5nYXNMaW1pdCxcbiAgICAgICAgICByZWNvdmVyeURlc3RpbmF0aW9uOiBwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbiB8fCAnJyxcbiAgICAgICAgICB1c2VyS2V5OiBwYXJhbXMudXNlcktleSB8fCAnJyxcbiAgICAgICAgICB3YWxsZXRDb250cmFjdEFkZHJlc3M6IGFkZHJlc3MsXG4gICAgICAgICAgZGVyaXZhdGlvblNlZWQ6ICcnLFxuICAgICAgICAgIGlzVHNzOiBwYXJhbXMuaXNUc3MsXG4gICAgICAgICAgZWlwMTU1OToge1xuICAgICAgICAgICAgbWF4RmVlUGVyR2FzOiBwYXJhbXMuZWlwMTU1OT8ubWF4RmVlUGVyR2FzIHx8IDIwLFxuICAgICAgICAgICAgbWF4UHJpb3JpdHlGZWVQZXJHYXM6IHBhcmFtcy5laXAxNTU5Py5tYXhQcmlvcml0eUZlZVBlckdhcyB8fCAyMDAwMDAsXG4gICAgICAgICAgfSxcbiAgICAgICAgICByZXBsYXlQcm90ZWN0aW9uT3B0aW9uczoge1xuICAgICAgICAgICAgY2hhaW46IHBhcmFtcy5yZXBsYXlQcm90ZWN0aW9uT3B0aW9ucz8uY2hhaW4gfHwgMCxcbiAgICAgICAgICAgIGhhcmRmb3JrOiBwYXJhbXMucmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM/LmhhcmRmb3JrIHx8ICdsb25kb24nLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgYml0Z29LZXk6ICcnLFxuICAgICAgICAgIGlnbm9yZUFkZHJlc3NUeXBlczogW10sXG4gICAgICAgIH07XG4gICAgICAgIGxldCByZWNvdmVyeVRyYW5zYWN0aW9uO1xuICAgICAgICB0cnkge1xuICAgICAgICAgIHJlY292ZXJ5VHJhbnNhY3Rpb24gPSBhd2FpdCB0aGlzLnJlY292ZXIocmVjb3ZlclBhcmFtcyk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICBpZiAoZSBpbnN0YW5jZW9mIEVycm9yTm9JbnB1dFRvUmVjb3Zlcikge1xuICAgICAgICAgICAgbGFzdFNjYW5JbmRleCA9IGk7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhyb3cgZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoaXNVbnNpZ25lZFN3ZWVwKSB7XG4gICAgICAgICAgY29uc29saWRhdGVkVHJhbnNhY3Rpb25zLnB1c2goKHJlY292ZXJ5VHJhbnNhY3Rpb24gYXMgTVBDU3dlZXBUeHMpLnR4UmVxdWVzdHNbMF0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbnNvbGlkYXRlZFRyYW5zYWN0aW9ucy5wdXNoKHJlY292ZXJ5VHJhbnNhY3Rpb24pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBUbyBhdm9pZCByYXRlIGxpbWl0IGZvciBldGhlcnNjYW5cbiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDEwMDApKTtcbiAgICAgIC8vIGxhc3RTY2FuSW5kZXggPSBpO1xuICAgIH1cblxuICAgIGlmIChjb25zb2xpZGF0ZWRUcmFuc2FjdGlvbnMubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBEaWQgbm90IGZpbmQgYW4gYWRkcmVzcyB3aXRoIHN1ZmZpY2llbnQgZnVuZHMgdG8gcmVjb3Zlci4gUGxlYXNlIHN0YXJ0IHRoZSBuZXh0IHNjYW4gYXQgYWRkcmVzcyBpbmRleCAke1xuICAgICAgICAgIGxhc3RTY2FuSW5kZXggKyAxXG4gICAgICAgIH0uYFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHsgdHJhbnNhY3Rpb25zOiBjb25zb2xpZGF0ZWRUcmFuc2FjdGlvbnMsIGxhc3RTY2FuSW5kZXggfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgYSBmdW5kcyByZWNvdmVyeSB0cmFuc2FjdGlvbiB3aXRob3V0IEJpdEdvIGZvciBub24tVFNTIHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSBwYXJhbXNcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy51c2VyS2V5IFtlbmNyeXB0ZWRdIHhwcnYgb3IgeHB1YlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmJhY2t1cEtleSBbZW5jcnlwdGVkXSB4cHJ2IG9yIHhwdWIgaWYgdGhlIHhwcnYgaXMgaGVsZCBieSBhIEtSUyBwcm92aWRlclxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLndhbGxldFBhc3NwaHJhc2UgdXNlZCB0byBkZWNyeXB0IHVzZXJLZXkgYW5kIGJhY2t1cEtleVxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyB0aGUgRXRoTGlrZSBhZGRyZXNzIG9mIHRoZSB3YWxsZXQgY29udHJhY3RcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5rcnNQcm92aWRlciBuZWNlc3NhcnkgaWYgYmFja3VwIGtleSBpcyBoZWxkIGJ5IEtSU1xuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24gdGFyZ2V0IGFkZHJlc3MgdG8gc2VuZCByZWNvdmVyZWQgZnVuZHMgdG9cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iaXRnb0ZlZUFkZHJlc3Mgd3JvbmcgY2hhaW4gd2FsbGV0IGZlZSBhZGRyZXNzIGZvciBldm0gYmFzZWQgY3Jvc3MgY2hhaW4gcmVjb3ZlcnkgdHhuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuYml0Z29EZXN0aW5hdGlvbkFkZHJlc3MgdGFyZ2V0IGJpdGdvIGFkZHJlc3Mgd2hlcmUgZmVlIHdpbGwgYmUgc2VudCBmb3IgZXZtIGJhc2VkIGNyb3NzIGNoYWluIHJlY292ZXJ5IHR4blxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxSZWNvdmVyeUluZm8gfCBPZmZsaW5lVmF1bHRUeEluZm8+fVxuICAgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIHJlY292ZXJFdGhMaWtlKHBhcmFtczogUmVjb3Zlck9wdGlvbnMpOiBQcm9taXNlPFJlY292ZXJ5SW5mbyB8IE9mZmxpbmVWYXVsdFR4SW5mbz4ge1xuICAgIC8vIGJpdGdvRmVlQWRkcmVzcyBpcyBvbmx5IGRlZmluZWQgd2hlbiBpdCBpcyBhIGV2bSBjcm9zcyBjaGFpbiByZWNvdmVyeVxuICAgIC8vIGFzIHdlIHVzZSBmZWUgZnJvbSB0aGlzIHdyb25nIGNoYWluIGFkZHJlc3MgZm9yIHRoZSByZWNvdmVyeSB0eG4gb24gdGhlIGNvcnJlY3QgY2hhaW4uXG4gICAgaWYgKHBhcmFtcy5iaXRnb0ZlZUFkZHJlc3MpIHtcbiAgICAgIHJldHVybiB0aGlzLnJlY292ZXJFdGhMaWtlZm9yRXZtQmFzZWRSZWNvdmVyeShwYXJhbXMpO1xuICAgIH1cblxuICAgIHRoaXMudmFsaWRhdGVSZWNvdmVyeVBhcmFtcyhwYXJhbXMpO1xuICAgIGNvbnN0IGlzVW5zaWduZWRTd2VlcCA9IHBhcmFtcy5pc1Vuc2lnbmVkU3dlZXAgPz8gZ2V0SXNVbnNpZ25lZFN3ZWVwKHBhcmFtcyk7XG5cbiAgICAvLyBDbGVhbiB1cCB3aGl0ZXNwYWNlIGZyb20gZW50ZXJlZCB2YWx1ZXNcbiAgICBsZXQgdXNlcktleSA9IHBhcmFtcy51c2VyS2V5LnJlcGxhY2UoL1xccy9nLCAnJyk7XG4gICAgY29uc3QgYmFja3VwS2V5ID0gcGFyYW1zLmJhY2t1cEtleS5yZXBsYWNlKC9cXHMvZywgJycpO1xuICAgIGNvbnN0IGdhc0xpbWl0ID0gbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHRoaXMuc2V0R2FzTGltaXQocGFyYW1zLmdhc0xpbWl0KSk7XG4gICAgY29uc3QgZ2FzUHJpY2UgPSBwYXJhbXMuZWlwMTU1OVxuICAgICAgPyBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocGFyYW1zLmVpcDE1NTkubWF4RmVlUGVyR2FzKVxuICAgICAgOiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4odGhpcy5zZXRHYXNQcmljZShwYXJhbXMuZ2FzUHJpY2UpKTtcblxuICAgIGlmICghdXNlcktleS5zdGFydHNXaXRoKCd4cHViJykgJiYgIXVzZXJLZXkuc3RhcnRzV2l0aCgneHBydicpKSB7XG4gICAgICB0cnkge1xuICAgICAgICB1c2VyS2V5ID0gYXdhaXQgdGhpcy5iaXRnby5kZWNyeXB0KHtcbiAgICAgICAgICBpbnB1dDogdXNlcktleSxcbiAgICAgICAgICBwYXNzd29yZDogcGFyYW1zLndhbGxldFBhc3NwaHJhc2UsXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEVycm9yIGRlY3J5cHRpbmcgdXNlciBrZXljaGFpbjogJHtlLm1lc3NhZ2V9YCk7XG4gICAgICB9XG4gICAgfVxuICAgIGxldCBiYWNrdXBLZXlBZGRyZXNzO1xuICAgIGxldCBiYWNrdXBTaWduaW5nS2V5O1xuICAgIGlmIChpc1Vuc2lnbmVkU3dlZXApIHtcbiAgICAgIGNvbnN0IGJhY2t1cEhETm9kZSA9IGJpcDMyLmZyb21CYXNlNTgoYmFja3VwS2V5KTtcbiAgICAgIGJhY2t1cFNpZ25pbmdLZXkgPSBiYWNrdXBIRE5vZGUucHVibGljS2V5O1xuICAgICAgYmFja3VwS2V5QWRkcmVzcyA9IGAweCR7b3B0aW9uYWxEZXBzLmV0aFV0aWwucHVibGljVG9BZGRyZXNzKGJhY2t1cFNpZ25pbmdLZXksIHRydWUpLnRvU3RyaW5nKCdoZXgnKX1gO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBEZWNyeXB0IGJhY2t1cCBwcml2YXRlIGtleSBhbmQgZ2V0IGFkZHJlc3NcbiAgICAgIGxldCBiYWNrdXBQcnY7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGJhY2t1cFBydiA9IGF3YWl0IHRoaXMuYml0Z28uZGVjcnlwdCh7XG4gICAgICAgICAgaW5wdXQ6IGJhY2t1cEtleSxcbiAgICAgICAgICBwYXNzd29yZDogcGFyYW1zLndhbGxldFBhc3NwaHJhc2UsXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEVycm9yIGRlY3J5cHRpbmcgYmFja3VwIGtleWNoYWluOiAke2UubWVzc2FnZX1gKTtcbiAgICAgIH1cblxuICAgICAgY29uc3Qga2V5UGFpciA9IG5ldyBLZXlQYWlyTGliKHsgcHJ2OiBiYWNrdXBQcnYgfSk7XG4gICAgICBiYWNrdXBTaWduaW5nS2V5ID0ga2V5UGFpci5nZXRLZXlzKCkucHJ2O1xuICAgICAgaWYgKCFiYWNrdXBTaWduaW5nS2V5KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignbm8gcHJpdmF0ZSBrZXknKTtcbiAgICAgIH1cbiAgICAgIGJhY2t1cEtleUFkZHJlc3MgPSBrZXlQYWlyLmdldEFkZHJlc3MoKTtcbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLmFzc2VydFJlY292ZXJ5S2V5c0FyZVdhbGxldFNpZ25lcnMoXG4gICAgICB1c2VyS2V5LFxuICAgICAgYmFja3VwS2V5QWRkcmVzcyxcbiAgICAgIHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MsXG4gICAgICBwYXJhbXMuYXBpS2V5XG4gICAgKTtcblxuICAgIGNvbnN0IGJhY2t1cEtleU5vbmNlID0gYXdhaXQgdGhpcy5nZXRBZGRyZXNzTm9uY2UoYmFja3VwS2V5QWRkcmVzcywgcGFyYW1zLmFwaUtleSk7XG4gICAgLy8gZ2V0IGJhbGFuY2Ugb2YgYmFja3VwS2V5IHRvIGVuc3VyZSBmdW5kcyBhcmUgYXZhaWxhYmxlIHRvIHBheSBmZWVzXG4gICAgY29uc3QgYmFja3VwS2V5QmFsYW5jZSA9IGF3YWl0IHRoaXMucXVlcnlBZGRyZXNzQmFsYW5jZShiYWNrdXBLZXlBZGRyZXNzLCBwYXJhbXMuYXBpS2V5KTtcbiAgICBsZXQgdG90YWxHYXNOZWVkZWQgPSBnYXNQcmljZS5tdWwoZ2FzTGltaXQpO1xuXG4gICAgLy8gT24gTDIgY2hhaW5zIHdpdGggTDEgZGF0YSBmZWVzLCBhZGQgYnVmZmVyIGZvciBMMSBmZWVzXG4gICAgaWYgKHRoaXMuc3RhdGljc0NvaW4/LmZhbWlseSAhPT0gdW5kZWZpbmVkICYmIGNvaW5GYW1pbGllc1dpdGhMMUZlZXMuaW5jbHVkZXModGhpcy5zdGF0aWNzQ29pbi5mYW1pbHkpKSB7XG4gICAgICB0b3RhbEdhc05lZWRlZCA9IHRvdGFsR2FzTmVlZGVkLmFkZChuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4oZXRoR2FzQ29uZmlncy5sMUdhc0ZlZUJ1ZmZlcikpO1xuICAgIH1cblxuICAgIGNvbnN0IHdlaVRvR3dlaSA9IDEwICoqIDk7XG4gICAgaWYgKGJhY2t1cEtleUJhbGFuY2UubHQodG90YWxHYXNOZWVkZWQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBCYWNrdXAga2V5IGFkZHJlc3MgJHtiYWNrdXBLZXlBZGRyZXNzfSBoYXMgYmFsYW5jZSAkeyhiYWNrdXBLZXlCYWxhbmNlIC8gd2VpVG9Hd2VpKS50b1N0cmluZygpfSBHd2VpLmAgK1xuICAgICAgICAgIGBUaGlzIGFkZHJlc3MgbXVzdCBoYXZlIGEgYmFsYW5jZSBvZiBhdCBsZWFzdCAkeyh0b3RhbEdhc05lZWRlZCAvIHdlaVRvR3dlaSkudG9TdHJpbmcoKX1gICtcbiAgICAgICAgICBgIEd3ZWkgdG8gcGVyZm9ybSByZWNvdmVyaWVzLiBUcnkgc2VuZGluZyBzb21lIGZ1bmRzIHRvIHRoaXMgYWRkcmVzcyB0aGVuIHJldHJ5LmBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gZ2V0IGJhbGFuY2Ugb2Ygd2FsbGV0XG4gICAgY29uc3QgdHhBbW91bnQgPSBhd2FpdCB0aGlzLnF1ZXJ5QWRkcmVzc0JhbGFuY2UocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcywgcGFyYW1zLmFwaUtleSk7XG4gICAgaWYgKG5ldyBCaWdOdW1iZXIodHhBbW91bnQpLmlzTGVzc1RoYW5PckVxdWFsVG8oMCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvck5vSW5wdXRUb1JlY292ZXIoJ1dhbGxldCBkb2VzIG5vdCBoYXZlIGVub3VnaCBmdW5kcyB0byByZWNvdmVyJyk7XG4gICAgfVxuXG4gICAgLy8gYnVpbGQgcmVjaXBpZW50cyBvYmplY3RcbiAgICBjb25zdCByZWNpcGllbnRzID0gW1xuICAgICAge1xuICAgICAgICBhZGRyZXNzOiBwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbixcbiAgICAgICAgYW1vdW50OiB0eEFtb3VudC50b1N0cmluZygxMCksXG4gICAgICB9LFxuICAgIF07XG5cbiAgICAvLyBHZXQgc2VxdWVuY2UgSUQgdXNpbmcgY29udHJhY3QgY2FsbFxuICAgIC8vIHdlIG5lZWQgdG8gd2FpdCBiZXR3ZWVuIG1ha2luZyB0d28gZXhwbG9yZXIgYXBpIGNhbGxzIHRvIGF2b2lkIGdldHRpbmcgYmFubmVkXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMTAwMCkpO1xuICAgIGNvbnN0IHNlcXVlbmNlSWQgPSBhd2FpdCB0aGlzLnF1ZXJ5U2VxdWVuY2VJZChwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzLCBwYXJhbXMuYXBpS2V5KTtcblxuICAgIGxldCBvcGVyYXRpb25IYXNoLCBzaWduYXR1cmU7XG4gICAgLy8gR2V0IG9wZXJhdGlvbiBoYXNoIGFuZCBzaWduIGl0XG4gICAgaWYgKCFpc1Vuc2lnbmVkU3dlZXApIHtcbiAgICAgIG9wZXJhdGlvbkhhc2ggPSB0aGlzLmdldE9wZXJhdGlvblNoYTNGb3JFeGVjdXRlQW5kQ29uZmlybShyZWNpcGllbnRzLCB0aGlzLmdldERlZmF1bHRFeHBpcmVUaW1lKCksIHNlcXVlbmNlSWQpO1xuICAgICAgc2lnbmF0dXJlID0gVXRpbC5ldGhTaWduTXNnSGFzaChvcGVyYXRpb25IYXNoLCBVdGlsLnhwcnZUb0V0aFByaXZhdGVLZXkodXNlcktleSkpO1xuXG4gICAgICB0cnkge1xuICAgICAgICBVdGlsLmVjUmVjb3ZlckV0aEFkZHJlc3Mob3BlcmF0aW9uSGFzaCwgc2lnbmF0dXJlKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHNpZ25hdHVyZScpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHR4SW5mbyA9IHtcbiAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50c1swXSxcbiAgICAgIGV4cGlyZVRpbWU6IHRoaXMuZ2V0RGVmYXVsdEV4cGlyZVRpbWUoKSxcbiAgICAgIGNvbnRyYWN0U2VxdWVuY2VJZDogc2VxdWVuY2VJZCxcbiAgICAgIG9wZXJhdGlvbkhhc2g6IG9wZXJhdGlvbkhhc2gsXG4gICAgICBzaWduYXR1cmU6IHNpZ25hdHVyZSxcbiAgICAgIGdhc0xpbWl0OiBnYXNMaW1pdC50b1N0cmluZygxMCksXG4gICAgfTtcblxuICAgIGNvbnN0IHR4QnVpbGRlciA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25CdWlsZGVyKHBhcmFtcy5jb21tb24pIGFzIFRyYW5zYWN0aW9uQnVpbGRlcjtcbiAgICB0eEJ1aWxkZXIuY291bnRlcihiYWNrdXBLZXlOb25jZSk7XG4gICAgdHhCdWlsZGVyLmNvbnRyYWN0KHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MpO1xuICAgIGxldCB0eEZlZTtcbiAgICBpZiAocGFyYW1zLmVpcDE1NTkpIHtcbiAgICAgIHR4RmVlID0ge1xuICAgICAgICBlaXAxNTU5OiB7XG4gICAgICAgICAgbWF4UHJpb3JpdHlGZWVQZXJHYXM6IHBhcmFtcy5laXAxNTU5Lm1heFByaW9yaXR5RmVlUGVyR2FzLFxuICAgICAgICAgIG1heEZlZVBlckdhczogcGFyYW1zLmVpcDE1NTkubWF4RmVlUGVyR2FzLFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgdHhGZWUgPSB7IGZlZTogZ2FzUHJpY2UudG9TdHJpbmcoKSB9O1xuICAgIH1cbiAgICB0eEJ1aWxkZXIuZmVlKHtcbiAgICAgIC4uLnR4RmVlLFxuICAgICAgZ2FzTGltaXQ6IGdhc0xpbWl0LnRvU3RyaW5nKCksXG4gICAgfSk7XG4gICAgY29uc3QgdHJhbnNmZXJCdWlsZGVyID0gdHhCdWlsZGVyLnRyYW5zZmVyKCkgYXMgVHJhbnNmZXJCdWlsZGVyO1xuICAgIHRyYW5zZmVyQnVpbGRlclxuICAgICAgLmNvaW4odGhpcy5zdGF0aWNzQ29pbj8ubmFtZSBhcyBzdHJpbmcpXG4gICAgICAuYW1vdW50KHJlY2lwaWVudHNbMF0uYW1vdW50KVxuICAgICAgLmNvbnRyYWN0U2VxdWVuY2VJZChzZXF1ZW5jZUlkKVxuICAgICAgLmV4cGlyYXRpb25UaW1lKHRoaXMuZ2V0RGVmYXVsdEV4cGlyZVRpbWUoKSlcbiAgICAgIC50byhwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbik7XG5cbiAgICBjb25zdCB0eCA9IGF3YWl0IHR4QnVpbGRlci5idWlsZCgpO1xuICAgIGlmIChpc1Vuc2lnbmVkU3dlZXApIHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlOiBPZmZsaW5lVmF1bHRUeEluZm8gPSB7XG4gICAgICAgIHR4SGV4OiB0eC50b0Jyb2FkY2FzdEZvcm1hdCgpLFxuICAgICAgICB1c2VyS2V5LFxuICAgICAgICBiYWNrdXBLZXksXG4gICAgICAgIGNvaW46IHRoaXMuZ2V0Q2hhaW4oKSxcbiAgICAgICAgZ2FzUHJpY2U6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSW50KGdhc1ByaWNlKS50b0ZpeGVkKCksXG4gICAgICAgIGdhc0xpbWl0LFxuICAgICAgICByZWNpcGllbnRzOiBbdHhJbmZvLnJlY2lwaWVudF0sXG4gICAgICAgIHdhbGxldENvbnRyYWN0QWRkcmVzczogdHgudG9Kc29uKCkudG8sXG4gICAgICAgIGFtb3VudDogdHhJbmZvLnJlY2lwaWVudC5hbW91bnQsXG4gICAgICAgIGJhY2t1cEtleU5vbmNlLFxuICAgICAgICBlaXAxNTU5OiBwYXJhbXMuZWlwMTU1OSxcbiAgICAgIH07XG4gICAgICBfLmV4dGVuZChyZXNwb25zZSwgdHhJbmZvKTtcbiAgICAgIHJlc3BvbnNlLm5leHRDb250cmFjdFNlcXVlbmNlSWQgPSByZXNwb25zZS5jb250cmFjdFNlcXVlbmNlSWQ7XG4gICAgICByZXR1cm4gcmVzcG9uc2U7XG4gICAgfVxuXG4gICAgdHhCdWlsZGVyXG4gICAgICAudHJhbnNmZXIoKVxuICAgICAgLmNvaW4odGhpcy5zdGF0aWNzQ29pbj8ubmFtZSBhcyBzdHJpbmcpXG4gICAgICAua2V5KG5ldyBLZXlQYWlyTGliKHsgcHJ2OiB1c2VyS2V5IH0pLmdldEtleXMoKS5wcnYgYXMgc3RyaW5nKTtcbiAgICB0eEJ1aWxkZXIuc2lnbih7IGtleTogYmFja3VwU2lnbmluZ0tleSB9KTtcblxuICAgIGNvbnN0IHNpZ25lZFR4ID0gYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgaWQ6IHNpZ25lZFR4LnRvSnNvbigpLmlkLFxuICAgICAgdHg6IHNpZ25lZFR4LnRvQnJvYWRjYXN0Rm9ybWF0KCksXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFeHRyYWN0IHJlY2lwaWVudHMgZnJvbSB0cmFuc2FjdGlvbiBoZXhcbiAgICogQHBhcmFtIHR4SGV4IC0gVGhlIHRyYW5zYWN0aW9uIGhleCBzdHJpbmdcbiAgICogQHJldHVybnMgQXJyYXkgb2YgcmVjaXBpZW50cyB3aXRoIGFkZHJlc3MgYW5kIGFtb3VudFxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBleHRyYWN0UmVjaXBpZW50c0Zyb21UeEhleCh0eEhleDogc3RyaW5nKTogUHJvbWlzZTxBcnJheTx7IGFkZHJlc3M6IHN0cmluZzsgYW1vdW50OiBzdHJpbmcgfT4+IHtcbiAgICBjb25zdCB0eEJ1ZmZlciA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsLnRvQnVmZmVyKHR4SGV4KTtcbiAgICBjb25zdCBkZWNvZGVkVHggPSBvcHRpb25hbERlcHMuRXRoVHguVHJhbnNhY3Rpb25GYWN0b3J5LmZyb21TZXJpYWxpemVkRGF0YSh0eEJ1ZmZlcik7XG4gICAgY29uc3QgcmVjaXBpZW50czogQXJyYXk8eyBhZGRyZXNzOiBzdHJpbmc7IGFtb3VudDogc3RyaW5nIH0+ID0gW107XG5cbiAgICBpZiAoZGVjb2RlZFR4LmRhdGEgJiYgZGVjb2RlZFR4LmRhdGEubGVuZ3RoID4gMCkge1xuICAgICAgY29uc3QgZGF0YUhleCA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSGV4KGRlY29kZWRUeC5kYXRhKTtcbiAgICAgIGNvbnN0IHRyYW5zZmVyRGF0YSA9IGRlY29kZVRyYW5zZmVyRGF0YShkYXRhSGV4KTtcbiAgICAgIGlmICh0cmFuc2ZlckRhdGEudG8pIHtcbiAgICAgICAgcmVjaXBpZW50cy5wdXNoKHtcbiAgICAgICAgICBhZGRyZXNzOiB0cmFuc2ZlckRhdGEudG8sXG4gICAgICAgICAgYW1vdW50OiB0cmFuc2ZlckRhdGEuYW1vdW50LFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gcmVjaXBpZW50cztcbiAgfVxuXG4gIGFzeW5jIHNlbmRDcm9zc0NoYWluUmVjb3ZlcnlUcmFuc2FjdGlvbihcbiAgICBwYXJhbXM6IFNlbmRDcm9zc0NoYWluUmVjb3ZlcnlPcHRpb25zXG4gICk6IFByb21pc2U8eyBjb2luOiBzdHJpbmc7IHR4SGV4Pzogc3RyaW5nOyB0eGlkOiBzdHJpbmcgfT4ge1xuICAgIGNvbnN0IGJ1aWxkUmVzcG9uc2UgPSBhd2FpdCB0aGlzLmJ1aWxkQ3Jvc3NDaGFpblJlY292ZXJ5VHJhbnNhY3Rpb24ocGFyYW1zLnJlY292ZXJ5SWQpO1xuICAgIGlmIChwYXJhbXMud2FsbGV0VHlwZSA9PT0gJ2NvbGQnKSB7XG4gICAgICByZXR1cm4gYnVpbGRSZXNwb25zZTtcbiAgICB9XG4gICAgaWYgKCFwYXJhbXMuZW5jcnlwdGVkUHJ2KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3NpbmcgZW5jcnlwdGVkUHJ2Jyk7XG4gICAgfVxuXG4gICAgbGV0IHVzZXJLZXlQcnY7XG4gICAgdHJ5IHtcbiAgICAgIHVzZXJLZXlQcnYgPSBhd2FpdCB0aGlzLmJpdGdvLmRlY3J5cHQoe1xuICAgICAgICBpbnB1dDogcGFyYW1zLmVuY3J5cHRlZFBydixcbiAgICAgICAgcGFzc3dvcmQ6IHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlLFxuICAgICAgfSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBFcnJvciBkZWNyeXB0aW5nIHVzZXIga2V5Y2hhaW46ICR7ZS5tZXNzYWdlfWApO1xuICAgIH1cbiAgICBjb25zdCBrZXlQYWlyID0gbmV3IEtleVBhaXJMaWIoeyBwcnY6IHVzZXJLZXlQcnYgfSk7XG4gICAgY29uc3QgdXNlclNpZ25pbmdLZXkgPSBrZXlQYWlyLmdldEtleXMoKS5wcnY7XG4gICAgaWYgKCF1c2VyU2lnbmluZ0tleSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdubyBwcml2YXRlIGtleScpO1xuICAgIH1cblxuICAgIGNvbnN0IHR4QnVpbGRlciA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25CdWlsZGVyKHBhcmFtcy5jb21tb24pIGFzIFRyYW5zYWN0aW9uQnVpbGRlcjtcbiAgICBjb25zdCB0eEhleCA9IGJ1aWxkUmVzcG9uc2UudHhIZXg7XG4gICAgdHhCdWlsZGVyLmZyb20odHhIZXgpO1xuICAgIGlmIChidWlsZFJlc3BvbnNlLndhbGxldFZlcnNpb24pIHtcbiAgICAgIC8vIElmIHdhbGxldFZlcnNpb24gaXMgcHJvdmlkZWQsIHNldCBpdCBpbiB0aGUgdHhCdWlsZGVyXG4gICAgICB0eEJ1aWxkZXIud2FsbGV0VmVyc2lvbihidWlsZFJlc3BvbnNlLndhbGxldFZlcnNpb24pO1xuICAgIH1cbiAgICB0eEJ1aWxkZXJcbiAgICAgIC50cmFuc2ZlcigpXG4gICAgICAuY29pbih0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZylcbiAgICAgIC5rZXkodXNlclNpZ25pbmdLZXkpO1xuICAgIGNvbnN0IHR4ID0gYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCk7XG4gICAgY29uc3QgcmVzID0gYXdhaXQgdGhpcy5iaXRnb1xuICAgICAgLnBvc3QodGhpcy5iaXRnby5taWNyb3NlcnZpY2VzVXJsKGAvYXBpL3JlY292ZXJ5L3YxL2Nyb3NzY2hhaW4vJHtwYXJhbXMucmVjb3ZlcnlJZH0vc2lnbmApKVxuICAgICAgLnNlbmQoeyB0eEhleDogdHgudG9Ccm9hZGNhc3RGb3JtYXQoKSB9KTtcbiAgICByZXR1cm4ge1xuICAgICAgY29pbjogdGhpcy5zdGF0aWNzQ29pbj8ubmFtZSBhcyBzdHJpbmcsXG4gICAgICB0eGlkOiByZXMuYm9keS50eGlkLFxuICAgIH07XG4gIH1cblxuICBhc3luYyBidWlsZENyb3NzQ2hhaW5SZWNvdmVyeVRyYW5zYWN0aW9uKHJlY292ZXJ5SWQ6IHN0cmluZyk6IFByb21pc2U8e1xuICAgIGNvaW46IHN0cmluZztcbiAgICB0eEhleDogc3RyaW5nO1xuICAgIHR4aWQ6IHN0cmluZztcbiAgICB3YWxsZXRWZXJzaW9uPzogbnVtYmVyO1xuICAgIHJlY2lwaWVudHM6IEFycmF5PHsgYWRkcmVzczogc3RyaW5nOyBhbW91bnQ6IHN0cmluZyB9PjtcbiAgfT4ge1xuICAgIGNvbnN0IHJlcyA9IGF3YWl0IHRoaXMuYml0Z28uZ2V0KHRoaXMuYml0Z28ubWljcm9zZXJ2aWNlc1VybChgL2FwaS9yZWNvdmVyeS92MS9jcm9zc2NoYWluLyR7cmVjb3ZlcnlJZH0vYnVpbGR0eGApKTtcbiAgICAvLyBFeHRyYWN0IHJlY2lwaWVudHMgZnJvbSB0aGUgdHJhbnNhY3Rpb24gaGV4XG4gICAgY29uc3QgcmVjaXBpZW50cyA9IGF3YWl0IHRoaXMuZXh0cmFjdFJlY2lwaWVudHNGcm9tVHhIZXgocmVzLmJvZHkudHhIZXgpO1xuICAgIHJldHVybiB7XG4gICAgICBjb2luOiByZXMuYm9keS5jb2luLFxuICAgICAgdHhIZXg6IHJlcy5ib2R5LnR4SGV4LFxuICAgICAgdHhpZDogcmVzLmJvZHkudHhpZCxcbiAgICAgIHdhbGxldFZlcnNpb246IHJlcy5ib2R5LndhbGxldFZlcnNpb24sXG4gICAgICByZWNpcGllbnRzLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIGEgdW5zaWduZWQgKGZvciBjb2xkLCBjdXN0b2R5IHdhbGxldCkgb3JcbiAgICogaGFsZi1zaWduZWQgKGZvciBob3Qgd2FsbGV0KSBldm0gY3Jvc3MgY2hhaW4gcmVjb3ZlcnkgdHJhbnNhY3Rpb24gd2l0aFxuICAgKiBzYW1lIGV4cGVjdGVkIGFyZ3VtZW50cyBhcyByZWNvdmVyIG1ldGhvZC5cbiAgICogVGhpcyBoZWxwcyByZWNvdmVyIGZ1bmRzIGZyb20gZXZtIGJhc2VkIHdyb25nIGNoYWluLlxuICAgKiBAcGFyYW0ge1JlY292ZXJPcHRpb25zfSBwYXJhbXNcbiAgICogQHJldHVybnMge1Byb21pc2U8UmVjb3ZlcnlJbmZvIHwgT2ZmbGluZVZhdWx0VHhJbmZvPn1cbiAgICovXG4gIHByb3RlY3RlZCBhc3luYyByZWNvdmVyRXRoTGlrZWZvckV2bUJhc2VkUmVjb3ZlcnkoXG4gICAgcGFyYW1zOiBSZWNvdmVyT3B0aW9uc1xuICApOiBQcm9taXNlPFJlY292ZXJ5SW5mbyB8IE9mZmxpbmVWYXVsdFR4SW5mbz4ge1xuICAgIHRoaXMudmFsaWRhdGVFdm1CYXNlZFJlY292ZXJ5UGFyYW1zKHBhcmFtcyk7XG5cbiAgICAvLyBDbGVhbiB1cCB3aGl0ZXNwYWNlIGZyb20gZW50ZXJlZCB2YWx1ZXNcbiAgICBjb25zdCB1c2VyS2V5ID0gcGFyYW1zLnVzZXJLZXkucmVwbGFjZSgvXFxzL2csICcnKTtcbiAgICBjb25zdCBiaXRnb0ZlZUFkZHJlc3MgPSBwYXJhbXMuYml0Z29GZWVBZGRyZXNzPy5yZXBsYWNlKC9cXHMvZywgJycpLnRvTG93ZXJDYXNlKCkgYXMgc3RyaW5nO1xuICAgIGNvbnN0IGJpdGdvRGVzdGluYXRpb25BZGRyZXNzID0gcGFyYW1zLmJpdGdvRGVzdGluYXRpb25BZGRyZXNzPy5yZXBsYWNlKC9cXHMvZywgJycpLnRvTG93ZXJDYXNlKCkgYXMgc3RyaW5nO1xuICAgIGNvbnN0IHJlY292ZXJ5RGVzdGluYXRpb24gPSBwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbj8ucmVwbGFjZSgvXFxzL2csICcnKS50b0xvd2VyQ2FzZSgpIGFzIHN0cmluZztcbiAgICBjb25zdCB3YWxsZXRDb250cmFjdEFkZHJlc3MgPSBwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzPy5yZXBsYWNlKC9cXHMvZywgJycpLnRvTG93ZXJDYXNlKCkgYXMgc3RyaW5nO1xuICAgIGNvbnN0IHRva2VuQ29udHJhY3RBZGRyZXNzID0gcGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzPy5yZXBsYWNlKC9cXHMvZywgJycpLnRvTG93ZXJDYXNlKCkgYXMgc3RyaW5nO1xuXG4gICAgbGV0IHVzZXJTaWduaW5nS2V5O1xuICAgIGxldCB1c2VyS2V5UHJ2O1xuICAgIGlmIChwYXJhbXMud2FsbGV0UGFzc3BocmFzZSkge1xuICAgICAgaWYgKCF1c2VyS2V5LnN0YXJ0c1dpdGgoJ3hwdWInKSAmJiAhdXNlcktleS5zdGFydHNXaXRoKCd4cHJ2JykpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICB1c2VyS2V5UHJ2ID0gYXdhaXQgdGhpcy5iaXRnby5kZWNyeXB0KHtcbiAgICAgICAgICAgIGlucHV0OiB1c2VyS2V5LFxuICAgICAgICAgICAgcGFzc3dvcmQ6IHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFcnJvciBkZWNyeXB0aW5nIHVzZXIga2V5Y2hhaW46ICR7ZS5tZXNzYWdlfWApO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGtleVBhaXIgPSBuZXcgS2V5UGFpckxpYih7IHBydjogdXNlcktleVBydiB9KTtcbiAgICAgIHVzZXJTaWduaW5nS2V5ID0ga2V5UGFpci5nZXRLZXlzKCkucHJ2O1xuICAgICAgaWYgKCF1c2VyU2lnbmluZ0tleSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ25vIHByaXZhdGUga2V5Jyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gVXNlIGRlZmF1bHQgZ2FzTGltaXQgZm9yIGNvbGQgYW5kIGN1c3RvZHkgd2FsbGV0c1xuICAgIGxldCBnYXNMaW1pdCA9XG4gICAgICBwYXJhbXMuZ2FzTGltaXQgfHwgdXNlcktleS5zdGFydHNXaXRoKCd4cHViJykgfHwgIXVzZXJLZXlcbiAgICAgICAgPyBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4odGhpcy5zZXRHYXNMaW1pdChwYXJhbXMuZ2FzTGltaXQpKVxuICAgICAgICA6IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTigwKTtcblxuICAgIGNvbnN0IGdhc1ByaWNlID0gcGFyYW1zLmVpcDE1NTlcbiAgICAgID8gbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHBhcmFtcy5laXAxNTU5Lm1heEZlZVBlckdhcylcbiAgICAgIDogcGFyYW1zLmdhc1ByaWNlXG4gICAgICA/IG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTih0aGlzLnNldEdhc1ByaWNlKHBhcmFtcy5nYXNQcmljZSkpXG4gICAgICA6IGF3YWl0IHRoaXMuZ2V0R2FzUHJpY2VGcm9tRXh0ZXJuYWxBUEkodGhpcy5zdGF0aWNzQ29pbj8ubmFtZSBhcyBzdHJpbmcsIHBhcmFtcy5hcGlLZXkpO1xuXG4gICAgY29uc3QgYml0Z29GZWVBZGRyZXNzTm9uY2UgPSBhd2FpdCB0aGlzLmdldEFkZHJlc3NOb25jZShiaXRnb0ZlZUFkZHJlc3MsIHBhcmFtcy5hcGlLZXkpO1xuXG4gICAgaWYgKHRva2VuQ29udHJhY3RBZGRyZXNzKSB7XG4gICAgICByZXR1cm4gdGhpcy5yZWNvdmVyRXRoTGlrZVRva2VuZm9yRXZtQmFzZWRSZWNvdmVyeShcbiAgICAgICAgcGFyYW1zLFxuICAgICAgICBiaXRnb0ZlZUFkZHJlc3NOb25jZSxcbiAgICAgICAgZ2FzTGltaXQsXG4gICAgICAgIGdhc1ByaWNlLFxuICAgICAgICB1c2VyS2V5LFxuICAgICAgICB1c2VyU2lnbmluZ0tleSxcbiAgICAgICAgcGFyYW1zLmFwaUtleVxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBnZXQgYmFsYW5jZSBvZiB3YWxsZXRcbiAgICBjb25zdCB0eEFtb3VudCA9IGF3YWl0IHRoaXMucXVlcnlBZGRyZXNzQmFsYW5jZSh3YWxsZXRDb250cmFjdEFkZHJlc3MsIHBhcmFtcy5hcGlLZXkpO1xuXG4gICAgY29uc3QgYml0Z29GZWVQZXJjZW50YWdlID0gMDsgLy8gVE9ETzogQkctNzE5MTIgY2FuIGNoYW5nZSB0aGUgZmVlJSBoZXJlLlxuICAgIGNvbnN0IGJpdGdvRmVlQW1vdW50ID0gdHhBbW91bnQgKiAoYml0Z29GZWVQZXJjZW50YWdlIC8gMTAwKTtcblxuICAgIC8vIGJ1aWxkIHJlY2lwaWVudHMgb2JqZWN0XG4gICAgY29uc3QgcmVjaXBpZW50czogUmVjaXBpZW50W10gPSBbXG4gICAgICB7XG4gICAgICAgIGFkZHJlc3M6IHJlY292ZXJ5RGVzdGluYXRpb24sXG4gICAgICAgIGFtb3VudDogbmV3IEJpZ051bWJlcih0eEFtb3VudCkubWludXMoYml0Z29GZWVBbW91bnQpLnRvRml4ZWQoKSxcbiAgICAgIH0sXG4gICAgXTtcblxuICAgIGlmIChiaXRnb0ZlZVBlcmNlbnRhZ2UgPiAwKSB7XG4gICAgICBpZiAoXy5pc1VuZGVmaW5lZChiaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcykgfHwgIXRoaXMuaXNWYWxpZEFkZHJlc3MoYml0Z29EZXN0aW5hdGlvbkFkZHJlc3MpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCBiaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcycpO1xuICAgICAgfVxuXG4gICAgICByZWNpcGllbnRzLnB1c2goe1xuICAgICAgICBhZGRyZXNzOiBiaXRnb0Rlc3RpbmF0aW9uQWRkcmVzcyxcbiAgICAgICAgYW1vdW50OiBiaXRnb0ZlZUFtb3VudC50b1N0cmluZygxMCksXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBjYWxjdWxhdGUgYmF0Y2ggZGF0YVxuICAgIGNvbnN0IEJBVENIX01FVEhPRF9OQU1FID0gJ2JhdGNoJztcbiAgICBjb25zdCBCQVRDSF9NRVRIT0RfVFlQRVMgPSBbJ2FkZHJlc3NbXScsICd1aW50MjU2W10nXTtcbiAgICBjb25zdCBiYXRjaEV4ZWN1dGlvbkluZm8gPSB0aGlzLmdldEJhdGNoRXhlY3V0aW9uSW5mbyhyZWNpcGllbnRzKTtcbiAgICBjb25zdCBiYXRjaERhdGEgPSBvcHRpb25hbERlcHMuZXRoVXRpbC5hZGRIZXhQcmVmaXgoXG4gICAgICB0aGlzLmdldE1ldGhvZENhbGxEYXRhKEJBVENIX01FVEhPRF9OQU1FLCBCQVRDSF9NRVRIT0RfVFlQRVMsIGJhdGNoRXhlY3V0aW9uSW5mby52YWx1ZXMpLnRvU3RyaW5nKCdoZXgnKVxuICAgICk7XG5cbiAgICAvLyBHZXQgc2VxdWVuY2UgSUQgdXNpbmcgY29udHJhY3QgY2FsbFxuICAgIC8vIHdlIG5lZWQgdG8gd2FpdCBiZXR3ZWVuIG1ha2luZyB0d28gZXhwbG9yZXIgYXBpIGNhbGxzIHRvIGF2b2lkIGdldHRpbmcgYmFubmVkXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMTAwMCkpO1xuICAgIGNvbnN0IHNlcXVlbmNlSWQgPSBhd2FpdCB0aGlzLnF1ZXJ5U2VxdWVuY2VJZCh3YWxsZXRDb250cmFjdEFkZHJlc3MsIHBhcmFtcy5hcGlLZXkpO1xuXG4gICAgY29uc3QgbmV0d29yayA9IHRoaXMuZ2V0TmV0d29yaygpO1xuICAgIGNvbnN0IGJhdGNoZXJDb250cmFjdEFkZHJlc3MgPSBuZXR3b3JrPy5iYXRjaGVyQ29udHJhY3RBZGRyZXNzIGFzIHN0cmluZztcblxuICAgIGNvbnN0IHR4QnVpbGRlciA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25CdWlsZGVyKHBhcmFtcy5jb21tb24pIGFzIFRyYW5zYWN0aW9uQnVpbGRlcjtcbiAgICB0eEJ1aWxkZXIuY291bnRlcihiaXRnb0ZlZUFkZHJlc3NOb25jZSk7XG4gICAgdHhCdWlsZGVyLmNvbnRyYWN0KHdhbGxldENvbnRyYWN0QWRkcmVzcyk7XG4gICAgbGV0IHR4RmVlO1xuICAgIGlmIChwYXJhbXMuZWlwMTU1OSkge1xuICAgICAgdHhGZWUgPSB7XG4gICAgICAgIGVpcDE1NTk6IHtcbiAgICAgICAgICBtYXhQcmlvcml0eUZlZVBlckdhczogcGFyYW1zLmVpcDE1NTkubWF4UHJpb3JpdHlGZWVQZXJHYXMsXG4gICAgICAgICAgbWF4RmVlUGVyR2FzOiBwYXJhbXMuZWlwMTU1OS5tYXhGZWVQZXJHYXMsXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgIH0gZWxzZSB7XG4gICAgICB0eEZlZSA9IHsgZmVlOiBnYXNQcmljZS50b1N0cmluZygpIH07XG4gICAgfVxuICAgIHR4QnVpbGRlci5mZWUoe1xuICAgICAgLi4udHhGZWUsXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoKSxcbiAgICB9KTtcblxuICAgIGNvbnN0IHRyYW5zZmVyQnVpbGRlciA9IHR4QnVpbGRlci50cmFuc2ZlcigpIGFzIFRyYW5zZmVyQnVpbGRlcjtcbiAgICBpZiAoIWJhdGNoZXJDb250cmFjdEFkZHJlc3MpIHtcbiAgICAgIHRyYW5zZmVyQnVpbGRlclxuICAgICAgICAuY29pbih0aGlzLnN0YXRpY3NDb2luPy5uYW1lIGFzIHN0cmluZylcbiAgICAgICAgLmFtb3VudChiYXRjaEV4ZWN1dGlvbkluZm8udG90YWxBbW91bnQpXG4gICAgICAgIC5jb250cmFjdFNlcXVlbmNlSWQoc2VxdWVuY2VJZClcbiAgICAgICAgLmV4cGlyYXRpb25UaW1lKHRoaXMuZ2V0RGVmYXVsdEV4cGlyZVRpbWUoKSlcbiAgICAgICAgLnRvKHJlY292ZXJ5RGVzdGluYXRpb24pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0cmFuc2ZlckJ1aWxkZXJcbiAgICAgICAgLmNvaW4odGhpcy5zdGF0aWNzQ29pbj8ubmFtZSBhcyBzdHJpbmcpXG4gICAgICAgIC5hbW91bnQoYmF0Y2hFeGVjdXRpb25JbmZvLnRvdGFsQW1vdW50KVxuICAgICAgICAuY29udHJhY3RTZXF1ZW5jZUlkKHNlcXVlbmNlSWQpXG4gICAgICAgIC5leHBpcmF0aW9uVGltZSh0aGlzLmdldERlZmF1bHRFeHBpcmVUaW1lKCkpXG4gICAgICAgIC50byhiYXRjaGVyQ29udHJhY3RBZGRyZXNzKVxuICAgICAgICAuZGF0YShiYXRjaERhdGEpO1xuICAgIH1cblxuICAgIGlmIChwYXJhbXMud2FsbGV0UGFzc3BocmFzZSkge1xuICAgICAgdHJhbnNmZXJCdWlsZGVyLmtleSh1c2VyU2lnbmluZ0tleSk7XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIGludGVuZGVkIGNoYWluIGlzIGFyYml0cnVtIG9yIG9wdGltaXNtLCB3ZSBuZWVkIHRvIHVzZSB3YWxsZXQgdmVyc2lvbiA0XG4gICAgLy8gc2luY2UgdGhlc2UgY29udHJhY3RzIGNvbnN0cnVjdCBvcGVyYXRpb25IYXNoIGRpZmZlcmVudGx5XG4gICAgaWYgKHBhcmFtcy5pbnRlbmRlZENoYWluICYmIFsnYXJiZXRoJywgJ29wZXRoJ10uaW5jbHVkZXMoY29pbnMuZ2V0KHBhcmFtcy5pbnRlbmRlZENoYWluKS5mYW1pbHkpKSB7XG4gICAgICB0eEJ1aWxkZXIud2FsbGV0VmVyc2lvbig0KTtcbiAgICB9XG5cbiAgICAvLyBJZiBnYXNMaW1pdCB3YXMgbm90IHBhc3NlZCBhcyBhIHBhcmFtIG9yIGlmIGl0IGlzIG5vdCBjb2xkL2N1c3RvZHkgd2FsbGV0LCB0aGVuIGZldGNoIHRoZSBnYXNMaW1pdCBmcm9tIEV4cGxvcmVyXG4gICAgaWYgKCFwYXJhbXMuZ2FzTGltaXQgJiYgdXNlcktleSAmJiAhdXNlcktleS5zdGFydHNXaXRoKCd4cHViJykpIHtcbiAgICAgIGNvbnN0IHNlbmREYXRhID0gdHhCdWlsZGVyLmdldFNlbmREYXRhKCk7XG4gICAgICBnYXNMaW1pdCA9IGF3YWl0IHRoaXMuZ2V0R2FzTGltaXRGcm9tRXh0ZXJuYWxBUEkoXG4gICAgICAgIHBhcmFtcy5pbnRlbmRlZENoYWluIGFzIHN0cmluZyxcbiAgICAgICAgcGFyYW1zLmJpdGdvRmVlQWRkcmVzcyBhcyBzdHJpbmcsXG4gICAgICAgIHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MsXG4gICAgICAgIHNlbmREYXRhLFxuICAgICAgICBwYXJhbXMuYXBpS2V5XG4gICAgICApO1xuICAgICAgdHhCdWlsZGVyLmZlZSh7XG4gICAgICAgIC4uLnR4RmVlLFxuICAgICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoKSxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEdldCB0aGUgYmFsYW5jZSBvZiBiaXRnb0ZlZUFkZHJlc3MgdG8gZW5zdXJlIGZ1bmRzIGFyZSBhdmFpbGFibGUgdG8gcGF5IGZlZXNcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVN1ZmZpY2llbnRCYWxhbmNlKGJpdGdvRmVlQWRkcmVzcywgZ2FzUHJpY2UsIGdhc0xpbWl0LCBwYXJhbXMuYXBpS2V5KTtcblxuICAgIGNvbnN0IHR4ID0gYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCk7XG5cbiAgICBjb25zdCB0eEluZm8gPSB7XG4gICAgICByZWNpcGllbnRzOiByZWNpcGllbnRzLFxuICAgICAgZXhwaXJlVGltZTogdGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpLFxuICAgICAgY29udHJhY3RTZXF1ZW5jZUlkOiBzZXF1ZW5jZUlkLFxuICAgICAgZ2FzTGltaXQ6IGdhc0xpbWl0LnRvU3RyaW5nKDEwKSxcbiAgICAgIGlzRXZtQmFzZWRDcm9zc0NoYWluUmVjb3Zlcnk6IHRydWUsXG4gICAgfTtcblxuICAgIGNvbnN0IHJlc3BvbnNlOiBPZmZsaW5lVmF1bHRUeEluZm8gPSB7XG4gICAgICB0eEhleDogdHgudG9Ccm9hZGNhc3RGb3JtYXQoKSxcbiAgICAgIHVzZXJLZXksXG4gICAgICBjb2luOiB0aGlzLmdldENoYWluKCksXG4gICAgICBnYXNQcmljZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKSxcbiAgICAgIGdhc0xpbWl0LFxuICAgICAgcmVjaXBpZW50czogdHhJbmZvLnJlY2lwaWVudHMsXG4gICAgICB3YWxsZXRDb250cmFjdEFkZHJlc3M6IHR4LnRvSnNvbigpLnRvLFxuICAgICAgYW1vdW50OiBiYXRjaEV4ZWN1dGlvbkluZm8udG90YWxBbW91bnQsXG4gICAgICBiYWNrdXBLZXlOb25jZTogYml0Z29GZWVBZGRyZXNzTm9uY2UsXG4gICAgICBlaXAxNTU5OiBwYXJhbXMuZWlwMTU1OSxcbiAgICAgIC4uLih0eEJ1aWxkZXIuZ2V0V2FsbGV0VmVyc2lvbigpID09PSA0ID8geyB3YWxsZXRWZXJzaW9uOiB0eEJ1aWxkZXIuZ2V0V2FsbGV0VmVyc2lvbigpIH0gOiB7fSksXG4gICAgfTtcbiAgICBfLmV4dGVuZChyZXNwb25zZSwgdHhJbmZvKTtcbiAgICByZXNwb25zZS5uZXh0Q29udHJhY3RTZXF1ZW5jZUlkID0gcmVzcG9uc2UuY29udHJhY3RTZXF1ZW5jZUlkO1xuXG4gICAgaWYgKHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKSB7XG4gICAgICBjb25zdCBoYWxmU2lnbmVkVHhuOiBIYWxmU2lnbmVkVHJhbnNhY3Rpb24gPSB7XG4gICAgICAgIGhhbGZTaWduZWQ6IHtcbiAgICAgICAgICB0eEhleDogdHgudG9Ccm9hZGNhc3RGb3JtYXQoKSxcbiAgICAgICAgICByZWNpcGllbnRzOiB0eEluZm8ucmVjaXBpZW50cyxcbiAgICAgICAgICBleHBpcmVUaW1lOiB0eEluZm8uZXhwaXJlVGltZSxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgICBfLmV4dGVuZChyZXNwb25zZSwgaGFsZlNpZ25lZFR4bik7XG5cbiAgICAgIGNvbnN0IGZlZXNVc2VkOiBGZWVzVXNlZCA9IHtcbiAgICAgICAgZ2FzUHJpY2U6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSW50KGdhc1ByaWNlKS50b0ZpeGVkKCksXG4gICAgICAgIGdhc0xpbWl0OiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0ludChnYXNMaW1pdCkudG9GaXhlZCgpLFxuICAgICAgfTtcbiAgICAgIHJlc3BvbnNlWydmZWVzVXNlZCddID0gZmVlc1VzZWQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3BvbnNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFF1ZXJ5IGV4cGxvcmVyIGZvciB0aGUgYmFsYW5jZSBvZiBhbiBhZGRyZXNzIGZvciBhIHRva2VuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0b2tlbkNvbnRyYWN0QWRkcmVzcyAtIGFkZHJlc3Mgd2hlcmUgdGhlIHRva2VuIHNtYXJ0IGNvbnRyYWN0IGlzIGhvc3RlZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gd2FsbGV0Q29udHJhY3RBZGRyZXNzIC0gYWRkcmVzcyBvZiB0aGUgd2FsbGV0XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcGlLZXkgLSBvcHRpb25hbCBBUEkga2V5IHRvIHVzZSBpbnN0ZWFkIG9mIHRoZSBvbmUgZnJvbSB0aGUgZW52aXJvbm1lbnRcbiAgICogQHJldHVybnMge0JpZ051bWJlcn0gdG9rZW4gYmFsYWFuY2UgaW4gYmFzZSB1bml0c1xuICAgKi9cbiAgYXN5bmMgcXVlcnlBZGRyZXNzVG9rZW5CYWxhbmNlKFxuICAgIHRva2VuQ29udHJhY3RBZGRyZXNzOiBzdHJpbmcsXG4gICAgd2FsbGV0Q29udHJhY3RBZGRyZXNzOiBzdHJpbmcsXG4gICAgYXBpS2V5Pzogc3RyaW5nXG4gICk6IFByb21pc2U8YW55PiB7XG4gICAgaWYgKCFvcHRpb25hbERlcHMuZXRoVXRpbC5pc1ZhbGlkQWRkcmVzcyh0b2tlbkNvbnRyYWN0QWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignY2Fubm90IGdldCBiYWxhbmNlIGZvciBpbnZhbGlkIHRva2VuIGFkZHJlc3MnKTtcbiAgICB9XG4gICAgaWYgKCFvcHRpb25hbERlcHMuZXRoVXRpbC5pc1ZhbGlkQWRkcmVzcyh3YWxsZXRDb250cmFjdEFkZHJlc3MpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2Nhbm5vdCBnZXQgdG9rZW4gYmFsYW5jZSBmb3IgaW52YWxpZCB3YWxsZXQgYWRkcmVzcycpO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMucmVjb3ZlcnlCbG9ja2NoYWluRXhwbG9yZXJRdWVyeShcbiAgICAgIHtcbiAgICAgICAgY2hhaW5pZDogdGhpcy5nZXRDaGFpbklkKCkudG9TdHJpbmcoKSxcbiAgICAgICAgbW9kdWxlOiAnYWNjb3VudCcsXG4gICAgICAgIGFjdGlvbjogJ3Rva2VuYmFsYW5jZScsXG4gICAgICAgIGNvbnRyYWN0YWRkcmVzczogdG9rZW5Db250cmFjdEFkZHJlc3MsXG4gICAgICAgIGFkZHJlc3M6IHdhbGxldENvbnRyYWN0QWRkcmVzcyxcbiAgICAgICAgdGFnOiAnbGF0ZXN0JyxcbiAgICAgIH0sXG4gICAgICBhcGlLZXlcbiAgICApO1xuICAgIC8vIHRocm93IGlmIHRoZSByZXN1bHQgZG9lcyBub3QgZXhpc3Qgb3IgdGhlIHJlc3VsdCBpcyBub3QgYSB2YWxpZCBudW1iZXJcbiAgICBpZiAoIXJlc3VsdCB8fCAhcmVzdWx0LnJlc3VsdCB8fCBpc05hTihyZXN1bHQucmVzdWx0KSkge1xuICAgICAgdGhyb3cgbmV3IFJlY292ZXJ5UHJvdmlkZXJFcnJvcihcbiAgICAgICAgYENvdWxkIG5vdCBvYnRhaW4gdG9rZW4gYWRkcmVzcyBiYWxhbmNlIGZvciAke3Rva2VuQ29udHJhY3RBZGRyZXNzfSBmcm9tIEV0aGVyc2NhbiwgZ290OiAke3Jlc3VsdC5yZXN1bHR9YFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBvcHRpb25hbERlcHMuZXRoVXRpbC5CTihyZXN1bHQucmVzdWx0LCAxMCk7XG4gIH1cblxuICBhc3luYyByZWNvdmVyRXRoTGlrZVRva2VuZm9yRXZtQmFzZWRSZWNvdmVyeShcbiAgICBwYXJhbXM6IFJlY292ZXJPcHRpb25zLFxuICAgIGJpdGdvRmVlQWRkcmVzc05vbmNlOiBudW1iZXIsXG4gICAgZ2FzTGltaXQsXG4gICAgZ2FzUHJpY2UsXG4gICAgdXNlcktleSxcbiAgICB1c2VyU2lnbmluZ0tleSxcbiAgICBhcGlLZXk/OiBzdHJpbmdcbiAgKSB7XG4gICAgLy8gZ2V0IHRva2VuIGJhbGFuY2Ugb2Ygd2FsbGV0XG4gICAgY29uc3QgdHhBbW91bnQgPSBhd2FpdCB0aGlzLnF1ZXJ5QWRkcmVzc1Rva2VuQmFsYW5jZShcbiAgICAgIHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcyBhcyBzdHJpbmcsXG4gICAgICBwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzLFxuICAgICAgYXBpS2V5XG4gICAgKTtcblxuICAgIC8vIGJ1aWxkIHJlY2lwaWVudHMgb2JqZWN0XG4gICAgY29uc3QgcmVjaXBpZW50czogUmVjaXBpZW50W10gPSBbXG4gICAgICB7XG4gICAgICAgIGFkZHJlc3M6IHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uLFxuICAgICAgICBhbW91bnQ6IG5ldyBCaWdOdW1iZXIodHhBbW91bnQpLnRvRml4ZWQoKSxcbiAgICAgIH0sXG4gICAgXTtcblxuICAgIC8vIEdldCBzZXF1ZW5jZSBJRCB1c2luZyBjb250cmFjdCBjYWxsXG4gICAgLy8gd2UgbmVlZCB0byB3YWl0IGJldHdlZW4gbWFraW5nIHR3byBleHBsb3JlciBhcGkgY2FsbHMgdG8gYXZvaWQgZ2V0dGluZyBiYW5uZWRcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCAxMDAwKSk7XG4gICAgY29uc3Qgc2VxdWVuY2VJZCA9IGF3YWl0IHRoaXMucXVlcnlTZXF1ZW5jZUlkKHBhcmFtcy53YWxsZXRDb250cmFjdEFkZHJlc3MsIHBhcmFtcy5hcGlLZXkpO1xuXG4gICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIocGFyYW1zLmNvbW1vbikgYXMgVHJhbnNhY3Rpb25CdWlsZGVyO1xuICAgIHR4QnVpbGRlci5jb3VudGVyKGJpdGdvRmVlQWRkcmVzc05vbmNlKTtcbiAgICB0eEJ1aWxkZXIuY29udHJhY3QocGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcyBhcyBzdHJpbmcpO1xuICAgIGxldCB0eEZlZTtcbiAgICBpZiAocGFyYW1zLmVpcDE1NTkpIHtcbiAgICAgIHR4RmVlID0ge1xuICAgICAgICBlaXAxNTU5OiB7XG4gICAgICAgICAgbWF4UHJpb3JpdHlGZWVQZXJHYXM6IHBhcmFtcy5laXAxNTU5Lm1heFByaW9yaXR5RmVlUGVyR2FzLFxuICAgICAgICAgIG1heEZlZVBlckdhczogcGFyYW1zLmVpcDE1NTkubWF4RmVlUGVyR2FzLFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgdHhGZWUgPSB7IGZlZTogZ2FzUHJpY2UudG9TdHJpbmcoKSB9O1xuICAgIH1cbiAgICB0eEJ1aWxkZXIuZmVlKHtcbiAgICAgIC4uLnR4RmVlLFxuICAgICAgZ2FzTGltaXQ6IGdhc0xpbWl0LnRvU3RyaW5nKCksXG4gICAgfSk7XG5cbiAgICBjb25zdCB0cmFuc2ZlckJ1aWxkZXIgPSB0eEJ1aWxkZXIudHJhbnNmZXIoKSBhcyBUcmFuc2ZlckJ1aWxkZXI7XG5cbiAgICBjb25zdCBuZXR3b3JrID0gdGhpcy5nZXROZXR3b3JrKCk7XG4gICAgY29uc3QgdG9rZW4gPSBnZXRUb2tlbihcbiAgICAgIHBhcmFtcy50b2tlbkNvbnRyYWN0QWRkcmVzcyBhcyBzdHJpbmcsXG4gICAgICBuZXR3b3JrIGFzIEV0aExpa2VOZXR3b3JrLFxuICAgICAgdGhpcy5zdGF0aWNzQ29pbj8uZmFtaWx5IGFzIHN0cmluZ1xuICAgICk/Lm5hbWUgYXMgc3RyaW5nO1xuXG4gICAgdHJhbnNmZXJCdWlsZGVyXG4gICAgICAuYW1vdW50KHR4QW1vdW50KVxuICAgICAgLmNvbnRyYWN0U2VxdWVuY2VJZChzZXF1ZW5jZUlkKVxuICAgICAgLmV4cGlyYXRpb25UaW1lKHRoaXMuZ2V0RGVmYXVsdEV4cGlyZVRpbWUoKSlcbiAgICAgIC50byhwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbik7XG5cbiAgICBpZiAodG9rZW4pIHtcbiAgICAgIHRyYW5zZmVyQnVpbGRlci5jb2luKHRva2VuKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdHJhbnNmZXJCdWlsZGVyXG4gICAgICAgIC5jb2luKHRoaXMuc3RhdGljc0NvaW4/Lm5hbWUgYXMgc3RyaW5nKVxuICAgICAgICAudG9rZW5Db250cmFjdEFkZHJlc3MocGFyYW1zLnRva2VuQ29udHJhY3RBZGRyZXNzIGFzIHN0cmluZyk7XG4gICAgfVxuXG4gICAgaWYgKHBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKSB7XG4gICAgICB0eEJ1aWxkZXIudHJhbnNmZXIoKS5rZXkodXNlclNpZ25pbmdLZXkpO1xuICAgIH1cbiAgICAvLyBJZiB0aGUgaW50ZW5kZWQgY2hhaW4gaXMgYXJiaXRydW0gb3Igb3B0aW1pc20sIHdlIG5lZWQgdG8gdXNlIHdhbGxldCB2ZXJzaW9uIDRcbiAgICAvLyBzaW5jZSB0aGVzZSBjb250cmFjdHMgY29uc3RydWN0IG9wZXJhdGlvbkhhc2ggZGlmZmVyZW50bHlcbiAgICBpZiAocGFyYW1zLmludGVuZGVkQ2hhaW4gJiYgWydhcmJldGgnLCAnb3BldGgnXS5pbmNsdWRlcyhjb2lucy5nZXQocGFyYW1zLmludGVuZGVkQ2hhaW4pLmZhbWlseSkpIHtcbiAgICAgIHR4QnVpbGRlci53YWxsZXRWZXJzaW9uKDQpO1xuICAgIH1cblxuICAgIGlmICghcGFyYW1zLmdhc0xpbWl0ICYmIHVzZXJLZXkgJiYgIXVzZXJLZXkuc3RhcnRzV2l0aCgneHB1YicpKSB7XG4gICAgICBjb25zdCBzZW5kRGF0YSA9IHR4QnVpbGRlci5nZXRTZW5kRGF0YSgpO1xuICAgICAgZ2FzTGltaXQgPSBhd2FpdCB0aGlzLmdldEdhc0xpbWl0RnJvbUV4dGVybmFsQVBJKFxuICAgICAgICBwYXJhbXMuaW50ZW5kZWRDaGFpbiBhcyBzdHJpbmcsXG4gICAgICAgIHBhcmFtcy5iaXRnb0ZlZUFkZHJlc3MgYXMgc3RyaW5nLFxuICAgICAgICBwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzLFxuICAgICAgICBzZW5kRGF0YSxcbiAgICAgICAgYXBpS2V5XG4gICAgICApO1xuICAgICAgdHhCdWlsZGVyLmZlZSh7XG4gICAgICAgIC4uLnR4RmVlLFxuICAgICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoKSxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEdldCB0aGUgYmFsYW5jZSBvZiBiaXRnb0ZlZUFkZHJlc3MgdG8gZW5zdXJlIGZ1bmRzIGFyZSBhdmFpbGFibGUgdG8gcGF5IGZlZXNcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVN1ZmZpY2llbnRCYWxhbmNlKHBhcmFtcy5iaXRnb0ZlZUFkZHJlc3MgYXMgc3RyaW5nLCBnYXNQcmljZSwgZ2FzTGltaXQsIHBhcmFtcy5hcGlLZXkpO1xuXG4gICAgY29uc3QgdHggPSBhd2FpdCB0eEJ1aWxkZXIuYnVpbGQoKTtcblxuICAgIGNvbnN0IHR4SW5mbyA9IHtcbiAgICAgIHJlY2lwaWVudHM6IHJlY2lwaWVudHMsXG4gICAgICBleHBpcmVUaW1lOiB0aGlzLmdldERlZmF1bHRFeHBpcmVUaW1lKCksXG4gICAgICBjb250cmFjdFNlcXVlbmNlSWQ6IHNlcXVlbmNlSWQsXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQudG9TdHJpbmcoMTApLFxuICAgICAgaXNFdm1CYXNlZENyb3NzQ2hhaW5SZWNvdmVyeTogdHJ1ZSxcbiAgICB9O1xuXG4gICAgY29uc3QgcmVzcG9uc2U6IE9mZmxpbmVWYXVsdFR4SW5mbyA9IHtcbiAgICAgIHR4SGV4OiB0eC50b0Jyb2FkY2FzdEZvcm1hdCgpLFxuICAgICAgdXNlcktleSxcbiAgICAgIGNvaW46IHRva2VuID8gdG9rZW4gOiB0aGlzLmdldENoYWluKCksXG4gICAgICBnYXNQcmljZTogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKSxcbiAgICAgIGdhc0xpbWl0LFxuICAgICAgcmVjaXBpZW50czogdHhJbmZvLnJlY2lwaWVudHMsXG4gICAgICB3YWxsZXRDb250cmFjdEFkZHJlc3M6IHR4LnRvSnNvbigpLnRvLFxuICAgICAgYW1vdW50OiB0eEFtb3VudC50b1N0cmluZygpLFxuICAgICAgYmFja3VwS2V5Tm9uY2U6IGJpdGdvRmVlQWRkcmVzc05vbmNlLFxuICAgICAgZWlwMTU1OTogcGFyYW1zLmVpcDE1NTksXG4gICAgICAuLi4odHhCdWlsZGVyLmdldFdhbGxldFZlcnNpb24oKSA9PT0gNCA/IHsgd2FsbGV0VmVyc2lvbjogdHhCdWlsZGVyLmdldFdhbGxldFZlcnNpb24oKSB9IDoge30pLFxuICAgIH07XG4gICAgXy5leHRlbmQocmVzcG9uc2UsIHR4SW5mbyk7XG4gICAgcmVzcG9uc2UubmV4dENvbnRyYWN0U2VxdWVuY2VJZCA9IHJlc3BvbnNlLmNvbnRyYWN0U2VxdWVuY2VJZDtcblxuICAgIGlmIChwYXJhbXMud2FsbGV0UGFzc3BocmFzZSkge1xuICAgICAgY29uc3QgaGFsZlNpZ25lZFR4bjogSGFsZlNpZ25lZFRyYW5zYWN0aW9uID0ge1xuICAgICAgICBoYWxmU2lnbmVkOiB7XG4gICAgICAgICAgdHhIZXg6IHR4LnRvQnJvYWRjYXN0Rm9ybWF0KCksXG4gICAgICAgICAgcmVjaXBpZW50czogdHhJbmZvLnJlY2lwaWVudHMsXG4gICAgICAgICAgZXhwaXJlVGltZTogdHhJbmZvLmV4cGlyZVRpbWUsXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgICAgXy5leHRlbmQocmVzcG9uc2UsIGhhbGZTaWduZWRUeG4pO1xuXG4gICAgICBjb25zdCBmZWVzVXNlZDogRmVlc1VzZWQgPSB7XG4gICAgICAgIGdhc1ByaWNlOiBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0ludChnYXNQcmljZSkudG9GaXhlZCgpLFxuICAgICAgICBnYXNMaW1pdDogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzTGltaXQpLnRvRml4ZWQoKSxcbiAgICAgIH07XG4gICAgICByZXNwb25zZVsnZmVlc1VzZWQnXSA9IGZlZXNVc2VkO1xuICAgIH1cblxuICAgIHJldHVybiByZXNwb25zZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBldm0gYmFzZWQgY3Jvc3MgY2hhaW4gcmVjb3ZlcnkgcGFyYW1zXG4gICAqIEBwYXJhbSBwYXJhbXMge1JlY292ZXJPcHRpb25zfVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIHZhbGlkYXRlRXZtQmFzZWRSZWNvdmVyeVBhcmFtcyhwYXJhbXM6IFJlY292ZXJPcHRpb25zKTogdm9pZCB7XG4gICAgaWYgKF8uaXNVbmRlZmluZWQocGFyYW1zLmJpdGdvRmVlQWRkcmVzcykgfHwgIXRoaXMuaXNWYWxpZEFkZHJlc3MocGFyYW1zLmJpdGdvRmVlQWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCBiaXRnb0ZlZUFkZHJlc3MnKTtcbiAgICB9XG5cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzKSB8fCAhdGhpcy5pc1ZhbGlkQWRkcmVzcyhwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkIHdhbGxldENvbnRyYWN0QWRkcmVzcycpO1xuICAgIH1cblxuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy5yZWNvdmVyeURlc3RpbmF0aW9uKSB8fCAhdGhpcy5pc1ZhbGlkQWRkcmVzcyhwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbikpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCByZWNvdmVyeURlc3RpbmF0aW9uJyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiB0eXBlcywgdmFsdWVzLCBhbmQgdG90YWwgYW1vdW50IGluIHdlaSB0byBzZW5kIGluIGEgYmF0Y2ggdHJhbnNhY3Rpb24sIHVzaW5nIHRoZSBtZXRob2Qgc2lnbmF0dXJlXG4gICAqIGBkaXN0cmlidXRlQmF0Y2goYWRkcmVzc1tdLCB1aW50MjU2W10pYFxuICAgKiBAcGFyYW0ge1JlY2lwaWVudFtdfSByZWNpcGllbnRzIC0gdHJhbnNhY3Rpb24gcmVjaXBpZW50c1xuICAgKiBAcmV0dXJucyB7R2V0QmF0Y2hFeGVjdXRpb25JbmZvUlR9IGluZm9ybWF0aW9uIG5lZWRlZCB0byBleGVjdXRlIHRoZSBiYXRjaCB0cmFuc2FjdGlvblxuICAgKi9cbiAgZ2V0QmF0Y2hFeGVjdXRpb25JbmZvKHJlY2lwaWVudHM6IFJlY2lwaWVudFtdKTogR2V0QmF0Y2hFeGVjdXRpb25JbmZvUlQge1xuICAgIGNvbnN0IGFkZHJlc3Nlczogc3RyaW5nW10gPSBbXTtcbiAgICBjb25zdCBhbW91bnRzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGxldCBzdW0gPSBuZXcgQmlnTnVtYmVyKCcwJyk7XG4gICAgXy5mb3JFYWNoKHJlY2lwaWVudHMsICh7IGFkZHJlc3MsIGFtb3VudCB9KSA9PiB7XG4gICAgICBhZGRyZXNzZXMucHVzaChhZGRyZXNzKTtcbiAgICAgIGFtb3VudHMucHVzaChhbW91bnQgYXMgc3RyaW5nKTtcbiAgICAgIHN1bSA9IHN1bS5wbHVzKGFtb3VudCk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgdmFsdWVzOiBbYWRkcmVzc2VzLCBhbW91bnRzXSxcbiAgICAgIHRvdGFsQW1vdW50OiBzdW0udG9GaXhlZCgpLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBkYXRhIHJlcXVpcmVkIHRvIG1ha2UgYW4gRVRIIGZ1bmN0aW9uIGNhbGwgZGVmaW5lZCBieSB0aGUgZ2l2ZW4gdHlwZXMgYW5kIHZhbHVlc1xuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gZnVuY3Rpb25OYW1lIC0gVGhlIG5hbWUgb2YgdGhlIGZ1bmN0aW9uIGJlaW5nIGNhbGxlZCwgZS5nLiB0cmFuc2ZlclxuICAgKiBAcGFyYW0gdHlwZXMgVGhlIHR5cGVzIG9mIHRoZSBmdW5jdGlvbiBjYWxsIGluIG9yZGVyXG4gICAqIEBwYXJhbSB2YWx1ZXMgVGhlIHZhbHVlcyBvZiB0aGUgZnVuY3Rpb24gY2FsbCBpbiBvcmRlclxuICAgKiBAcmV0dXJuIHtCdWZmZXJ9IFRoZSBjb21iaW5lZCBkYXRhIGZvciB0aGUgZnVuY3Rpb24gY2FsbFxuICAgKi9cbiAgZ2V0TWV0aG9kQ2FsbERhdGEgPSAoZnVuY3Rpb25OYW1lLCB0eXBlcywgdmFsdWVzKSA9PiB7XG4gICAgcmV0dXJuIEJ1ZmZlci5jb25jYXQoW1xuICAgICAgLy8gZnVuY3Rpb24gc2lnbmF0dXJlXG4gICAgICBvcHRpb25hbERlcHMuZXRoQWJpLm1ldGhvZElEKGZ1bmN0aW9uTmFtZSwgdHlwZXMpLFxuICAgICAgLy8gZnVuY3Rpb24gYXJndW1lbnRzXG4gICAgICBvcHRpb25hbERlcHMuZXRoQWJpLnJhd0VuY29kZSh0eXBlcywgdmFsdWVzKSxcbiAgICBdKTtcbiAgfTtcblxuICAvKipcbiAgICogQnVpbGQgYXJndW1lbnRzIHRvIGNhbGwgdGhlIHNlbmQgbWV0aG9kIG9uIHRoZSB3YWxsZXQgY29udHJhY3RcbiAgICogQHBhcmFtIHR4SW5mb1xuICAgKi9cbiAgZ2V0U2VuZE1ldGhvZEFyZ3ModHhJbmZvOiBHZXRTZW5kTWV0aG9kQXJnc09wdGlvbnMpOiBTZW5kTWV0aG9kQXJnc1tdIHtcbiAgICAvLyBNZXRob2Qgc2lnbmF0dXJlIGlzXG4gICAgLy8gc2VuZE11bHRpU2lnKGFkZHJlc3MgdG9BZGRyZXNzLCB1aW50IHZhbHVlLCBieXRlcyBkYXRhLCB1aW50IGV4cGlyZVRpbWUsIHVpbnQgc2VxdWVuY2VJZCwgYnl0ZXMgc2lnbmF0dXJlKVxuICAgIHJldHVybiBbXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICd0b0FkZHJlc3MnLFxuICAgICAgICB0eXBlOiAnYWRkcmVzcycsXG4gICAgICAgIHZhbHVlOiB0eEluZm8ucmVjaXBpZW50LmFkZHJlc3MsXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBuYW1lOiAndmFsdWUnLFxuICAgICAgICB0eXBlOiAndWludCcsXG4gICAgICAgIHZhbHVlOiB0eEluZm8ucmVjaXBpZW50LmFtb3VudCxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdkYXRhJyxcbiAgICAgICAgdHlwZTogJ2J5dGVzJyxcbiAgICAgICAgdmFsdWU6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLnRvQnVmZmVyKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmFkZEhleFByZWZpeCh0eEluZm8ucmVjaXBpZW50LmRhdGEgfHwgJycpKSxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdleHBpcmVUaW1lJyxcbiAgICAgICAgdHlwZTogJ3VpbnQnLFxuICAgICAgICB2YWx1ZTogdHhJbmZvLmV4cGlyZVRpbWUsXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBuYW1lOiAnc2VxdWVuY2VJZCcsXG4gICAgICAgIHR5cGU6ICd1aW50JyxcbiAgICAgICAgdmFsdWU6IHR4SW5mby5jb250cmFjdFNlcXVlbmNlSWQsXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBuYW1lOiAnc2lnbmF0dXJlJyxcbiAgICAgICAgdHlwZTogJ2J5dGVzJyxcbiAgICAgICAgdmFsdWU6IG9wdGlvbmFsRGVwcy5ldGhVdGlsLnRvQnVmZmVyKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmFkZEhleFByZWZpeCh0eEluZm8uc2lnbmF0dXJlKSksXG4gICAgICB9LFxuICAgIF07XG4gIH1cblxuICAvKipcbiAgICogUmVjb3ZlcnMgYSB0eCB3aXRoIFRTUyBrZXkgc2hhcmVzXG4gICAqIHNhbWUgZXhwZWN0ZWQgYXJndW1lbnRzIGFzIHJlY292ZXIgbWV0aG9kLCBidXQgd2l0aCBUU1Mga2V5IHNoYXJlc1xuICAgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIHJlY292ZXJUU1MoXG4gICAgcGFyYW1zOiBSZWNvdmVyT3B0aW9uc1xuICApOiBQcm9taXNlPFJlY292ZXJ5SW5mbyB8IE9mZmxpbmVWYXVsdFR4SW5mbyB8IFVuc2lnbmVkU3dlZXBUeE1QQ3YyPiB7XG4gICAgdGhpcy52YWxpZGF0ZVJlY292ZXJ5UGFyYW1zKHBhcmFtcyk7XG4gICAgLy8gQ2xlYW4gdXAgd2hpdGVzcGFjZSBmcm9tIGVudGVyZWQgdmFsdWVzXG4gICAgY29uc3QgdXNlclB1YmxpY09yUHJpdmF0ZUtleVNoYXJlID0gcGFyYW1zLnVzZXJLZXkucmVwbGFjZSgvXFxzL2csICcnKTtcbiAgICBjb25zdCBiYWNrdXBQcml2YXRlT3JQdWJsaWNLZXlTaGFyZSA9IHBhcmFtcy5iYWNrdXBLZXkucmVwbGFjZSgvXFxzL2csICcnKTtcblxuICAgIGlmIChcbiAgICAgIGdldElzVW5zaWduZWRTd2VlcCh7XG4gICAgICAgIHVzZXJLZXk6IHVzZXJQdWJsaWNPclByaXZhdGVLZXlTaGFyZSxcbiAgICAgICAgYmFja3VwS2V5OiBiYWNrdXBQcml2YXRlT3JQdWJsaWNLZXlTaGFyZSxcbiAgICAgICAgaXNUc3M6IHBhcmFtcy5pc1RzcyxcbiAgICAgIH0pXG4gICAgKSB7XG4gICAgICByZXR1cm4gdGhpcy5idWlsZFVuc2lnbmVkU3dlZXBUeG5UU1MocGFyYW1zKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgeyB1c2VyS2V5U2hhcmUsIGJhY2t1cEtleVNoYXJlLCBjb21tb25LZXlDaGFpbiB9ID0gYXdhaXQgRUNEU0FVdGlscy5nZXRNcGNWMlJlY292ZXJ5S2V5U2hhcmVzKFxuICAgICAgICB1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUsXG4gICAgICAgIGJhY2t1cFByaXZhdGVPclB1YmxpY0tleVNoYXJlLFxuICAgICAgICBwYXJhbXMud2FsbGV0UGFzc3BocmFzZSxcbiAgICAgICAgdGhpcy5iaXRnb1xuICAgICAgKTtcblxuICAgICAgY29uc3QgeyBnYXNMaW1pdCwgZ2FzUHJpY2UgfSA9IGF3YWl0IHRoaXMuZ2V0R2FzVmFsdWVzKHBhcmFtcyk7XG5cbiAgICAgIGNvbnN0IE1QQyA9IG5ldyBFY2RzYSgpO1xuICAgICAgY29uc3QgZGVyaXZlZENvbW1vbktleUNoYWluID0gTVBDLmRlcml2ZVVuaGFyZGVuZWQoY29tbW9uS2V5Q2hhaW4sICdtLzAnKTtcbiAgICAgIGNvbnN0IGJhY2t1cEtleVBhaXIgPSBuZXcgS2V5UGFpckxpYih7IHB1YjogZGVyaXZlZENvbW1vbktleUNoYWluLnNsaWNlKDAsIDY2KSB9KTtcbiAgICAgIGNvbnN0IGJhc2VBZGRyZXNzID0gYmFja3VwS2V5UGFpci5nZXRBZGRyZXNzKCk7XG4gICAgICBpZiAoYmFzZUFkZHJlc3MudG9Mb3dlckNhc2UoKSAhPT0gcGFyYW1zLndhbGxldENvbnRyYWN0QWRkcmVzcy50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIHRocm93IG5ldyBSZWNvdmVyeUtleU1pc21hdGNoRXJyb3IoXG4gICAgICAgICAgYGRlcml2ZWQgd2FsbGV0IGJhc2UgYWRkcmVzcyAke2Jhc2VBZGRyZXNzfSBkb2VzIG5vdCBtYXRjaCB0aGUgZ2l2ZW4gd2FsbGV0IGNvbnRyYWN0IGFkZHJlc3MgJHtwYXJhbXMud2FsbGV0Q29udHJhY3RBZGRyZXNzfWBcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHVuc2lnbmVkVHggPSAoYXdhaXQgdGhpcy5idWlsZFRzc1JlY292ZXJ5VHhuKGJhc2VBZGRyZXNzLCBnYXNQcmljZSwgZ2FzTGltaXQsIHBhcmFtcykpLnR4O1xuICAgICAgY29uc3QgbWVzc2FnZUhhc2ggPSB1bnNpZ25lZFR4LmdldE1lc3NhZ2VUb1NpZ24odHJ1ZSk7XG4gICAgICBjb25zdCBzaWduYXR1cmUgPSBhd2FpdCBFQ0RTQVV0aWxzLnNpZ25SZWNvdmVyeU1wY1YyKG1lc3NhZ2VIYXNoLCB1c2VyS2V5U2hhcmUsIGJhY2t1cEtleVNoYXJlLCBjb21tb25LZXlDaGFpbik7XG4gICAgICBjb25zdCBldGhDb21tbW9uID0gQWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuZ2V0RXRoTGlrZUNvbW1vbihwYXJhbXMuZWlwMTU1OSwgcGFyYW1zLnJlcGxheVByb3RlY3Rpb25PcHRpb25zKTtcbiAgICAgIGNvbnN0IHNpZ25lZFR4ID0gdGhpcy5nZXRTaWduZWRUeEZyb21TaWduYXR1cmUoZXRoQ29tbW1vbiwgdW5zaWduZWRUeCwgc2lnbmF0dXJlKTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaWQ6IGFkZEhleFByZWZpeChzaWduZWRUeC5oYXNoKCkudG9TdHJpbmcoJ2hleCcpKSxcbiAgICAgICAgdHg6IGFkZEhleFByZWZpeChzaWduZWRUeC5zZXJpYWxpemUoKS50b1N0cmluZygnaGV4JykpLFxuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGdldEdhc1ZhbHVlcyhwYXJhbXM6IFJlY292ZXJPcHRpb25zKTogUHJvbWlzZTx7IGdhc0xpbWl0OiBudW1iZXI7IGdhc1ByaWNlOiBCdWZmZXIgfT4ge1xuICAgIGNvbnN0IGdhc0xpbWl0ID0gbmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKHRoaXMuc2V0R2FzTGltaXQocGFyYW1zLmdhc0xpbWl0KSk7XG4gICAgY29uc3QgZ2FzUHJpY2UgPSBwYXJhbXMuZWlwMTU1OVxuICAgICAgPyBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4ocGFyYW1zLmVpcDE1NTkubWF4RmVlUGVyR2FzKVxuICAgICAgOiBuZXcgb3B0aW9uYWxEZXBzLmV0aFV0aWwuQk4odGhpcy5zZXRHYXNQcmljZShwYXJhbXMuZ2FzUHJpY2UpKTtcbiAgICByZXR1cm4geyBnYXNMaW1pdCwgZ2FzUHJpY2UgfTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhc3luYyBidWlsZFVuc2lnbmVkU3dlZXBUeG5UU1MocGFyYW1zOiBSZWNvdmVyT3B0aW9ucyk6IFByb21pc2U8T2ZmbGluZVZhdWx0VHhJbmZvIHwgVW5zaWduZWRTd2VlcFR4TVBDdjI+IHtcbiAgICBjb25zdCB1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUgPSBwYXJhbXMudXNlcktleS5yZXBsYWNlKC9cXHMvZywgJycpO1xuICAgIGNvbnN0IGJhY2t1cFByaXZhdGVPclB1YmxpY0tleVNoYXJlID0gcGFyYW1zLmJhY2t1cEtleS5yZXBsYWNlKC9cXHMvZywgJycpO1xuXG4gICAgY29uc3QgeyBnYXNMaW1pdCwgZ2FzUHJpY2UgfSA9IGF3YWl0IHRoaXMuZ2V0R2FzVmFsdWVzKHBhcmFtcyk7XG5cbiAgICBjb25zdCBiYWNrdXBLZXlQYWlyID0gbmV3IEtleVBhaXJMaWIoeyBwdWI6IGJhY2t1cFByaXZhdGVPclB1YmxpY0tleVNoYXJlIH0pO1xuICAgIGNvbnN0IGJhc2VBZGRyZXNzID0gYmFja3VwS2V5UGFpci5nZXRBZGRyZXNzKCk7XG4gICAgY29uc3QgeyB0eEluZm8sIHR4LCBub25jZSB9ID0gYXdhaXQgdGhpcy5idWlsZFRzc1JlY292ZXJ5VHhuKGJhc2VBZGRyZXNzLCBnYXNQcmljZSwgZ2FzTGltaXQsIHBhcmFtcyk7XG4gICAgcmV0dXJuIHRoaXMuZm9ybWF0Rm9yT2ZmbGluZVZhdWx0VFNTKFxuICAgICAgdHhJbmZvLFxuICAgICAgdHgsXG4gICAgICB1c2VyUHVibGljT3JQcml2YXRlS2V5U2hhcmUsXG4gICAgICBiYWNrdXBQcml2YXRlT3JQdWJsaWNLZXlTaGFyZSxcbiAgICAgIGdhc1ByaWNlLFxuICAgICAgZ2FzTGltaXQsXG4gICAgICBub25jZSxcbiAgICAgIHBhcmFtcy5laXAxNTU5LFxuICAgICAgcGFyYW1zLnJlcGxheVByb3RlY3Rpb25PcHRpb25zXG4gICAgKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhc3luYyBidWlsZFVuc2lnbmVkU3dlZXBUeG5NUEN2MihwYXJhbXM6IFJlY292ZXJPcHRpb25zKTogUHJvbWlzZTxVbnNpZ25lZFN3ZWVwVHhNUEN2Mj4ge1xuICAgIGNvbnN0IHsgZ2FzTGltaXQsIGdhc1ByaWNlIH0gPSBhd2FpdCB0aGlzLmdldEdhc1ZhbHVlcyhwYXJhbXMpO1xuXG4gICAgY29uc3QgcmVjb3ZlclBhcmFtcyA9IHBhcmFtcyBhcyBSZWNvdmVyT3B0aW9ucztcbiAgICB0aGlzLnZhbGlkYXRlVW5zaWduZWRTd2VlcFRTU1BhcmFtcyhyZWNvdmVyUGFyYW1zKTtcblxuICAgIGNvbnN0IGRlcml2YXRpb25QYXRoID0gcmVjb3ZlclBhcmFtcy5kZXJpdmF0aW9uU2VlZCA/IGdldERlcml2YXRpb25QYXRoKHJlY292ZXJQYXJhbXMuZGVyaXZhdGlvblNlZWQpIDogJ20vMCc7XG4gICAgY29uc3QgTVBDID0gbmV3IEVjZHNhKCk7XG4gICAgY29uc3QgZGVyaXZlZENvbW1vbktleUNoYWluID0gTVBDLmRlcml2ZVVuaGFyZGVuZWQocmVjb3ZlclBhcmFtcy5iYWNrdXBLZXkgYXMgc3RyaW5nLCBkZXJpdmF0aW9uUGF0aCk7XG4gICAgY29uc3QgYmFja3VwS2V5UGFpciA9IG5ldyBLZXlQYWlyTGliKHsgcHViOiBkZXJpdmVkQ29tbW9uS2V5Q2hhaW4uc2xpY2UoMCwgNjYpIH0pO1xuICAgIGNvbnN0IGJhc2VBZGRyZXNzID0gYmFja3VwS2V5UGFpci5nZXRBZGRyZXNzKCk7XG4gICAgY29uc3QgeyB0eEluZm8sIHR4LCBub25jZSB9ID0gYXdhaXQgdGhpcy5idWlsZFRzc1JlY292ZXJ5VHhuKGJhc2VBZGRyZXNzLCBnYXNQcmljZSwgZ2FzTGltaXQsIHBhcmFtcyk7XG4gICAgcmV0dXJuIHRoaXMuYnVpbGRUeFJlcXVlc3RGb3JPZmZsaW5lVmF1bHRNUEN2MihcbiAgICAgIHR4SW5mbyxcbiAgICAgIHR4LFxuICAgICAgZGVyaXZhdGlvblBhdGgsXG4gICAgICBub25jZSxcbiAgICAgIGdhc1ByaWNlLFxuICAgICAgZ2FzTGltaXQsXG4gICAgICBwYXJhbXMuZWlwMTU1OSxcbiAgICAgIHBhcmFtcy5yZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyxcbiAgICAgIHJlY292ZXJQYXJhbXMuYmFja3VwS2V5IGFzIHN0cmluZ1xuICAgICk7XG4gIH1cblxuICBhc3luYyBjcmVhdGVCcm9hZGNhc3RhYmxlU3dlZXBUcmFuc2FjdGlvbihwYXJhbXM6IE1QQ1N3ZWVwUmVjb3ZlcnlPcHRpb25zKTogUHJvbWlzZTxNUENUeHM+IHtcbiAgICBjb25zdCByZXEgPSBwYXJhbXMuc2lnbmF0dXJlU2hhcmVzO1xuICAgIGNvbnN0IGJyb2FkY2FzdGFibGVUcmFuc2FjdGlvbnM6IE1QQ1R4W10gPSBbXTtcbiAgICBsZXQgbGFzdFNjYW5JbmRleCA9IDA7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJlcS5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSByZXFbaV0/LnR4UmVxdWVzdD8udHJhbnNhY3Rpb25zPy5bMF0/LnVuc2lnbmVkVHggYXMgdW5rbm93biBhcyBVbnNpZ25lZFRyYW5zYWN0aW9uVHNzO1xuICAgICAgaWYgKCFyZXFbaV0ub3ZjIHx8ICFyZXFbaV0ub3ZjWzBdLmVjZHNhU2lnbmF0dXJlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTWlzc2luZyBzaWduYXR1cmUocyknKTtcbiAgICAgIH1cbiAgICAgIGlmICghdHJhbnNhY3Rpb24uc2lnbmFibGVIZXgpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNaXNzaW5nIHNpZ25hYmxlIGhleCcpO1xuICAgICAgfVxuICAgICAgY29uc3Qgc2lnbmF0dXJlID0gcmVxW2ldLm92Y1swXS5lY2RzYVNpZ25hdHVyZTtcbiAgICAgIGlmICghc2lnbmF0dXJlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignU2lnbmF0dXJlIGlzIHVuZGVmaW5lZCcpO1xuICAgICAgfVxuICAgICAgY29uc3Qgc2hhcmVzOiBzdHJpbmdbXSA9IHNpZ25hdHVyZS50b1N0cmluZygpLnNwbGl0KCc6Jyk7XG4gICAgICBpZiAoc2hhcmVzLmxlbmd0aCAhPT0gNCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgc2lnbmF0dXJlJyk7XG4gICAgICB9XG4gICAgICBjb25zdCBmaW5hbFNpZ25hdHVyZTogRUNEU0FNZXRob2RUeXBlcy5TaWduYXR1cmUgPSB7XG4gICAgICAgIHJlY2lkOiBOdW1iZXIoc2hhcmVzWzBdKSxcbiAgICAgICAgcjogc2hhcmVzWzFdLFxuICAgICAgICBzOiBzaGFyZXNbMl0sXG4gICAgICAgIHk6IHNoYXJlc1szXSxcbiAgICAgIH0gYXMgdW5rbm93biBhcyBFQ0RTQU1ldGhvZFR5cGVzLlNpZ25hdHVyZTtcblxuICAgICAgaWYgKCF0cmFuc2FjdGlvbi5jb2luU3BlY2lmaWM/LmNvbW1vbktleUNoYWluKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgTWlzc2luZyBjb21tb24ga2V5Y2hhaW4gZm9yIHRyYW5zYWN0aW9uIGF0IGluZGV4ICR7aX1gKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGNvbW1vbktleUNoYWluID0gdHJhbnNhY3Rpb24uY29pblNwZWNpZmljLmNvbW1vbktleUNoYWluO1xuICAgICAgaWYgKCF0cmFuc2FjdGlvbi5kZXJpdmF0aW9uUGF0aCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE1pc3NpbmcgZGVyaXZhdGlvbiBwYXRoIGZvciB0cmFuc2FjdGlvbiBhdCBpbmRleCAke2l9YCk7XG4gICAgICB9XG4gICAgICBpZiAoIWNvbW1vbktleUNoYWluKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgTWlzc2luZyBjb21tb24ga2V5IGNoYWluIGZvciB0cmFuc2FjdGlvbiBhdCBpbmRleCAke2l9YCk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGV0aENvbW1tb24gPSBBYnN0cmFjdEV0aExpa2VOZXdDb2lucy5nZXRFdGhMaWtlQ29tbW9uKFxuICAgICAgICB0cmFuc2FjdGlvbi5laXAxNTU5LFxuICAgICAgICB0cmFuc2FjdGlvbi5yZXBsYXlQcm90ZWN0aW9uT3B0aW9uc1xuICAgICAgKTtcbiAgICAgIGxldCB1bnNpZ25lZFR4OiBFdGhMaWtlVHhMaWIuRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uIHwgRXRoTGlrZVR4TGliLlRyYW5zYWN0aW9uO1xuICAgICAgaWYgKHRyYW5zYWN0aW9uLmVpcDE1NTkpIHtcbiAgICAgICAgdW5zaWduZWRUeCA9IEZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvbi5mcm9tU2VyaWFsaXplZFR4KEJ1ZmZlci5mcm9tKHRyYW5zYWN0aW9uLnNlcmlhbGl6ZWRUeEhleCwgJ2hleCcpKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHVuc2lnbmVkVHggPSBMZWdhY3lUcmFuc2FjdGlvbi5mcm9tU2VyaWFsaXplZFR4KEJ1ZmZlci5mcm9tKHRyYW5zYWN0aW9uLnNlcmlhbGl6ZWRUeEhleCwgJ2hleCcpKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHNpZ25lZFR4ID0gdGhpcy5nZXRTaWduZWRUeEZyb21TaWduYXR1cmUoZXRoQ29tbW1vbiwgdW5zaWduZWRUeCwgZmluYWxTaWduYXR1cmUpO1xuICAgICAgYnJvYWRjYXN0YWJsZVRyYW5zYWN0aW9ucy5wdXNoKHtcbiAgICAgICAgc2VyaWFsaXplZFR4OiBhZGRIZXhQcmVmaXgoc2lnbmVkVHguc2VyaWFsaXplKCkudG9TdHJpbmcoJ2hleCcpKSxcbiAgICAgIH0pO1xuXG4gICAgICBpZiAoaSA9PT0gcmVxLmxlbmd0aCAtIDEgJiYgdHJhbnNhY3Rpb24uY29pblNwZWNpZmljPy5sYXN0U2NhbkluZGV4KSB7XG4gICAgICAgIGxhc3RTY2FuSW5kZXggPSB0cmFuc2FjdGlvbi5jb2luU3BlY2lmaWM/Lmxhc3RTY2FuSW5kZXggYXMgbnVtYmVyO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7IHRyYW5zYWN0aW9uczogYnJvYWRjYXN0YWJsZVRyYW5zYWN0aW9ucywgbGFzdFNjYW5JbmRleCB9O1xuICB9XG5cbiAgLyoqXG4gICAqIE1ldGhvZCB0byB2YWxpZGF0ZSByZWNvdmVyeSBwYXJhbXNcbiAgICogQHBhcmFtIHtSZWNvdmVyT3B0aW9uc30gcGFyYW1zXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB2YWxpZGF0ZVVuc2lnbmVkU3dlZXBUU1NQYXJhbXMocGFyYW1zOiBSZWNvdmVyT3B0aW9ucyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmIChfLmlzVW5kZWZpbmVkKHBhcmFtcy5iYWNrdXBLZXkpICYmIHBhcmFtcy5iYWNrdXBLZXkgPT09ICcnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3NpbmcgY29tbW9uS2V5Q2hhaW4nKTtcbiAgICB9XG4gICAgaWYgKCFfLmlzVW5kZWZpbmVkKHBhcmFtcy5kZXJpdmF0aW9uU2VlZCkgJiYgdHlwZW9mIHBhcmFtcy5kZXJpdmF0aW9uU2VlZCAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCBkZXJpdmF0aW9uU2VlZCcpO1xuICAgIH1cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChwYXJhbXMucmVjb3ZlcnlEZXN0aW5hdGlvbikgfHwgIXRoaXMuaXNWYWxpZEFkZHJlc3MocGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24pKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21pc3Npbmcgb3IgaW52YWxpZCBkZXN0aW5hdGlvbkFkZHJlc3MnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSGVscGVyIGZ1bmN0aW9uIGZvciByZWNvdmVyKClcbiAgICogVGhpcyB0cmFuc2Zvcm1zIHRoZSB1bnNpZ25lZCB0cmFuc2FjdGlvbiBpbmZvcm1hdGlvbiBpbnRvIGEgZm9ybWF0IHRoZSBCaXRHbyBvZmZsaW5lIHZhdWx0IGV4cGVjdHNcbiAgICogQHBhcmFtIHtVbmZvcm1hdHRlZFR4SW5mb30gdHhJbmZvIC0gdHggaW5mb1xuICAgKiBAcGFyYW0ge0xlZ2FjeVRyYW5zYWN0aW9uIHwgRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9ufSBldGhUeCAtIHRoZSBldGhlcmV1bWpzIHR4IG9iamVjdFxuICAgKiBAcGFyYW0ge3N0cmluZ30gZGVyaXZhdGlvblBhdGggLSB0aGUgZGVyaXZhdGlvblBhdGhcbiAgICogQHBhcmFtIHtudW1iZXJ9IG5vbmNlIC0gdGhlIG5vbmNlIG9mIHRoZSBiYWNrdXAga2V5IGFkZHJlc3NcbiAgICogQHBhcmFtIHtCdWZmZXJ9IGdhc1ByaWNlIC0gZ2FzIHByaWNlIGZvciB0aGUgdHhcbiAgICogQHBhcmFtIHtudW1iZXJ9IGdhc0xpbWl0IC0gZ2FzIGxpbWl0IGZvciB0aGUgdHhcbiAgICogQHBhcmFtIHtFSVAxNTU5fSBlaXAxNTU5IC0gZWlwMTU1OSBwYXJhbXNcbiAgICogQHBhcmFtIHJlcGxheVByb3RlY3Rpb25PcHRpb25zXG4gICAqIEBwYXJhbSBjb21tb25LZXlDaGFpblxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPZmZsaW5lVmF1bHRUeEluZm8+fVxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZFR4UmVxdWVzdEZvck9mZmxpbmVWYXVsdE1QQ3YyKFxuICAgIHR4SW5mbzogVW5mb3JtYXR0ZWRUeEluZm8sXG4gICAgZXRoVHg6IExlZ2FjeVRyYW5zYWN0aW9uIHwgRmVlTWFya2V0RUlQMTU1OVRyYW5zYWN0aW9uLFxuICAgIGRlcml2YXRpb25QYXRoOiBzdHJpbmcsXG4gICAgbm9uY2U6IG51bWJlcixcbiAgICBnYXNQcmljZTogQnVmZmVyLFxuICAgIGdhc0xpbWl0OiBudW1iZXIsXG4gICAgZWlwMTU1OT86IEVJUDE1NTksXG4gICAgcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM/OiBSZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyxcbiAgICBjb21tb25LZXlDaGFpbj86IHN0cmluZ1xuICApOiBVbnNpZ25lZFN3ZWVwVHhNUEN2MiB7XG4gICAgaWYgKCFldGhUeC50bykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdFdGggdHggbXVzdCBoYXZlIGEgYHRvYCBhZGRyZXNzJyk7XG4gICAgfVxuXG4gICAgY29uc3QgZmVlID0gZWlwMTU1OVxuICAgICAgPyBnYXNMaW1pdCAqIGVpcDE1NTkubWF4RmVlUGVyR2FzXG4gICAgICA6IGdhc0xpbWl0ICogb3B0aW9uYWxEZXBzLmV0aFV0aWwuYnVmZmVyVG9JbnQoZ2FzUHJpY2UpLnRvRml4ZWQoKTtcblxuICAgIGNvbnN0IHVuc2lnbmVkVHg6IFVuc2lnbmVkVHJhbnNhY3Rpb25Uc3MgPSB7XG4gICAgICBzZXJpYWxpemVkVHhIZXg6IGV0aFR4LnNlcmlhbGl6ZSgpLnRvU3RyaW5nKCdoZXgnKSxcbiAgICAgIHNpZ25hYmxlSGV4OlxuICAgICAgICBldGhUeCBpbnN0YW5jZW9mIEZlZU1hcmtldEVJUDE1NTlUcmFuc2FjdGlvblxuICAgICAgICAgID8gZXRoVHguZ2V0TWVzc2FnZVRvU2lnbihmYWxzZSkudG9TdHJpbmcoJ2hleCcpXG4gICAgICAgICAgOiBCdWZmZXIuZnJvbShSTFAuZW5jb2RlKGJ1ZkFyclRvQXJyKGV0aFR4LmdldE1lc3NhZ2VUb1NpZ24oZmFsc2UpKSkpLnRvU3RyaW5nKCdoZXgnKSxcbiAgICAgIGRlcml2YXRpb25QYXRoOiBkZXJpdmF0aW9uUGF0aCxcbiAgICAgIGZlZUluZm86IHtcbiAgICAgICAgZmVlOiBmZWUsXG4gICAgICAgIGZlZVN0cmluZzogZmVlLnRvU3RyaW5nKCksXG4gICAgICB9LFxuICAgICAgcGFyc2VkVHg6IHtcbiAgICAgICAgc3BlbmRBbW91bnQ6IHR4SW5mby5yZWNpcGllbnQuYW1vdW50LFxuICAgICAgICBvdXRwdXRzOiBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgY29pbk5hbWU6IHRoaXMuZ2V0Q2hhaW4oKSxcbiAgICAgICAgICAgIGFkZHJlc3M6IHR4SW5mby5yZWNpcGllbnQuYWRkcmVzcyxcbiAgICAgICAgICAgIHZhbHVlU3RyaW5nOiB0eEluZm8ucmVjaXBpZW50LmFtb3VudCxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgfSxcbiAgICAgIGNvaW5TcGVjaWZpYzoge1xuICAgICAgICBjb21tb25LZXlDaGFpbjogY29tbW9uS2V5Q2hhaW4sXG4gICAgICB9LFxuICAgICAgZWlwMTU1OTogZWlwMTU1OSxcbiAgICAgIHJlcGxheVByb3RlY3Rpb25PcHRpb25zOiByZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyxcbiAgICB9O1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHR4UmVxdWVzdHM6IFtcbiAgICAgICAge1xuICAgICAgICAgIHdhbGxldENvaW46IHRoaXMuZ2V0Q2hhaW4oKSxcbiAgICAgICAgICB0cmFuc2FjdGlvbnM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgdW5zaWduZWRUeDogdW5zaWduZWRUeCxcbiAgICAgICAgICAgICAgbm9uY2U6IG5vbmNlLFxuICAgICAgICAgICAgICBzaWduYXR1cmVTaGFyZXM6IFtdLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBidWlsZFRzc1JlY292ZXJ5VHhuKGJhc2VBZGRyZXNzOiBzdHJpbmcsIGdhc1ByaWNlOiBhbnksIGdhc0xpbWl0OiBhbnksIHBhcmFtczogUmVjb3Zlck9wdGlvbnMpIHtcbiAgICBjb25zdCB0eEFtb3VudCA9IGF3YWl0IHRoaXMudmFsaWRhdGVCYWxhbmNlQW5kR2V0VHhBbW91bnQoYmFzZUFkZHJlc3MsIGdhc1ByaWNlLCBnYXNMaW1pdCwgcGFyYW1zLmFwaUtleSk7XG4gICAgY29uc3Qgbm9uY2UgPSBhd2FpdCB0aGlzLmdldEFkZHJlc3NOb25jZShiYXNlQWRkcmVzcywgcGFyYW1zLmFwaUtleSk7XG4gICAgY29uc3QgcmVjaXBpZW50cyA9IFtcbiAgICAgIHtcbiAgICAgICAgYWRkcmVzczogcGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24sXG4gICAgICAgIGFtb3VudDogdHhBbW91bnQudG9TdHJpbmcoMTApLFxuICAgICAgfSxcbiAgICBdO1xuXG4gICAgY29uc3QgdHhJbmZvID0ge1xuICAgICAgcmVjaXBpZW50OiByZWNpcGllbnRzWzBdLFxuICAgICAgZXhwaXJlVGltZTogdGhpcy5nZXREZWZhdWx0RXhwaXJlVGltZSgpLFxuICAgICAgZ2FzTGltaXQ6IGdhc0xpbWl0LnRvU3RyaW5nKDEwKSxcbiAgICB9O1xuXG4gICAgY29uc3QgdHhQYXJhbXMgPSB7XG4gICAgICB0bzogcGFyYW1zLnJlY292ZXJ5RGVzdGluYXRpb24sXG4gICAgICBub25jZTogbm9uY2UsXG4gICAgICB2YWx1ZTogdHhBbW91bnQsXG4gICAgICBnYXNQcmljZTogZ2FzUHJpY2UsXG4gICAgICBnYXNMaW1pdDogZ2FzTGltaXQsXG4gICAgICBkYXRhOiBCdWZmZXIuZnJvbSgnMHgnKSxcbiAgICAgIGVpcDE1NTk6IHBhcmFtcy5laXAxNTU5LFxuICAgICAgcmVwbGF5UHJvdGVjdGlvbk9wdGlvbnM6IHBhcmFtcy5yZXBsYXlQcm90ZWN0aW9uT3B0aW9ucyxcbiAgICB9O1xuXG4gICAgY29uc3QgdHggPSBBYnN0cmFjdEV0aExpa2VOZXdDb2lucy5idWlsZFRyYW5zYWN0aW9uKHR4UGFyYW1zKTtcbiAgICByZXR1cm4geyB0eEluZm8sIHR4LCBub25jZSB9O1xuICB9XG5cbiAgYXN5bmMgdmFsaWRhdGVCYWxhbmNlQW5kR2V0VHhBbW91bnQoYmFzZUFkZHJlc3M6IHN0cmluZywgZ2FzUHJpY2U6IEJOLCBnYXNMaW1pdDogQk4sIGFwaUtleT86IHN0cmluZykge1xuICAgIGNvbnN0IGJhc2VBZGRyZXNzQmFsYW5jZSA9IGF3YWl0IHRoaXMucXVlcnlBZGRyZXNzQmFsYW5jZShiYXNlQWRkcmVzcywgYXBpS2V5KTtcbiAgICBsZXQgdG90YWxHYXNOZWVkZWQgPSBnYXNQcmljZS5tdWwoZ2FzTGltaXQpO1xuICAgIC8vIE9uIEwyIGNoYWlucyB3aXRoIEwxIGRhdGEgZmVlcywgYWRkIGJ1ZmZlciBmb3IgTDEgZmVlc1xuICAgIGlmICh0aGlzLnN0YXRpY3NDb2luPy5mYW1pbHkgIT09IHVuZGVmaW5lZCAmJiBjb2luRmFtaWxpZXNXaXRoTDFGZWVzLmluY2x1ZGVzKHRoaXMuc3RhdGljc0NvaW4uZmFtaWx5KSkge1xuICAgICAgdG90YWxHYXNOZWVkZWQgPSB0b3RhbEdhc05lZWRlZC5hZGQobmV3IG9wdGlvbmFsRGVwcy5ldGhVdGlsLkJOKGV0aEdhc0NvbmZpZ3MubDFHYXNGZWVCdWZmZXIpKTtcbiAgICB9XG4gICAgY29uc3Qgd2VpVG9Hd2VpID0gbmV3IEJOKDEwICoqIDkpO1xuICAgIGlmIChiYXNlQWRkcmVzc0JhbGFuY2UubHQodG90YWxHYXNOZWVkZWQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBCYWNrdXAga2V5IGFkZHJlc3MgJHtiYXNlQWRkcmVzc30gaGFzIGJhbGFuY2UgJHtiYXNlQWRkcmVzc0JhbGFuY2UuZGl2KHdlaVRvR3dlaSkudG9TdHJpbmcoKX0gR3dlaS5gICtcbiAgICAgICAgICBgVGhpcyBhZGRyZXNzIG11c3QgaGF2ZSBhIGJhbGFuY2Ugb2YgYXQgbGVhc3QgJHt0b3RhbEdhc05lZWRlZC5kaXYod2VpVG9Hd2VpKS50b1N0cmluZygpfWAgK1xuICAgICAgICAgIGAgR3dlaSB0byBwZXJmb3JtIHJlY292ZXJpZXMuIFRyeSBzZW5kaW5nIHNvbWUgRVRIIHRvIHRoaXMgYWRkcmVzcyB0aGVuIHJldHJ5LmBcbiAgICAgICk7XG4gICAgfVxuICAgIGNvbnN0IHR4QW1vdW50ID0gYmFzZUFkZHJlc3NCYWxhbmNlLnN1Yih0b3RhbEdhc05lZWRlZCk7XG4gICAgcmV0dXJuIHR4QW1vdW50O1xuICB9XG5cbiAgLyoqXG4gICAqIE1ha2UgYSBxdWVyeSB0byBibG9ja2NoYWluIGV4cGxvcmVyIGZvciBpbmZvcm1hdGlvbiBzdWNoIGFzIGJhbGFuY2UsIHRva2VuIGJhbGFuY2UsIHNvbGlkaXR5IGNhbGxzXG4gICAqIEBwYXJhbSBxdWVyeSB7T2JqZWN0fSBrZXktdmFsdWUgcGFpcnMgb2YgcGFyYW1ldGVycyB0byBhcHBlbmQgYWZ0ZXIgL2FwaVxuICAgKiBAcGFyYW0gYXBpS2V5IHtzdHJpbmd9IG9wdGlvbmFsIEFQSSBrZXkgdG8gdXNlIGluc3RlYWQgb2YgdGhlIG9uZSBmcm9tIHRoZSBlbnZpcm9ubWVudFxuICAgKiBAcmV0dXJucyB7T2JqZWN0fSByZXNwb25zZSBmcm9tIHRoZSBibG9ja2NoYWluIGV4cGxvcmVyXG4gICAqL1xuICBhc3luYyByZWNvdmVyeUJsb2NrY2hhaW5FeHBsb3JlclF1ZXJ5KHF1ZXJ5OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+LCBhcGlLZXk/OiBzdHJpbmcpOiBQcm9taXNlPGFueT4ge1xuICAgIHRocm93IG5ldyBFcnJvcignbWV0aG9kIG5vdCBpbXBsZW1lbnRlZCcpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgdGhlIGV4dHJhIHBhcmFtZXRlcnMgbmVlZGVkIHRvIGJ1aWxkIGEgaG9wIHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSBidWlsZFBhcmFtcyBUaGUgb3JpZ2luYWwgYnVpbGQgcGFyYW1ldGVyc1xuICAgKiBAcmV0dXJucyBleHRyYSBwYXJhbWV0ZXJzIG9iamVjdCB0byBtZXJnZSB3aXRoIHRoZSBvcmlnaW5hbCBidWlsZCBwYXJhbWV0ZXJzIG9iamVjdCBhbmQgc2VuZCB0byB0aGUgcGxhdGZvcm1cbiAgICovXG4gIGFzeW5jIGNyZWF0ZUhvcFRyYW5zYWN0aW9uUGFyYW1zKGJ1aWxkUGFyYW1zOiBIb3BUcmFuc2FjdGlvbkJ1aWxkT3B0aW9ucyk6IFByb21pc2U8SG9wUGFyYW1zPiB7XG4gICAgY29uc3Qgd2FsbGV0ID0gYnVpbGRQYXJhbXMud2FsbGV0O1xuICAgIGNvbnN0IHJlY2lwaWVudHMgPSBidWlsZFBhcmFtcy5yZWNpcGllbnRzO1xuICAgIGNvbnN0IHdhbGxldFBhc3NwaHJhc2UgPSBidWlsZFBhcmFtcy53YWxsZXRQYXNzcGhyYXNlO1xuXG4gICAgY29uc3QgdXNlcktleWNoYWluID0gYXdhaXQgdGhpcy5rZXljaGFpbnMoKS5nZXQoeyBpZDogd2FsbGV0LmtleUlkcygpWzBdIH0pO1xuICAgIGNvbnN0IHVzZXJQcnYgPSBhd2FpdCB3YWxsZXQuZ2V0VXNlclBydih7IGtleWNoYWluOiB1c2VyS2V5Y2hhaW4sIHdhbGxldFBhc3NwaHJhc2UgfSk7XG4gICAgY29uc3QgdXNlclBydkJ1ZmZlciA9IGJpcDMyLmZyb21CYXNlNTgodXNlclBydikucHJpdmF0ZUtleTtcbiAgICBpZiAoIXVzZXJQcnZCdWZmZXIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCB1c2VyUHJ2Jyk7XG4gICAgfVxuICAgIGlmICghcmVjaXBpZW50cyB8fCAhQXJyYXkuaXNBcnJheShyZWNpcGllbnRzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdleHBlY3RpbmcgYXJyYXkgb2YgcmVjaXBpZW50cycpO1xuICAgIH1cblxuICAgIC8vIFJpZ2h0IG5vdyB3ZSBvbmx5IHN1cHBvcnQgMSByZWNpcGllbnRcbiAgICBpZiAocmVjaXBpZW50cy5sZW5ndGggIT09IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbXVzdCBzZW5kIHRvIGV4YWN0bHkgMSByZWNpcGllbnQnKTtcbiAgICB9XG4gICAgY29uc3QgcmVjaXBpZW50QWRkcmVzcyA9IHJlY2lwaWVudHNbMF0uYWRkcmVzcztcbiAgICBjb25zdCByZWNpcGllbnRBbW91bnQgPSByZWNpcGllbnRzWzBdLmFtb3VudCBhcyBzdHJpbmc7XG4gICAgY29uc3QgZmVlRXN0aW1hdGVQYXJhbXMgPSB7XG4gICAgICByZWNpcGllbnQ6IHJlY2lwaWVudEFkZHJlc3MsXG4gICAgICBhbW91bnQ6IHJlY2lwaWVudEFtb3VudCxcbiAgICAgIGhvcDogdHJ1ZSxcbiAgICB9O1xuICAgIGNvbnN0IGZlZUVzdGltYXRlOiBGZWVFc3RpbWF0ZSA9IGF3YWl0IHRoaXMuZmVlRXN0aW1hdGUoZmVlRXN0aW1hdGVQYXJhbXMpO1xuXG4gICAgY29uc3QgZ2FzTGltaXQgPSBmZWVFc3RpbWF0ZS5nYXNMaW1pdEVzdGltYXRlO1xuICAgIGNvbnN0IGdhc1ByaWNlID0gTWF0aC5yb3VuZChmZWVFc3RpbWF0ZS5mZWVFc3RpbWF0ZSAvIGdhc0xpbWl0KTtcbiAgICBjb25zdCBnYXNQcmljZU1heCA9IGdhc1ByaWNlICogNTtcbiAgICAvLyBQYXltZW50IGlkIGEgcmFuZG9tIG51bWJlciBzbyBpdHMgZGlmZmVyZW50IGZvciBldmVyeSB0eFxuICAgIGNvbnN0IHBheW1lbnRJZCA9IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIDEwMDAwMDAwMDAwKS50b1N0cmluZygpO1xuICAgIGNvbnN0IGhvcERpZ2VzdDogQnVmZmVyID0gQWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuZ2V0SG9wRGlnZXN0KFtcbiAgICAgIHJlY2lwaWVudEFkZHJlc3MsXG4gICAgICByZWNpcGllbnRBbW91bnQsXG4gICAgICBnYXNQcmljZU1heC50b1N0cmluZygpLFxuICAgICAgZ2FzTGltaXQudG9TdHJpbmcoKSxcbiAgICAgIHBheW1lbnRJZCxcbiAgICBdKTtcblxuICAgIGNvbnN0IHVzZXJSZXFTaWcgPSBvcHRpb25hbERlcHMuZXRoVXRpbC5hZGRIZXhQcmVmaXgoXG4gICAgICBCdWZmZXIuZnJvbShzZWNwMjU2azEuZWNkc2FTaWduKGhvcERpZ2VzdCwgdXNlclBydkJ1ZmZlcikuc2lnbmF0dXJlKS50b1N0cmluZygnaGV4JylcbiAgICApO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIGhvcFBhcmFtczoge1xuICAgICAgICBnYXNQcmljZU1heCxcbiAgICAgICAgdXNlclJlcVNpZyxcbiAgICAgICAgcGF5bWVudElkLFxuICAgICAgICBnYXNMaW1pdCxcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZXMgdGhhdCB0aGUgaG9wIHByZWJ1aWxkIGZyb20gdGhlIEhTTSBpcyB2YWxpZCBhbmQgY29ycmVjdFxuICAgKiBAcGFyYW0ge0lXYWxsZXR9IHdhbGxldCAtIFRoZSB3YWxsZXQgdGhhdCB0aGUgcHJlYnVpbGQgaXMgZm9yXG4gICAqIEBwYXJhbSB7SG9wUHJlYnVpbGR9IGhvcFByZWJ1aWxkIC0gVGhlIHByZWJ1aWxkIHRvIHZhbGlkYXRlXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBvcmlnaW5hbFBhcmFtcyAtIFRoZSBvcmlnaW5hbCBwYXJhbWV0ZXJzIHBhc3NlZCB0byBwcmVidWlsZFRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSB7UmVjaXBpZW50W119IG9yaWdpbmFsUGFyYW1zLnJlY2lwaWVudHMgLSBUaGUgb3JpZ2luYWwgcmVjaXBpZW50cyBhcnJheVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICogQHRocm93cyBFcnJvciBpZiBUaGUgcHJlYnVpbGQgaXMgaW52YWxpZFxuICAgKi9cbiAgYXN5bmMgdmFsaWRhdGVIb3BQcmVidWlsZChcbiAgICB3YWxsZXQ6IElXYWxsZXQsXG4gICAgaG9wUHJlYnVpbGQ6IEhvcFByZWJ1aWxkLFxuICAgIG9yaWdpbmFsUGFyYW1zPzogeyByZWNpcGllbnRzOiBSZWNpcGllbnRbXSB9XG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgdHgsIGlkLCBzaWduYXR1cmUgfSA9IGhvcFByZWJ1aWxkO1xuXG4gICAgLy8gZmlyc3QsIHZhbGlkYXRlIHRoZSBIU00gc2lnbmF0dXJlXG4gICAgY29uc3Qgc2VydmVyWHB1YiA9IGNvbW1vbi5FbnZpcm9ubWVudHNbdGhpcy5iaXRnby5nZXRFbnYoKV0uaHNtWHB1YjtcbiAgICBjb25zdCBzZXJ2ZXJQdWJrZXlCdWZmZXI6IEJ1ZmZlciA9IGJpcDMyLmZyb21CYXNlNTgoc2VydmVyWHB1YikucHVibGljS2V5O1xuICAgIGNvbnN0IHNpZ25hdHVyZUJ1ZmZlcjogQnVmZmVyID0gQnVmZmVyLmZyb20ob3B0aW9uYWxEZXBzLmV0aFV0aWwuc3RyaXBIZXhQcmVmaXgoc2lnbmF0dXJlKSwgJ2hleCcpO1xuICAgIGNvbnN0IG1lc3NhZ2VCdWZmZXI6IEJ1ZmZlciA9IEJ1ZmZlci5mcm9tKFxuICAgICAgb3B0aW9uYWxEZXBzLmV0aFV0aWwucGFkVG9FdmVuKG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KGlkKSksXG4gICAgICAnaGV4J1xuICAgICk7XG5cbiAgICBjb25zdCBzaWcgPSBuZXcgVWludDhBcnJheShzaWduYXR1cmVCdWZmZXIuc2xpY2UoMSkpO1xuICAgIGNvbnN0IGlzVmFsaWRTaWduYXR1cmU6IGJvb2xlYW4gPSBzZWNwMjU2azEuZWNkc2FWZXJpZnkoc2lnLCBtZXNzYWdlQnVmZmVyLCBzZXJ2ZXJQdWJrZXlCdWZmZXIpO1xuICAgIGlmICghaXNWYWxpZFNpZ25hdHVyZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgSG9wIHR4aWQgc2lnbmF0dXJlIGludmFsaWQgLSBwdWI6ICR7c2VydmVyWHB1Yn0sIG1zZzogJHttZXNzYWdlQnVmZmVyPy50b1N0cmluZygpfSwgc2lnOiAke3NpZ25hdHVyZUJ1ZmZlcj8udG9TdHJpbmcoKX1gXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IGJ1aWx0SG9wVHggPSBvcHRpb25hbERlcHMuRXRoVHguVHJhbnNhY3Rpb25GYWN0b3J5LmZyb21TZXJpYWxpemVkRGF0YShvcHRpb25hbERlcHMuZXRoVXRpbC50b0J1ZmZlcih0eCkpO1xuICAgIC8vIElmIG9yaWdpbmFsIHBhcmFtcyBhcmUgZ2l2ZW4sIHdlIGNhbiBjaGVjayB0aGVtIGFnYWluc3QgdGhlIHRyYW5zYWN0aW9uIHByZWJ1aWxkIHBhcmFtc1xuICAgIGlmICghXy5pc05pbChvcmlnaW5hbFBhcmFtcykpIHtcbiAgICAgIGNvbnN0IHsgcmVjaXBpZW50cyB9ID0gb3JpZ2luYWxQYXJhbXM7XG5cbiAgICAgIC8vIFRoZW4gdmFsaWRhdGUgdGhhdCB0aGUgdHggcGFyYW1zIGFjdHVhbGx5IGVxdWFsIHRoZSByZXF1ZXN0ZWQgcGFyYW1zXG4gICAgICBjb25zdCBvcmlnaW5hbEFtb3VudCA9IG5ldyBCaWdOdW1iZXIocmVjaXBpZW50c1swXS5hbW91bnQpO1xuICAgICAgY29uc3Qgb3JpZ2luYWxEZXN0aW5hdGlvbjogc3RyaW5nID0gcmVjaXBpZW50c1swXS5hZGRyZXNzO1xuXG4gICAgICBjb25zdCBob3BBbW91bnQgPSBuZXcgQmlnTnVtYmVyKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSGV4KGJ1aWx0SG9wVHgudmFsdWUpKTtcbiAgICAgIGlmICghYnVpbHRIb3BUeC50bykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFRyYW5zYWN0aW9uIGRvZXMgbm90IGhhdmUgYSBkZXN0aW5hdGlvbiBhZGRyZXNzYCk7XG4gICAgICB9XG4gICAgICBjb25zdCBob3BEZXN0aW5hdGlvbiA9IGJ1aWx0SG9wVHgudG8udG9TdHJpbmcoKTtcbiAgICAgIGlmICghaG9wQW1vdW50LmVxKG9yaWdpbmFsQW1vdW50KSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEhvcCBhbW91bnQ6ICR7aG9wQW1vdW50fSBkb2VzIG5vdCBlcXVhbCBvcmlnaW5hbCBhbW91bnQ6ICR7b3JpZ2luYWxBbW91bnR9YCk7XG4gICAgICB9XG4gICAgICBpZiAoaG9wRGVzdGluYXRpb24udG9Mb3dlckNhc2UoKSAhPT0gb3JpZ2luYWxEZXN0aW5hdGlvbi50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgSG9wIGRlc3RpbmF0aW9uOiAke2hvcERlc3RpbmF0aW9ufSBkb2VzIG5vdCBlcXVhbCBvcmlnaW5hbCByZWNpcGllbnQ6ICR7aG9wRGVzdGluYXRpb259YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFidWlsdEhvcFR4LnZlcmlmeVNpZ25hdHVyZSgpKSB7XG4gICAgICAvLyBXZSBkb250IHdhbnQgdG8gY29udGludWUgYXQgYWxsIGluIHRoaXMgY2FzZSwgYXQgcmlzayBvZiBFVEggYmVpbmcgc3R1Y2sgb24gdGhlIGhvcCBhZGRyZXNzXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgaG9wIHRyYW5zYWN0aW9uIHNpZ25hdHVyZSwgdHhpZDogJHtpZH1gKTtcbiAgICB9XG4gICAgaWYgKG9wdGlvbmFsRGVwcy5ldGhVdGlsLmFkZEhleFByZWZpeChidWlsdEhvcFR4Lmhhc2goKS50b1N0cmluZygnaGV4JykpICE9PSBpZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBTaWduZWQgaG9wIHR4aWQgZG9lcyBub3QgZXF1YWwgYWN0dWFsIHR4aWRgKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0cyB0aGUgaG9wIGRpZ2VzdCBmb3IgdGhlIHVzZXIgdG8gc2lnbi4gVGhpcyBpcyB2YWxpZGF0ZWQgaW4gdGhlIEhTTSB0byBwcm92ZSB0aGF0IHRoZSB1c2VyIHJlcXVlc3RlZCB0aGlzIHR4XG4gICAqIEBwYXJhbSB7c3RyaW5nW119IHBhcmFtc0FyciAtIFRoZSBwYXJhbWV0ZXJzIHRvIGhhc2ggdG9nZXRoZXIgZm9yIHRoZSBkaWdlc3RcbiAgICogQHJldHVybnMge0J1ZmZlcn1cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgZ2V0SG9wRGlnZXN0KHBhcmFtc0Fycjogc3RyaW5nW10pOiBCdWZmZXIge1xuICAgIGNvbnN0IGhhc2ggPSBLZWNjYWsoJ2tlY2NhazI1NicpO1xuICAgIGhhc2gudXBkYXRlKFtBYnN0cmFjdEV0aExpa2VOZXdDb2lucy5ob3BUcmFuc2FjdGlvblNhbHQsIC4uLnBhcmFtc0Fycl0uam9pbignJCcpKTtcbiAgICByZXR1cm4gaGFzaC5kaWdlc3QoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNb2RpZnkgcHJlYnVpbGQgYmVmb3JlIHNlbmRpbmcgaXQgdG8gdGhlIHNlcnZlci4gQWRkIHRoaW5ncyBsaWtlIGhvcCB0cmFuc2FjdGlvbiBwYXJhbXNcbiAgICogQHBhcmFtIHtCdWlsZE9wdGlvbnN9IGJ1aWxkUGFyYW1zIC0gVGhlIHdoaXRlbGlzdGVkIHBhcmFtZXRlcnMgZm9yIHRoaXMgcHJlYnVpbGRcbiAgICogQHBhcmFtIHtib29sZWFufSBidWlsZFBhcmFtcy5ob3AgLSBUcnVlIGlmIHRoaXMgc2hvdWxkIHByZWJ1aWxkIGEgaG9wIHR4LCBlbHNlIGZhbHNlXG4gICAqIEBwYXJhbSB7UmVjaXBpZW50W119IGJ1aWxkUGFyYW1zLnJlY2lwaWVudHMgLSBUaGUgcmVjaXBpZW50cyBhcnJheSBvZiB0aGlzIHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSB7V2FsbGV0fSBidWlsZFBhcmFtcy53YWxsZXQgLSBUaGUgd2FsbGV0IHNlbmRpbmcgdGhpcyB0eFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYnVpbGRQYXJhbXMud2FsbGV0UGFzc3BocmFzZSAtIHRoZSBwYXNzcGhyYXNlIGZvciB0aGlzIHdhbGxldFxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxCdWlsZE9wdGlvbnM+fVxuICAgKi9cbiAgYXN5bmMgZ2V0RXh0cmFQcmVidWlsZFBhcmFtcyhidWlsZFBhcmFtczogQnVpbGRPcHRpb25zKTogUHJvbWlzZTxCdWlsZE9wdGlvbnM+IHtcbiAgICBpZiAoXG4gICAgICAhXy5pc1VuZGVmaW5lZChidWlsZFBhcmFtcy5ob3ApICYmXG4gICAgICBidWlsZFBhcmFtcy5ob3AgJiZcbiAgICAgICFfLmlzVW5kZWZpbmVkKGJ1aWxkUGFyYW1zLndhbGxldCkgJiZcbiAgICAgICFfLmlzVW5kZWZpbmVkKGJ1aWxkUGFyYW1zLnJlY2lwaWVudHMpICYmXG4gICAgICAhXy5pc1VuZGVmaW5lZChidWlsZFBhcmFtcy53YWxsZXRQYXNzcGhyYXNlKVxuICAgICkge1xuICAgICAgaWYgKHRoaXMgaW5zdGFuY2VvZiBFdGhMaWtlVG9rZW4pIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBIb3AgdHJhbnNhY3Rpb25zIGFyZSBub3QgZW5hYmxlZCBmb3IgRVJDLTIwIHRva2Vucywgbm9yIGFyZSB0aGV5IG5lY2Vzc2FyeS4gUGxlYXNlIHJlbW92ZSB0aGUgJ2hvcCcgcGFyYW1ldGVyIGFuZCB0cnkgYWdhaW4uYFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIChhd2FpdCB0aGlzLmNyZWF0ZUhvcFRyYW5zYWN0aW9uUGFyYW1zKHtcbiAgICAgICAgd2FsbGV0OiBidWlsZFBhcmFtcy53YWxsZXQsXG4gICAgICAgIHJlY2lwaWVudHM6IGJ1aWxkUGFyYW1zLnJlY2lwaWVudHMsXG4gICAgICAgIHdhbGxldFBhc3NwaHJhc2U6IGJ1aWxkUGFyYW1zLndhbGxldFBhc3NwaHJhc2UsXG4gICAgICB9KSkgYXMgYW55O1xuICAgIH1cbiAgICByZXR1cm4ge307XG4gIH1cblxuICAvKipcbiAgICogTW9kaWZ5IHByZWJ1aWxkIGFmdGVyIHJlY2VpdmluZyBpdCBmcm9tIHRoZSBzZXJ2ZXIuIEFkZCB0aGluZ3MgbGlrZSBubG9ja3RpbWVcbiAgICogQHBhcmFtIHtUcmFuc2FjdGlvblByZWJ1aWxkfSBwYXJhbXMgLSBUaGUgcHJlYnVpbGQgdG8gbW9kaWZ5XG4gICAqIEByZXR1cm5zIHtUcmFuc2FjdGlvblByZWJ1aWxkfSBUaGUgbW9kaWZpZWQgcHJlYnVpbGRcbiAgICovXG4gIGFzeW5jIHBvc3RQcm9jZXNzUHJlYnVpbGQocGFyYW1zOiBUcmFuc2FjdGlvblByZWJ1aWxkKTogUHJvbWlzZTxUcmFuc2FjdGlvblByZWJ1aWxkPiB7XG4gICAgaWYgKCFfLmlzVW5kZWZpbmVkKHBhcmFtcy5ob3BUcmFuc2FjdGlvbikgJiYgIV8uaXNVbmRlZmluZWQocGFyYW1zLndhbGxldCkgJiYgIV8uaXNVbmRlZmluZWQocGFyYW1zLmJ1aWxkUGFyYW1zKSkge1xuICAgICAgYXdhaXQgdGhpcy52YWxpZGF0ZUhvcFByZWJ1aWxkKHBhcmFtcy53YWxsZXQsIHBhcmFtcy5ob3BUcmFuc2FjdGlvbiwgcGFyYW1zLmJ1aWxkUGFyYW1zKTtcbiAgICB9XG4gICAgcmV0dXJuIHBhcmFtcztcbiAgfVxuXG4gIC8qKlxuICAgKiBDb2luLXNwZWNpZmljIHRoaW5ncyBkb25lIGJlZm9yZSBzaWduaW5nIGEgdHJhbnNhY3Rpb24sIGkuZS4gdmVyaWZpY2F0aW9uXG4gICAqIEBwYXJhbSB7UHJlc2lnblRyYW5zYWN0aW9uT3B0aW9uc30gcGFyYW1zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFByZXNpZ25UcmFuc2FjdGlvbk9wdGlvbnM+fVxuICAgKi9cbiAgYXN5bmMgcHJlc2lnblRyYW5zYWN0aW9uKHBhcmFtczogUHJlc2lnblRyYW5zYWN0aW9uT3B0aW9ucyk6IFByb21pc2U8UHJlc2lnblRyYW5zYWN0aW9uT3B0aW9ucz4ge1xuICAgIGlmICghXy5pc1VuZGVmaW5lZChwYXJhbXMuaG9wVHJhbnNhY3Rpb24pICYmICFfLmlzVW5kZWZpbmVkKHBhcmFtcy53YWxsZXQpICYmICFfLmlzVW5kZWZpbmVkKHBhcmFtcy5idWlsZFBhcmFtcykpIHtcbiAgICAgIGF3YWl0IHRoaXMudmFsaWRhdGVIb3BQcmVidWlsZChwYXJhbXMud2FsbGV0LCBwYXJhbXMuaG9wVHJhbnNhY3Rpb24pO1xuICAgIH1cbiAgICByZXR1cm4gcGFyYW1zO1xuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoIGZlZSBlc3RpbWF0ZSBpbmZvcm1hdGlvbiBmcm9tIHRoZSBzZXJ2ZXJcbiAgICogQHBhcmFtIHtPYmplY3R9IHBhcmFtcyAtIFRoZSBwYXJhbXMgcGFzc2VkIGludG8gdGhlIGZ1bmN0aW9uXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW3BhcmFtcy5ob3BdIC0gVHJ1ZSBpZiB3ZSBzaG91bGQgZXN0aW1hdGUgZmVlIGZvciBhIGhvcCB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0ge3N0cmluZ30gW3BhcmFtcy5yZWNpcGllbnRdIC0gVGhlIHJlY2lwaWVudCBvZiB0aGUgdHJhbnNhY3Rpb24gdG8gZXN0aW1hdGUgYSBzZW5kIHRvXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbcGFyYW1zLmRhdGFdIC0gVGhlIEVUSCB0eCBkYXRhIHRvIGVzdGltYXRlIGEgc2VuZCBmb3JcbiAgICogQHJldHVybnMge09iamVjdH0gVGhlIGZlZSBpbmZvIHJldHVybmVkIGZyb20gdGhlIHNlcnZlclxuICAgKi9cbiAgYXN5bmMgZmVlRXN0aW1hdGUocGFyYW1zOiBGZWVFc3RpbWF0ZU9wdGlvbnMpOiBQcm9taXNlPEZlZUVzdGltYXRlPiB7XG4gICAgY29uc3QgcXVlcnk6IEZlZUVzdGltYXRlT3B0aW9ucyA9IHt9O1xuICAgIGlmIChwYXJhbXMgJiYgcGFyYW1zLmhvcCkge1xuICAgICAgcXVlcnkuaG9wID0gcGFyYW1zLmhvcDtcbiAgICB9XG4gICAgaWYgKHBhcmFtcyAmJiBwYXJhbXMucmVjaXBpZW50KSB7XG4gICAgICBxdWVyeS5yZWNpcGllbnQgPSBwYXJhbXMucmVjaXBpZW50O1xuICAgIH1cbiAgICBpZiAocGFyYW1zICYmIHBhcmFtcy5kYXRhKSB7XG4gICAgICBxdWVyeS5kYXRhID0gcGFyYW1zLmRhdGE7XG4gICAgfVxuICAgIGlmIChwYXJhbXMgJiYgcGFyYW1zLmFtb3VudCkge1xuICAgICAgcXVlcnkuYW1vdW50ID0gcGFyYW1zLmFtb3VudDtcbiAgICB9XG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5iaXRnby5nZXQodGhpcy51cmwoJy90eC9mZWUnKSkucXVlcnkocXVlcnkpLnJlc3VsdCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlIHNlY3AyNTZrMSBrZXkgcGFpclxuICAgKlxuICAgKiBAcGFyYW0ge0J1ZmZlcn0gc2VlZFxuICAgKiBAcmV0dXJucyB7S2V5UGFpcn0gb2JqZWN0IHdpdGggZ2VuZXJhdGVkIHB1YiBhbmQgcHJ2XG4gICAqL1xuICBnZW5lcmF0ZUtleVBhaXIoc2VlZDogQnVmZmVyKTogS2V5UGFpciB7XG4gICAgaWYgKCFzZWVkKSB7XG4gICAgICAvLyBBbiBleHRlbmRlZCBwcml2YXRlIGtleSBoYXMgYm90aCBhIG5vcm1hbCAyNTYgYml0IHByaXZhdGUga2V5IGFuZCBhIDI1NlxuICAgICAgLy8gYml0IGNoYWluIGNvZGUsIGJvdGggb2Ygd2hpY2ggbXVzdCBiZSByYW5kb20uIDUxMiBiaXRzIGlzIHRoZXJlZm9yZSB0aGVcbiAgICAgIC8vIG1heGltdW0gZW50cm9weSBhbmQgZ2l2ZXMgdXMgbWF4aW11bSBzZWN1cml0eSBhZ2FpbnN0IGNyYWNraW5nLlxuICAgICAgc2VlZCA9IHJhbmRvbUJ5dGVzKDUxMiAvIDgpO1xuICAgIH1cbiAgICBjb25zdCBleHRlbmRlZEtleSA9IGJpcDMyLmZyb21TZWVkKHNlZWQpO1xuICAgIGNvbnN0IHhwdWIgPSBleHRlbmRlZEtleS5uZXV0ZXJlZCgpLnRvQmFzZTU4KCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHB1YjogeHB1YixcbiAgICAgIHBydjogZXh0ZW5kZWRLZXkudG9CYXNlNTgoKSxcbiAgICB9O1xuICB9XG5cbiAgYXN5bmMgcGFyc2VUcmFuc2FjdGlvbihwYXJhbXM6IFBhcnNlVHJhbnNhY3Rpb25PcHRpb25zKTogUHJvbWlzZTxQYXJzZWRUcmFuc2FjdGlvbj4ge1xuICAgIHJldHVybiB7fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgZm9yd2FyZGVyIGZhY3RvcnkgYW5kIGltcGxlbWVudGF0aW9uIGFkZHJlc3NlcyBmb3IgZGVwb3NpdCBhZGRyZXNzIHZlcmlmaWNhdGlvbi5cbiAgICogRm9yd2FyZGVycyBhcmUgc21hcnQgY29udHJhY3RzIHRoYXQgZm9yd2FyZCBmdW5kcyB0byB0aGUgYmFzZSB3YWxsZXQgYWRkcmVzcy5cbiAgICpcbiAgICogQHBhcmFtIHtudW1iZXIgfCB1bmRlZmluZWR9IGZvcndhcmRlclZlcnNpb24gLSBUaGUgd2FsbGV0IHZlcnNpb25cbiAgICogQHJldHVybnMge29iamVjdH0gRmFjdG9yeSBhbmQgaW1wbGVtZW50YXRpb24gYWRkcmVzc2VzIGZvciBmb3J3YXJkZXJzXG4gICAqL1xuICBnZXRGb3J3YXJkZXJGYWN0b3J5QWRkcmVzc2VzQW5kRm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzKGZvcndhcmRlclZlcnNpb246IG51bWJlciB8IHVuZGVmaW5lZCk6IHtcbiAgICBmb3J3YXJkZXJGYWN0b3J5QWRkcmVzczogc3RyaW5nO1xuICAgIGZvcndhcmRlckltcGxlbWVudGF0aW9uQWRkcmVzczogc3RyaW5nO1xuICB9IHtcbiAgICBjb25zdCBldGhOZXR3b3JrID0gdGhpcy5nZXROZXR3b3JrKCk7XG5cbiAgICBzd2l0Y2ggKGZvcndhcmRlclZlcnNpb24pIHtcbiAgICAgIGNhc2UgMTpcbiAgICAgICAgaWYgKCFldGhOZXR3b3JrPy5mb3J3YXJkZXJGYWN0b3J5QWRkcmVzcyB8fCAhZXRoTmV0d29yaz8uZm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdGb3J3YXJkZXIgZmFjdG9yeSBhZGRyZXNzZXMgbm90IGNvbmZpZ3VyZWQgZm9yIHRoaXMgbmV0d29yaycpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgZm9yd2FyZGVyRmFjdG9yeUFkZHJlc3M6IGV0aE5ldHdvcmsuZm9yd2FyZGVyRmFjdG9yeUFkZHJlc3MsXG4gICAgICAgICAgZm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzOiBldGhOZXR3b3JrLmZvcndhcmRlckltcGxlbWVudGF0aW9uQWRkcmVzcyxcbiAgICAgICAgfTtcbiAgICAgIGNhc2UgMjpcbiAgICAgICAgaWYgKCFldGhOZXR3b3JrPy53YWxsZXRWMkZvcndhcmRlckZhY3RvcnlBZGRyZXNzIHx8ICFldGhOZXR3b3JrPy53YWxsZXRWMkZvcndhcmRlckltcGxlbWVudGF0aW9uQWRkcmVzcykge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcignV2FsbGV0IHYyIGZhY3RvcnkgYWRkcmVzc2VzIG5vdCBjb25maWd1cmVkIGZvciB0aGlzIG5ldHdvcmsnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGZvcndhcmRlckZhY3RvcnlBZGRyZXNzOiBldGhOZXR3b3JrLndhbGxldFYyRm9yd2FyZGVyRmFjdG9yeUFkZHJlc3MsXG4gICAgICAgICAgZm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzOiBldGhOZXR3b3JrLndhbGxldFYyRm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzLFxuICAgICAgICB9O1xuICAgICAgY2FzZSA0OlxuICAgICAgY2FzZSA1OlxuICAgICAgICBpZiAoIWV0aE5ldHdvcms/LndhbGxldFY0Rm9yd2FyZGVyRmFjdG9yeUFkZHJlc3MgfHwgIWV0aE5ldHdvcms/LndhbGxldFY0Rm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGb3J3YXJkZXIgdiR7Zm9yd2FyZGVyVmVyc2lvbn0gZmFjdG9yeSBhZGRyZXNzZXMgbm90IGNvbmZpZ3VyZWQgZm9yIHRoaXMgbmV0d29ya2ApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgZm9yd2FyZGVyRmFjdG9yeUFkZHJlc3M6IGV0aE5ldHdvcmsud2FsbGV0VjRGb3J3YXJkZXJGYWN0b3J5QWRkcmVzcyxcbiAgICAgICAgICBmb3J3YXJkZXJJbXBsZW1lbnRhdGlvbkFkZHJlc3M6IGV0aE5ldHdvcmsud2FsbGV0VjRGb3J3YXJkZXJJbXBsZW1lbnRhdGlvbkFkZHJlc3MsXG4gICAgICAgIH07XG4gICAgICBkZWZhdWx0OlxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZvcndhcmRlciB2ZXJzaW9uICR7Zm9yd2FyZGVyVmVyc2lvbn0gbm90IHN1cHBvcnRlZGApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgd2FsbGV0IGJhc2UgYWRkcmVzcyBmYWN0b3J5IGFuZCBpbXBsZW1lbnRhdGlvbiBhZGRyZXNzZXMuXG4gICAqIFRoaXMgaXMgdXNlZCBmb3IgYmFzZSBhZGRyZXNzIHZlcmlmaWNhdGlvbiBmb3IgVjEsIFYyLCBWNCwgYW5kIFY1IHdhbGxldHMuXG4gICAqIFRoZSBiYXNlIGFkZHJlc3MgaXMgdGhlIG1haW4gd2FsbGV0IGNvbnRyYWN0IGRlcGxveWVkIHZpYSBDUkVBVEUyLlxuICAgKlxuICAgKiBAcGFyYW0ge251bWJlcn0gd2FsbGV0VmVyc2lvbiAtIFRoZSB3YWxsZXQgdmVyc2lvbiAoMSwgMiwgNCwgb3IgNSlcbiAgICogQHJldHVybnMge29iamVjdH0gRmFjdG9yeSBhbmQgaW1wbGVtZW50YXRpb24gYWRkcmVzc2VzIGZvciB0aGUgd2FsbGV0IGJhc2UgYWRkcmVzc1xuICAgKiBAdGhyb3dzIHtFcnJvcn0gaWYgd2FsbGV0IHZlcnNpb24gYWRkcmVzc2VzIGFyZSBub3QgY29uZmlndXJlZFxuICAgKi9cbiAgZ2V0V2FsbGV0QWRkcmVzc0ZhY3RvcnlBZGRyZXNzZXNBbmRJbXBsZW1lbnRhdGlvbkFkZHJlc3Mod2FsbGV0VmVyc2lvbjogbnVtYmVyKToge1xuICAgIHdhbGxldEZhY3RvcnlBZGRyZXNzOiBzdHJpbmc7XG4gICAgd2FsbGV0SW1wbGVtZW50YXRpb25BZGRyZXNzOiBzdHJpbmc7XG4gIH0ge1xuICAgIGNvbnN0IGV0aE5ldHdvcmsgPSB0aGlzLmdldE5ldHdvcmsoKTtcblxuICAgIHN3aXRjaCAod2FsbGV0VmVyc2lvbikge1xuICAgICAgY2FzZSAxOlxuICAgICAgICBpZiAoIWV0aE5ldHdvcms/LndhbGxldEZhY3RvcnlBZGRyZXNzIHx8ICFldGhOZXR3b3JrPy53YWxsZXRJbXBsZW1lbnRhdGlvbkFkZHJlc3MpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1dhbGxldCB2MSBmYWN0b3J5IGFkZHJlc3NlcyBub3QgY29uZmlndXJlZCBmb3IgdGhpcyBuZXR3b3JrJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB3YWxsZXRGYWN0b3J5QWRkcmVzczogZXRoTmV0d29yay53YWxsZXRGYWN0b3J5QWRkcmVzcyxcbiAgICAgICAgICB3YWxsZXRJbXBsZW1lbnRhdGlvbkFkZHJlc3M6IGV0aE5ldHdvcmsud2FsbGV0SW1wbGVtZW50YXRpb25BZGRyZXNzLFxuICAgICAgICB9O1xuICAgICAgY2FzZSAyOlxuICAgICAgICBpZiAoIWV0aE5ldHdvcms/LndhbGxldFYyRmFjdG9yeUFkZHJlc3MgfHwgIWV0aE5ldHdvcms/LndhbGxldFYySW1wbGVtZW50YXRpb25BZGRyZXNzKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdXYWxsZXQgdjIgZmFjdG9yeSBhZGRyZXNzZXMgbm90IGNvbmZpZ3VyZWQgZm9yIHRoaXMgbmV0d29yaycpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgd2FsbGV0RmFjdG9yeUFkZHJlc3M6IGV0aE5ldHdvcmsud2FsbGV0VjJGYWN0b3J5QWRkcmVzcyxcbiAgICAgICAgICB3YWxsZXRJbXBsZW1lbnRhdGlvbkFkZHJlc3M6IGV0aE5ldHdvcmsud2FsbGV0VjJJbXBsZW1lbnRhdGlvbkFkZHJlc3MsXG4gICAgICAgIH07XG4gICAgICBjYXNlIDQ6XG4gICAgICBjYXNlIDU6XG4gICAgICAgIGlmICghZXRoTmV0d29yaz8ud2FsbGV0VjRGb3J3YXJkZXJGYWN0b3J5QWRkcmVzcyB8fCAhZXRoTmV0d29yaz8ud2FsbGV0VjRGb3J3YXJkZXJJbXBsZW1lbnRhdGlvbkFkZHJlc3MpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFdhbGxldCB2JHt3YWxsZXRWZXJzaW9ufSBmYWN0b3J5IGFkZHJlc3NlcyBub3QgY29uZmlndXJlZCBmb3IgdGhpcyBuZXR3b3JrYCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB3YWxsZXRGYWN0b3J5QWRkcmVzczogZXRoTmV0d29yay53YWxsZXRWNEZvcndhcmRlckZhY3RvcnlBZGRyZXNzLFxuICAgICAgICAgIHdhbGxldEltcGxlbWVudGF0aW9uQWRkcmVzczogZXRoTmV0d29yay53YWxsZXRWNEZvcndhcmRlckltcGxlbWVudGF0aW9uQWRkcmVzcyxcbiAgICAgICAgfTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgV2FsbGV0IHZlcnNpb24gJHt3YWxsZXRWZXJzaW9ufSBub3Qgc3VwcG9ydGVkYCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEhlbHBlciBtZXRob2QgdG8gY3JlYXRlIGEgc2FsdCBidWZmZXIgZnJvbSBoZXggc3RyaW5nLlxuICAgKiBDb252ZXJ0cyBhIGhleCBzYWx0IHN0cmluZyB0byBhIDMyLWJ5dGUgYnVmZmVyLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gc2FsdCAtIFRoZSBoZXggc2FsdCBzdHJpbmdcbiAgICogQHJldHVybnMge0J1ZmZlcn0gMzItYnl0ZSBzYWx0IGJ1ZmZlclxuICAgKi9cbiAgcHJpdmF0ZSBjcmVhdGVTYWx0QnVmZmVyKHNhbHQ6IHN0cmluZyk6IEJ1ZmZlciB7XG4gICAgY29uc3QgZXRoVXRpbCA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsO1xuICAgIHJldHVybiBldGhVdGlsLnNldExlbmd0aExlZnQoQnVmZmVyLmZyb20oZXRoVXRpbC5wYWRUb0V2ZW4oZXRoVXRpbC5zdHJpcEhleFByZWZpeChzYWx0IHx8ICcnKSksICdoZXgnKSwgMzIpO1xuICB9XG5cbiAgLyoqXG4gICAqIFZlcmlmeSBCSVAzMiB3YWxsZXQgYmFzZSBhZGRyZXNzIChWMSwgVjIsIFY0KS5cbiAgICogVGhlc2Ugd2FsbGV0cyB1c2UgYSB3YWxsZXQgZmFjdG9yeSB0byBkZXBsb3kgYmFzZSBhZGRyZXNzZXMgd2l0aCBDUkVBVEUyLlxuICAgKiBUaGUgYWRkcmVzcyBpcyBkZXJpdmVkIGZyb20gdGhlIGtleWNoYWlucycgZXRoQWRkcmVzc2VzIGFuZCBhIHNhbHQuXG4gICAqXG4gICAqIEBwYXJhbSB7VmVyaWZ5QmlwMzJCYXNlQWRkcmVzc09wdGlvbnN9IHBhcmFtcyAtIFZlcmlmaWNhdGlvbiBwYXJhbWV0ZXJzXG4gICAqIEByZXR1cm5zIHtvYmplY3R9IEV4cGVjdGVkIGFuZCBhY3R1YWwgYWRkcmVzc2VzIGZvciBjb21wYXJpc29uXG4gICAqL1xuICBwcml2YXRlIHZlcmlmeUNyZWF0ZTJCYXNlQWRkcmVzcyhwYXJhbXM6IFZlcmlmeUNvbnRyYWN0QmFzZUFkZHJlc3NPcHRpb25zKTogYm9vbGVhbiB7XG4gICAgY29uc3QgeyBhZGRyZXNzLCBjb2luU3BlY2lmaWMsIGtleWNoYWlucywgd2FsbGV0VmVyc2lvbiB9ID0gcGFyYW1zO1xuXG4gICAgaWYgKCFjb2luU3BlY2lmaWMuc2FsdCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBtaXNzaW5nIHNhbHQgZm9yIHYke3dhbGxldFZlcnNpb259IGJhc2UgYWRkcmVzcyB2ZXJpZmljYXRpb25gKTtcbiAgICB9XG5cbiAgICAvLyBHZXQgd2FsbGV0IGZhY3RvcnkgYW5kIGltcGxlbWVudGF0aW9uIGFkZHJlc3NlcyBmb3IgdGhlIHdhbGxldCB2ZXJzaW9uXG4gICAgY29uc3QgeyB3YWxsZXRGYWN0b3J5QWRkcmVzcywgd2FsbGV0SW1wbGVtZW50YXRpb25BZGRyZXNzIH0gPVxuICAgICAgdGhpcy5nZXRXYWxsZXRBZGRyZXNzRmFjdG9yeUFkZHJlc3Nlc0FuZEltcGxlbWVudGF0aW9uQWRkcmVzcyh3YWxsZXRWZXJzaW9uKTtcbiAgICBjb25zdCBpbml0Y29kZSA9IGdldFByb3h5SW5pdGNvZGUod2FsbGV0SW1wbGVtZW50YXRpb25BZGRyZXNzKTtcblxuICAgIC8vIENvbnZlcnQgdGhlIHdhbGxldCBzYWx0IHRvIGEgYnVmZmVyLCBwYWQgdG8gMzIgYnl0ZXNcbiAgICBjb25zdCBzYWx0QnVmZmVyID0gdGhpcy5jcmVhdGVTYWx0QnVmZmVyKGNvaW5TcGVjaWZpYy5zYWx0KTtcblxuICAgIC8vIFJlY29uc3RydWN0IGNhbGN1bGF0aW9uU2FsdCB1c2luZyBrZXljaGFpbnMnIGV0aEFkZHJlc3NlcyBhbmQgd2FsbGV0IHNhbHRcbiAgICBjb25zdCBldGhBZGRyZXNzZXMgPSBrZXljaGFpbnMubWFwKChrYykgPT4ge1xuICAgICAgaWYgKCFrYy5ldGhBZGRyZXNzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihga2V5Y2hhaW4gbWlzc2luZyBldGhBZGRyZXNzIGZvciB2JHt3YWxsZXRWZXJzaW9ufSBiYXNlIGFkZHJlc3MgdmVyaWZpY2F0aW9uYCk7XG4gICAgICB9XG4gICAgICByZXR1cm4ga2MuZXRoQWRkcmVzcztcbiAgICB9KTtcblxuICAgIGNvbnN0IGNhbGN1bGF0aW9uU2FsdCA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsLmJ1ZmZlclRvSGV4KFxuICAgICAgb3B0aW9uYWxEZXBzLmV0aEFiaS5zb2xpZGl0eVNIQTMoWydhZGRyZXNzW10nLCAnYnl0ZXMzMiddLCBbZXRoQWRkcmVzc2VzLCBzYWx0QnVmZmVyXSlcbiAgICApO1xuXG4gICAgY29uc3QgZXhwZWN0ZWRBZGRyZXNzID0gY2FsY3VsYXRlRm9yd2FyZGVyVjFBZGRyZXNzKHdhbGxldEZhY3RvcnlBZGRyZXNzLCBjYWxjdWxhdGlvblNhbHQsIGluaXRjb2RlKTtcblxuICAgIGlmIChleHBlY3RlZEFkZHJlc3MgIT09IGFkZHJlc3MpIHtcbiAgICAgIHRocm93IG5ldyBVbmV4cGVjdGVkQWRkcmVzc0Vycm9yKGBhZGRyZXNzIHZhbGlkYXRpb24gZmFpbHVyZTogZXhwZWN0ZWQgJHtleHBlY3RlZEFkZHJlc3N9IGJ1dCBnb3QgJHthZGRyZXNzfWApO1xuICAgIH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIFZlcmlmeSBmb3J3YXJkZXIgcmVjZWl2ZSBhZGRyZXNzIChkZXBvc2l0IGFkZHJlc3MpLlxuICAgKiBGb3J3YXJkZXIgYWRkcmVzc2VzIGFyZSBkZXJpdmVkIHVzaW5nIENSRUFURTIgZnJvbSB0aGUgYmFzZSBhZGRyZXNzIGFuZCBzYWx0LlxuICAgKlxuICAgKiBAcGFyYW0ge1ZlcmlmeUV0aEFkZHJlc3NPcHRpb25zfSBwYXJhbXMgLSBWZXJpZmljYXRpb24gcGFyYW1ldGVyc1xuICAgKiBAcGFyYW0ge251bWJlcn0gZm9yd2FyZGVyVmVyc2lvbiAtIFRoZSBmb3J3YXJkZXIgdmVyc2lvblxuICAgKiBAcmV0dXJucyB7b2JqZWN0fSBFeHBlY3RlZCBhbmQgYWN0dWFsIGFkZHJlc3NlcyBmb3IgY29tcGFyaXNvblxuICAgKi9cbiAgcHJpdmF0ZSB2ZXJpZnlGb3J3YXJkZXJBZGRyZXNzKHBhcmFtczogVmVyaWZ5RXRoQWRkcmVzc09wdGlvbnMsIGZvcndhcmRlclZlcnNpb246IG51bWJlcik6IGJvb2xlYW4ge1xuICAgIGNvbnN0IHsgYWRkcmVzcywgY29pblNwZWNpZmljLCBiYXNlQWRkcmVzcyB9ID0gcGFyYW1zO1xuXG4gICAgY29uc3QgeyBmb3J3YXJkZXJGYWN0b3J5QWRkcmVzcywgZm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzIH0gPVxuICAgICAgdGhpcy5nZXRGb3J3YXJkZXJGYWN0b3J5QWRkcmVzc2VzQW5kRm9yd2FyZGVySW1wbGVtZW50YXRpb25BZGRyZXNzKGZvcndhcmRlclZlcnNpb24pO1xuICAgIGNvbnN0IGluaXRjb2RlID0gZ2V0UHJveHlJbml0Y29kZShmb3J3YXJkZXJJbXBsZW1lbnRhdGlvbkFkZHJlc3MpO1xuICAgIGNvbnN0IHNhbHRCdWZmZXIgPSB0aGlzLmNyZWF0ZVNhbHRCdWZmZXIoY29pblNwZWNpZmljLnNhbHQgfHwgJycpO1xuXG4gICAgY29uc3QgeyBjcmVhdGVGb3J3YXJkZXJQYXJhbXMsIGNyZWF0ZUZvcndhcmRlclR5cGVzIH0gPVxuICAgICAgZm9yd2FyZGVyVmVyc2lvbiA9PT0gNFxuICAgICAgICA/IGdldENyZWF0ZUZvcndhcmRlclBhcmFtc0FuZFR5cGVzKGJhc2VBZGRyZXNzLCBzYWx0QnVmZmVyLCBjb2luU3BlY2lmaWMuZmVlQWRkcmVzcylcbiAgICAgICAgOiBnZXRDcmVhdGVGb3J3YXJkZXJQYXJhbXNBbmRUeXBlcyhiYXNlQWRkcmVzcywgc2FsdEJ1ZmZlcik7XG5cbiAgICBjb25zdCBjYWxjdWxhdGlvblNhbHQgPSBvcHRpb25hbERlcHMuZXRoVXRpbC5idWZmZXJUb0hleChcbiAgICAgIG9wdGlvbmFsRGVwcy5ldGhBYmkuc29saWRpdHlTSEEzKGNyZWF0ZUZvcndhcmRlclR5cGVzLCBjcmVhdGVGb3J3YXJkZXJQYXJhbXMpXG4gICAgKTtcblxuICAgIGNvbnN0IGV4cGVjdGVkQWRkcmVzcyA9IGNhbGN1bGF0ZUZvcndhcmRlclYxQWRkcmVzcyhmb3J3YXJkZXJGYWN0b3J5QWRkcmVzcywgY2FsY3VsYXRpb25TYWx0LCBpbml0Y29kZSk7XG5cbiAgICBpZiAoZXhwZWN0ZWRBZGRyZXNzICE9PSBhZGRyZXNzKSB7XG4gICAgICB0aHJvdyBuZXcgVW5leHBlY3RlZEFkZHJlc3NFcnJvcihgYWRkcmVzcyB2YWxpZGF0aW9uIGZhaWx1cmU6IGV4cGVjdGVkICR7ZXhwZWN0ZWRBZGRyZXNzfSBidXQgZ290ICR7YWRkcmVzc31gKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNYWtlIHN1cmUgYW4gYWRkcmVzcyBpcyBhIHdhbGxldCBhZGRyZXNzIGFuZCB0aHJvdyBhbiBlcnJvciBpZiBpdCdzIG5vdC5cbiAgICogQHBhcmFtIHtPYmplY3R9IHBhcmFtc1xuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmFkZHJlc3MgLSBUaGUgZGVyaXZlZCBhZGRyZXNzIHN0cmluZyBvbiB0aGUgbmV0d29ya1xuICAgKiBAcGFyYW0ge09iamVjdH0gcGFyYW1zLmNvaW5TcGVjaWZpYyAtIENvaW4tc3BlY2lmaWMgZGV0YWlscyBmb3IgdGhlIGFkZHJlc3Mgc3VjaCBhcyBhIGZvcndhcmRlclZlcnNpb25cbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5iYXNlQWRkcmVzcyAtIFRoZSBiYXNlIGFkZHJlc3Mgb2YgdGhlIHdhbGxldCBvbiB0aGUgbmV0d29ya1xuICAgKiBAdGhyb3dzIHtJbnZhbGlkQWRkcmVzc0Vycm9yfVxuICAgKiBAdGhyb3dzIHtJbnZhbGlkQWRkcmVzc1ZlcmlmaWNhdGlvbk9iamVjdFByb3BlcnR5RXJyb3J9XG4gICAqIEB0aHJvd3Mge1VuZXhwZWN0ZWRBZGRyZXNzRXJyb3J9XG4gICAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmZiBhZGRyZXNzIGlzIGEgd2FsbGV0IGFkZHJlc3NcbiAgICovXG4gIGFzeW5jIGlzV2FsbGV0QWRkcmVzcyhwYXJhbXM6IFZlcmlmeUV0aEFkZHJlc3NPcHRpb25zIHwgVHNzVmVyaWZ5RXRoQWRkcmVzc09wdGlvbnMpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCB7IGFkZHJlc3MsIGltcGxpZWRGb3J3YXJkZXJWZXJzaW9uLCBjb2luU3BlY2lmaWMsIGJhc2VBZGRyZXNzIH0gPSBwYXJhbXM7XG4gICAgY29uc3QgZm9yd2FyZGVyVmVyc2lvbiA9IGltcGxpZWRGb3J3YXJkZXJWZXJzaW9uID8/IGNvaW5TcGVjaWZpYz8uZm9yd2FyZGVyVmVyc2lvbjtcblxuICAgIC8vIFZhbGlkYXRlIGFkZHJlc3MgZm9ybWF0XG4gICAgaWYgKGFkZHJlc3MgJiYgIXRoaXMuaXNWYWxpZEFkZHJlc3MoYWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBJbnZhbGlkQWRkcmVzc0Vycm9yKGBpbnZhbGlkIGFkZHJlc3M6ICR7YWRkcmVzc31gKTtcbiAgICB9XG5cbiAgICAvLyBGb3J3YXJkZXIgdmVyc2lvbiAwIGFkZHJlc3NlcyBjYW5ub3QgYmUgdmVyaWZpZWQgYmVjYXVzZSB3ZSBkbyBub3Qgc3RvcmUgdGhlIG5vbmNlIHZhbHVlIHJlcXVpcmVkIGZvciBhZGRyZXNzIGRlcml2YXRpb24uXG4gICAgaWYgKGZvcndhcmRlclZlcnNpb24gPT09IDApIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIC8vIERldGVybWluZSBpZiB3ZSBhcmUgdmVyaWZ5aW5nIGEgYmFzZSBhZGRyZXNzXG4gICAgY29uc3QgaXNWZXJpZnlpbmdCYXNlQWRkcmVzcyA9IGJhc2VBZGRyZXNzICYmIGFkZHJlc3MgPT09IGJhc2VBZGRyZXNzO1xuXG4gICAgLy8gVFNTL01QQyB3YWxsZXQgYWRkcmVzcyB2ZXJpZmljYXRpb24gKFYzLCBWNSwgVjYpXG4gICAgLy8gVjUgYmFzZSBhZGRyZXNzZXMgdXNlIFRTUywgYnV0IFY1IGZvcndhcmRlcnMgdXNlIHRoZSByZWd1bGFyIGZvcndhcmRlciB2ZXJpZmljYXRpb25cbiAgICBjb25zdCBpc1Rzc1dhbGxldFZlcnNpb24gPSBwYXJhbXMud2FsbGV0VmVyc2lvbiA9PT0gMyB8fCBwYXJhbXMud2FsbGV0VmVyc2lvbiA9PT0gNSB8fCBwYXJhbXMud2FsbGV0VmVyc2lvbiA9PT0gNjtcbiAgICBjb25zdCBzaG91bGRVc2VUc3NWZXJpZmljYXRpb24gPVxuICAgICAgaXNUc3NWZXJpZnlBZGRyZXNzT3B0aW9ucyhwYXJhbXMpICYmIGlzVHNzV2FsbGV0VmVyc2lvbiAmJiAocGFyYW1zLndhbGxldFZlcnNpb24gIT09IDUgfHwgaXNWZXJpZnlpbmdCYXNlQWRkcmVzcyk7XG5cbiAgICBpZiAoc2hvdWxkVXNlVHNzVmVyaWZpY2F0aW9uKSB7XG4gICAgICBpZiAoaXNWZXJpZnlpbmdCYXNlQWRkcmVzcykge1xuICAgICAgICBjb25zdCBpbmRleCA9IHR5cGVvZiBwYXJhbXMuaW5kZXggPT09ICdzdHJpbmcnID8gcGFyc2VJbnQocGFyYW1zLmluZGV4LCAxMCkgOiBwYXJhbXMuaW5kZXg7XG4gICAgICAgIGlmIChpbmRleCAhPT0gMCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBCYXNlIGFkZHJlc3MgdmVyaWZpY2F0aW9uIHJlcXVpcmVzIGluZGV4IDAsIGJ1dCBnb3QgaW5kZXggJHtwYXJhbXMuaW5kZXh9LiBgICtcbiAgICAgICAgICAgICAgYFRoZSBiYXNlIGFkZHJlc3MgaXMgYWx3YXlzIGRlcml2ZWQgYXQgaW5kZXggMC5gXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gdmVyaWZ5TVBDV2FsbGV0QWRkcmVzcyh7IC4uLnBhcmFtcywga2V5Q3VydmU6ICdzZWNwMjU2azEnIH0sIHRoaXMuaXNWYWxpZEFkZHJlc3MsIChwdWJLZXkpID0+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBLZXlQYWlyTGliKHsgcHViOiBwdWJLZXkgfSkuZ2V0QWRkcmVzcygpO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gRnJvbSBoZXJlIG9uLCB3ZSBuZWVkIGJhc2VBZGRyZXNzIGFuZCBjb2luU3BlY2lmaWMgZm9yIG5vbi1UU1MgdmVyaWZpY2F0aW9uc1xuICAgIGlmIChfLmlzVW5kZWZpbmVkKGJhc2VBZGRyZXNzKSB8fCAhdGhpcy5pc1ZhbGlkQWRkcmVzcyhiYXNlQWRkcmVzcykpIHtcbiAgICAgIHRocm93IG5ldyBJbnZhbGlkQWRkcmVzc0Vycm9yKCdpbnZhbGlkIGJhc2UgYWRkcmVzcycpO1xuICAgIH1cblxuICAgIGlmICghXy5pc09iamVjdChjb2luU3BlY2lmaWMpKSB7XG4gICAgICB0aHJvdyBuZXcgSW52YWxpZEFkZHJlc3NWZXJpZmljYXRpb25PYmplY3RQcm9wZXJ0eUVycm9yKFxuICAgICAgICAnYWRkcmVzcyB2YWxpZGF0aW9uIGZhaWx1cmU6IGNvaW5TcGVjaWZpYyBmaWVsZCBtdXN0IGJlIGFuIG9iamVjdCdcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gQklQMzIgd2FsbGV0IGJhc2UgYWRkcmVzcyB2ZXJpZmljYXRpb24gKFYxLCBWMiwgVjQpXG4gICAgaWYgKGlzVmVyaWZ5aW5nQmFzZUFkZHJlc3MgJiYgaXNWZXJpZnlDb250cmFjdEJhc2VBZGRyZXNzT3B0aW9ucyhwYXJhbXMpKSB7XG4gICAgICByZXR1cm4gdGhpcy52ZXJpZnlDcmVhdGUyQmFzZUFkZHJlc3MocGFyYW1zKTtcbiAgICB9XG5cbiAgICAvLyBGb3J3YXJkZXIgcmVjZWl2ZSBhZGRyZXNzIHZlcmlmaWNhdGlvbiAoZGVwb3NpdCBhZGRyZXNzZXMpXG4gICAgaWYgKCFpc1ZlcmlmeWluZ0Jhc2VBZGRyZXNzKSB7XG4gICAgICByZXR1cm4gdGhpcy52ZXJpZnlGb3J3YXJkZXJBZGRyZXNzKHBhcmFtcywgZm9yd2FyZGVyVmVyc2lvbik7XG4gICAgfVxuXG4gICAgLy8gSWYgd2UgcmVhY2ggaGVyZSwgaXQncyBhIGJhc2UgYWRkcmVzcyB2ZXJpZmljYXRpb24gZm9yIGFuIHVuc3VwcG9ydGVkIHdhbGxldCB2ZXJzaW9uXG4gICAgdGhyb3cgbmV3IEVycm9yKGBCYXNlIGFkZHJlc3MgdmVyaWZpY2F0aW9uIG5vdCBzdXBwb3J0ZWQgZm9yIHdhbGxldCB2ZXJzaW9uICR7cGFyYW1zLndhbGxldFZlcnNpb259YCk7XG4gIH1cblxuICAvKipcbiAgICogTG9jYWxseSBkZXJpdmUgYW4gRVRIIHdhbGxldCByZWNlaXZlIGFkZHJlc3MgZnJvbSBhIGRlcml2YXRpb24gcGF0aCwgdXNpbmcgdGhlIHdhbGxldCdzXG4gICAqIGNvbW1vbktleWNoYWluIG9ubHkgKG5vIHByaXZhdGUga2V5cywgbm8gbmV0d29yayBhY2Nlc3MpLiBUaGUgaW52ZXJzZSBvZlxuICAgKiB7QGxpbmsgaXNXYWxsZXRBZGRyZXNzfTogaXQgKnByb2R1Y2VzKiB0aGUgYWRkcmVzcyB2aWEgdGhlIHNhbWUgc2VjcDI1NmsxIE1QQyBkZXJpdmF0aW9uXG4gICAqIHRoYXQgaXNXYWxsZXRBZGRyZXNzIGNoZWNrcyBhZ2FpbnN0IChgZGVyaXZlTVBDV2FsbGV0QWRkcmVzc2AgKyBgS2V5UGFpci5nZXRBZGRyZXNzKClgKSxcbiAgICogc28gZGVyaXZlIGFuZCB2ZXJpZnkgY2FuIG5ldmVyIGRpdmVyZ2UuXG4gICAqXG4gICAqIFN1cHBvcnRzIE1QQy9UU1Mgd2FsbGV0cyBvbmx5ICh3YWxsZXQgdmVyc2lvbnMgMywgNSwgNikuIExlZ2FjeSBCSVAzMiBmb3J3YXJkZXIgd2FsbGV0c1xuICAgKiAodmVyc2lvbnMgMSwgMiwgNCkgZGVyaXZlIHBlci1pbmRleCBmb3J3YXJkZXIgY29udHJhY3QgYWRkcmVzc2VzIGFuZCBhcmUgaGFuZGxlZCBzZXBhcmF0ZWx5LlxuICAgKiBAcGFyYW0gcGFyYW1zIGtleWNoYWlucyAoY29tbW9uS2V5Y2hhaW4pLCBkZXJpdmF0aW9uIGluZGV4LCB3YWxsZXRWZXJzaW9uLCBhbmQgb3B0aW9uYWwgU01DIHNlZWRcbiAgICogQHJldHVybnMgdGhlIGRlcml2ZWQgYWRkcmVzcywgdGhlIGluZGV4IHVzZWQsIGFuZCB0aGUgSEQgZGVyaXZhdGlvbiBwYXRoXG4gICAqL1xuICBhc3luYyBkZXJpdmVBZGRyZXNzKHBhcmFtczogRGVyaXZlQWRkcmVzc09wdGlvbnMpOiBQcm9taXNlPERlcml2ZUFkZHJlc3NSZXN1bHQ+IHtcbiAgICBjb25zdCB7IHdhbGxldFZlcnNpb24sIGluZGV4IH0gPSBwYXJhbXM7XG4gICAgY29uc3QgZm9yd2FyZGVyVmVyc2lvbiA9IHBhcmFtcy5jb2luU3BlY2lmaWM/LmZvcndhcmRlclZlcnNpb247XG5cbiAgICAvLyBNaXJyb3JzIGlzV2FsbGV0QWRkcmVzcydzIGJyYW5jaGluZzpcbiAgICAvLyAtIE1QQy9UU1MgcmVjZWl2ZSBhZGRyZXNzZXM6IHYzICYgdjYgKGV2ZXJ5IGluZGV4KSwgYW5kIHRoZSB2NSAqYmFzZSogYWRkcmVzcyAoaW5kZXggMCkuXG4gICAgLy8gLSBGb3J3YXJkZXIgKENSRUFURTIpIHJlY2VpdmUgYWRkcmVzc2VzOiBsZWdhY3kgdjEvdjIvdjQgd2FsbGV0cywgYW5kIHY1IGZvcndhcmRlcnMgKGluZGV4ID4gMCkuXG4gICAgY29uc3QgdXNlTXBjID0gd2FsbGV0VmVyc2lvbiA9PT0gMyB8fCB3YWxsZXRWZXJzaW9uID09PSA2IHx8ICh3YWxsZXRWZXJzaW9uID09PSA1ICYmIGluZGV4ID09PSAwKTtcbiAgICBpZiAodXNlTXBjKSB7XG4gICAgICBjb25zdCB7IGFkZHJlc3MsIGRlcml2YXRpb25QYXRoIH0gPSBhd2FpdCBkZXJpdmVNUENXYWxsZXRBZGRyZXNzKFxuICAgICAgICB7XG4gICAgICAgICAgLy8gZXh0cmFjdENvbW1vbktleWNoYWluIHZhbGlkYXRlcyB0aGUgY29tbW9uS2V5Y2hhaW4gaXMgcHJlc2VudCBhdCBydW50aW1lXG4gICAgICAgICAga2V5Y2hhaW5zOiAocGFyYW1zLmtleWNoYWlucyA/PyBbXSkgYXMgVHNzVmVyaWZ5QWRkcmVzc09wdGlvbnNbJ2tleWNoYWlucyddLFxuICAgICAgICAgIGluZGV4LFxuICAgICAgICAgIGRlcml2ZWRGcm9tUGFyZW50V2l0aFNlZWQ6IHBhcmFtcy5kZXJpdmVkRnJvbVBhcmVudFdpdGhTZWVkLFxuICAgICAgICAgIG11bHRpc2lnVHlwZVZlcnNpb246IHBhcmFtcy5tdWx0aXNpZ1R5cGVWZXJzaW9uLFxuICAgICAgICAgIGtleUN1cnZlOiAnc2VjcDI1NmsxJyxcbiAgICAgICAgfSxcbiAgICAgICAgKHB1YktleSkgPT4gbmV3IEtleVBhaXJMaWIoeyBwdWI6IHB1YktleSB9KS5nZXRBZGRyZXNzKClcbiAgICAgICk7XG5cbiAgICAgIHJldHVybiB7IGFkZHJlc3MsIGluZGV4LCBkZXJpdmF0aW9uUGF0aCB9O1xuICAgIH1cblxuICAgIC8vIEZvcndhcmRlciAoQ1JFQVRFMikgZGVyaXZhdGlvbiBmb3IgbGVnYWN5IG11bHRpc2lnIC8gdjUgZm9yd2FyZGVyIHJlY2VpdmUgYWRkcmVzc2VzLlxuICAgIGlmIChmb3J3YXJkZXJWZXJzaW9uID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdGb3J3YXJkZXIgdmVyc2lvbiAwIGFkZHJlc3NlcyBjYW5ub3QgYmUgZGVyaXZlZDogdGhlIG5vbmNlIHJlcXVpcmVkIGZvciBhZGRyZXNzIGRlcml2YXRpb24gaXMgbm90IHN0b3JlZC4nXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAoZm9yd2FyZGVyVmVyc2lvbiA9PT0gMSB8fCBmb3J3YXJkZXJWZXJzaW9uID09PSAyIHx8IGZvcndhcmRlclZlcnNpb24gPT09IDQpIHtcbiAgICAgIGNvbnN0IHsgYmFzZUFkZHJlc3MsIGNvaW5TcGVjaWZpYyB9ID0gcGFyYW1zO1xuICAgICAgaWYgKCFiYXNlQWRkcmVzcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2Jhc2VBZGRyZXNzIGlzIHJlcXVpcmVkIHRvIGRlcml2ZSBhIGZvcndhcmRlciBhZGRyZXNzJyk7XG4gICAgICB9XG4gICAgICBjb25zdCBmZWVBZGRyZXNzID0gY29pblNwZWNpZmljPy5mZWVBZGRyZXNzID8/ICcnO1xuICAgICAgaWYgKGZvcndhcmRlclZlcnNpb24gPT09IDQgJiYgIWZlZUFkZHJlc3MpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdjb2luU3BlY2lmaWMuZmVlQWRkcmVzcyBpcyByZXF1aXJlZCB0byBkZXJpdmUgYSB2NCBmb3J3YXJkZXIgYWRkcmVzcycpO1xuICAgICAgfVxuXG4gICAgICBjb25zdCB7IGZvcndhcmRlckZhY3RvcnlBZGRyZXNzLCBmb3J3YXJkZXJJbXBsZW1lbnRhdGlvbkFkZHJlc3MgfSA9XG4gICAgICAgIHRoaXMuZ2V0Rm9yd2FyZGVyRmFjdG9yeUFkZHJlc3Nlc0FuZEZvcndhcmRlckltcGxlbWVudGF0aW9uQWRkcmVzcyhmb3J3YXJkZXJWZXJzaW9uKTtcbiAgICAgIC8vIEZvciBCaXRHbyBmb3J3YXJkZXJzIHRoZSBwZXItYWRkcmVzcyBzYWx0IGlzIHRoZSBkZXJpdmF0aW9uIGluZGV4LCBzbyB0aGUgZm9yd2FyZGVyXG4gICAgICAvLyBhZGRyZXNzIGlzIHJlcHJvZHVjaWJsZSBmcm9tIChiYXNlQWRkcmVzcywgaW5kZXgpIHBsdXMgdGhlIG5ldHdvcmsgZmFjdG9yeS9pbXBsZW1lbnRhdGlvbi5cbiAgICAgIGNvbnN0IGFkZHJlc3MgPSB0aGlzLmdlbmVyYXRlRm9yd2FyZGVyQWRkcmVzcyhcbiAgICAgICAgYmFzZUFkZHJlc3MsXG4gICAgICAgIGZlZUFkZHJlc3MsXG4gICAgICAgIGZvcndhcmRlckZhY3RvcnlBZGRyZXNzLFxuICAgICAgICBmb3J3YXJkZXJJbXBsZW1lbnRhdGlvbkFkZHJlc3MsXG4gICAgICAgIGluZGV4XG4gICAgICApO1xuXG4gICAgICByZXR1cm4geyBhZGRyZXNzLCBpbmRleCwgY29pblNwZWNpZmljOiB7IGZvcndhcmRlclZlcnNpb24gfSB9O1xuICAgIH1cblxuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBkZXJpdmVBZGRyZXNzOiBjb3VsZCBub3QgZGV0ZXJtaW5lIGFuIEVUSCBkZXJpdmF0aW9uIHN0cmF0ZWd5LiBQcm92aWRlIGFuIE1QQyB3YWxsZXQgYCArXG4gICAgICAgIGAod2FsbGV0VmVyc2lvbiAzLzUvNiB3aXRoIGNvbW1vbktleWNoYWluKSwgb3IgYSBsZWdhY3kgZm9yd2FyZGVyIChjb2luU3BlY2lmaWMuZm9yd2FyZGVyVmVyc2lvbiBgICtcbiAgICAgICAgYDEvMi80IHdpdGggYmFzZUFkZHJlc3MpLiBHb3Qgd2FsbGV0VmVyc2lvbj0ke3dhbGxldFZlcnNpb259LCBmb3J3YXJkZXJWZXJzaW9uPSR7Zm9yd2FyZGVyVmVyc2lvbn0uYFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICpcbiAgICogQHBhcmFtIHtUcmFuc2FjdGlvblByZWJ1aWxkfSB0eFByZWJ1aWxkXG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgdmVyaWZ5Q29pbih0eFByZWJ1aWxkOiBUcmFuc2FjdGlvblByZWJ1aWxkKTogYm9vbGVhbiB7XG4gICAgY29uc3QgbmF0aXZlQ29pbiA9IHRoaXMuZ2V0Q2hhaW4oKS5zcGxpdCgnOicpWzBdO1xuICAgIHJldHVybiB0eFByZWJ1aWxkLmNvaW4gPT09IG5hdGl2ZUNvaW47XG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGUgdHJhbnNhY3Rpb24gZXhwbGFuYXRpb24gZm9yIGVycm9yIHJlcG9ydGluZ1xuICAgKiBAcGFyYW0gdHhQcmVidWlsZCAtIFRyYW5zYWN0aW9uIHByZWJ1aWxkIGNvbnRhaW5pbmcgdHhIZXggYW5kIGZlZSBpbmZvXG4gICAqIEByZXR1cm5zIFN0cmluZ2lmaWVkIEpTT04gZXhwbGFuYXRpb25cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2V0VHhFeHBsYW5hdGlvbih0eFByZWJ1aWxkPzogVHJhbnNhY3Rpb25QcmVidWlsZCk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gICAgaWYgKCF0eFByZWJ1aWxkPy50eEhleCB8fCAhdHhQcmVidWlsZD8uZ2FzUHJpY2UpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGV4cGxhbmF0aW9uID0gYXdhaXQgdGhpcy5leHBsYWluVHJhbnNhY3Rpb24oe1xuICAgICAgICB0eEhleDogdHhQcmVidWlsZC50eEhleCxcbiAgICAgICAgZmVlSW5mbzoge1xuICAgICAgICAgIGZlZTogdHhQcmVidWlsZC5nYXNQcmljZS50b1N0cmluZygpLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkoZXhwbGFuYXRpb24sIG51bGwsIDIpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGNvbnN0IGVycm9yRGV0YWlscyA9IHtcbiAgICAgICAgZXJyb3I6ICdGYWlsZWQgdG8gcGFyc2UgdHJhbnNhY3Rpb24gZXhwbGFuYXRpb24nLFxuICAgICAgICB0eEhleDogdHhQcmVidWlsZC50eEhleCxcbiAgICAgICAgZGV0YWlsczogZSBpbnN0YW5jZW9mIEVycm9yID8gZS5tZXNzYWdlIDogU3RyaW5nKGUpLFxuICAgICAgfTtcbiAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShlcnJvckRldGFpbHMsIG51bGwsIDIpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZnkgaWYgYSB0c3MgdHJhbnNhY3Rpb24gaXMgdmFsaWRcbiAgICpcbiAgLyoqIEBpbmhlcml0ZG9jIENvaW5XaXRoU2lnbmFibGVDb25zaXN0ZW5jeSAqL1xuICBhc3NlcnRTaWduYWJsZUNvbnNpc3RlbmN5KHNlcmlhbGl6ZWRUeEhleDogc3RyaW5nLCBzaWduYWJsZUhleDogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgY29tbW9uID0gQWJzdHJhY3RFdGhMaWtlTmV3Q29pbnMuZ2V0Q3VzdG9tQ2hhaW5Db21tb24odGhpcy5nZXRDaGFpbklkKCkpO1xuICAgIC8vIGdldEN1c3RvbUNoYWluQ29tbW9uIGRlZmF1bHRzIHRvIHRoZSBsb25kb24gaGFyZGZvcmssIHdoaWNoIGlzIG9ubHkgdmFsaWQgZm9yIGNoYWluc1xuICAgIC8vIHRoYXQgYWN0aXZhdGVkIEVJUC0xNTU5LiBDaGFpbnMgd2l0aG91dCBpdCAoZS5nLiBCU0MsIFhEQykgc2lnbiB1bmRlciBwZXRlcnNidXJnLlxuICAgIGlmICghdGhpcy5zdGF0aWNzQ29pbj8uZmVhdHVyZXMuaW5jbHVkZXMoQ29pbkZlYXR1cmUuRUlQMTU1OSkpIHtcbiAgICAgIGNvbW1vbi5zZXRIYXJkZm9yayhvcHRpb25hbERlcHMuRXRoQ29tbW9uLkhhcmRmb3JrLlBldGVyc2J1cmcpO1xuICAgIH1cbiAgICBjb25zdCBldGhUeCA9IFRyYW5zYWN0aW9uRmFjdG9yeS5mcm9tU2VyaWFsaXplZERhdGEodG9CdWZmZXIoYWRkSGV4UHJlZml4KHNlcmlhbGl6ZWRUeEhleCkpLCB7IGNvbW1vbiB9KTtcbiAgICBjb25zdCBkZXJpdmVkU2lnbmFibGVIZXggPVxuICAgICAgZXRoVHggaW5zdGFuY2VvZiBGZWVNYXJrZXRFSVAxNTU5VHJhbnNhY3Rpb25cbiAgICAgICAgPyBldGhUeC5nZXRNZXNzYWdlVG9TaWduKGZhbHNlKS50b1N0cmluZygnaGV4JylcbiAgICAgICAgOiBCdWZmZXIuZnJvbShSTFAuZW5jb2RlKGJ1ZkFyclRvQXJyKChldGhUeCBhcyBMZWdhY3lUcmFuc2FjdGlvbikuZ2V0TWVzc2FnZVRvU2lnbihmYWxzZSkpKSkudG9TdHJpbmcoJ2hleCcpO1xuICAgIGlmIChkZXJpdmVkU2lnbmFibGVIZXggIT09IHNpZ25hYmxlSGV4KSB7XG4gICAgICB0aHJvdyBuZXcgSW52YWxpZFRyYW5zYWN0aW9uRXJyb3IoJ3NpZ25hYmxlSGV4IGlzIGluY29uc2lzdGVudCB3aXRoIHNlcmlhbGl6ZWRUeEhleDogcG9zc2libGUgc2VydmVyIHRhbXBlcmluZycpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEZWNvZGUtYW5kLWFzc2VydCBmb3IgdGhlIEVSQy03OTg0IHNoaWVsZCBhcHByb3ZlIGxlZyAoZGVmZW5zZS1pbi1kZXB0aCkuXG4gICAqXG4gICAqIFRoZSBhcHByb3ZlIGlzIHNpZ25lZCB0aHJvdWdoIHRoZSBjaGFpbiBjb2luLCB3aG9zZSBnZW5lcmljIGNoZWNrcyBuZXZlciBwYXJzZSBjYWxsZGF0YS5cbiAgICogV1AgYnVpbGRzIHRoZSBieXRlcyBzZXJ2ZXItc2lkZTsgcmUtcGFyc2UgdGhlbSBoZXJlIHNvIHRoZSBjbGllbnQgc2lnbnMgb25seVxuICAgKiBgYXBwcm92ZSh3cmFwcGVyLCBhbW91bnQpYCBvbiB0aGUgcGFpcidzIHVuZGVybHlpbmcgRVJDLTIwIHdpdGggdGhlIGFtb3VudCBmcm9tIHRoZSBpbnRlbnQuXG4gICAqIEZhaWxzIGNsb3NlZCBvbiBhbnkgZGV2aWF0aW9uLlxuICAgKlxuICAgKiBTY29wZTogdGhpcyBydW5zIHdoZW4gdGhlIGRlY2xhcmVkIHR5cGUgaXMgYHdyYXBBcHByb3ZlYCwgYW5kIHtAbGluayBhc3NlcnRXcmFwcGVyQXBwcm92ZUlzRGVjbGFyZWR9IGFsc29cbiAgICogZm9yY2VzIGFueSBhcHByb3ZlIHdob3NlIHNwZW5kZXIgaXMgYSByZWdpc3RlcmVkIHdyYXBwZXIgdGhyb3VnaCBpdC4gRGVsaWJlcmF0ZWx5IE5PVCBjb3ZlcmVkOiBhbiBhcHByb3ZlXG4gICAqIG9mIGEgcmVnaXN0ZXJlZCB1bmRlcmx5aW5nIHRvIGEgdGhpcmQtcGFydHkgc3BlbmRlciB1bmRlciBhbm90aGVyIGRlY2xhcmVkIHR5cGUgKHRva2VuQXBwcm92YWwsIGRlZmlBcHByb3ZlLFxuICAgKiBjdXN0b21UeCkgaXMgaW5kaXN0aW5ndWlzaGFibGUgZnJvbSBhIGxlZ2l0aW1hdGUgYXBwcm92YWwsIHNvIGEgc2VydmVyIHRoYXQgcmVsYWJlbHMgdGhlIGludGVudCBvZiBzdWNoIGFcbiAgICogdHggc3RpbGwgc2tpcHMgdGhpcyBjaGVjayBvbiBzaWduaW5nIHBhdGhzIHRoYXQgY2Fycnkgbm8gY2FsbGVyIHR4UGFyYW1zIChwZW5kaW5nQXBwcm92YWwgcmUtc2lnbikuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHZlcmlmeVdyYXBBcHByb3ZlVHJhbnNhY3Rpb24oXG4gICAgcGFyYW1zOiBWZXJpZnlFdGhUcmFuc2FjdGlvbk9wdGlvbnMsXG4gICAgdGhyb3dSZWNpcGllbnRNaXNtYXRjaDogKG1lc3NhZ2U6IHN0cmluZywgbWlzbWF0Y2hlZFJlY2lwaWVudHM6IFJlY2lwaWVudFtdKSA9PiBQcm9taXNlPG5ldmVyPlxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IHR4UGFyYW1zLCB0eFByZWJ1aWxkIH0gPSBwYXJhbXM7XG4gICAgaWYgKCF0eFByZWJ1aWxkPy50eEhleCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtaXNzaW5nIHR4SGV4IGluIHR4UHJlYnVpbGQnKTtcbiAgICB9XG4gICAgY29uc3Qgd3JhcFBhcmFtcyA9IHR4UGFyYW1zLndyYXBQYXJhbXM7XG4gICAgaWYgKCF3cmFwUGFyYW1zPy50b2tlbk5hbWUgfHwgIXdyYXBQYXJhbXMuYW1vdW50KSB7XG4gICAgICByZXR1cm4gdGhyb3dSZWNpcGllbnRNaXNtYXRjaCgnd3JhcEFwcHJvdmUgdHJhbnNhY3Rpb24gcmVxdWlyZXMgd3JhcFBhcmFtcyB3aXRoIHRva2VuTmFtZSBhbmQgYW1vdW50JywgW10pO1xuICAgIH1cblxuICAgIGxldCB3cmFwcGVyOiBFcmM3OTg0Q29pbjtcbiAgICB0cnkge1xuICAgICAgY29uc3Qgc3RhdGljc0NvaW4gPSBjb2lucy5nZXQod3JhcFBhcmFtcy50b2tlbk5hbWUpO1xuICAgICAgaWYgKCEoc3RhdGljc0NvaW4gaW5zdGFuY2VvZiBFcmM3OTg0Q29pbikgfHwgIXN0YXRpY3NDb2luLnVuZGVybHlpbmdFcmMyMEFkZHJlc3MpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdub3QgYW4gRVJDLTc5ODQgcGFpcicpO1xuICAgICAgfVxuICAgICAgd3JhcHBlciA9IHN0YXRpY3NDb2luO1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIHRocm93UmVjaXBpZW50TWlzbWF0Y2goXG4gICAgICAgIGB3cmFwQXBwcm92ZTogJyR7d3JhcFBhcmFtcy50b2tlbk5hbWV9JyBpcyBub3QgYSByZWdpc3RlcmVkIEVSQy03OTg0IHNoaWVsZCBwYWlyYCxcbiAgICAgICAgW11cbiAgICAgICk7XG4gICAgfVxuXG4gICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIoKTtcbiAgICB0eEJ1aWxkZXIuZnJvbSh0eFByZWJ1aWxkLnR4SGV4KTtcbiAgICBjb25zdCB0eEpzb24gPSAoYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCkpLnRvSnNvbigpO1xuICAgIGNvbnN0IG1pc21hdGNoID0gKG1lc3NhZ2U6IHN0cmluZywgYWRkcmVzcyA9IHR4SnNvbi50bywgYW1vdW50ID0gdHhKc29uLnZhbHVlKTogUHJvbWlzZTxuZXZlcj4gPT5cbiAgICAgIHRocm93UmVjaXBpZW50TWlzbWF0Y2gobWVzc2FnZSwgW3sgYWRkcmVzczogYWRkcmVzcyB8fCAnJywgYW1vdW50OiBhbW91bnQgfHwgJycgfV0pO1xuXG4gICAgaWYgKCF0eEpzb24udG8gfHwgdHhKc29uLnRvLnRvTG93ZXJDYXNlKCkgIT09IHdyYXBwZXIudW5kZXJseWluZ0VyYzIwQWRkcmVzcykge1xuICAgICAgYXdhaXQgbWlzbWF0Y2goJ3dyYXBBcHByb3ZlIHRyYW5zYWN0aW9uIG11c3QgdGFyZ2V0IHRoZSB1bmRlcmx5aW5nIEVSQy0yMCBvZiB0aGUgd3JhcHBlcicpO1xuICAgIH1cbiAgICBpZiAodHhKc29uLnZhbHVlICE9PSB1bmRlZmluZWQgJiYgU3RyaW5nKHR4SnNvbi52YWx1ZSkgIT09ICcwJykge1xuICAgICAgYXdhaXQgbWlzbWF0Y2goJ3dyYXBBcHByb3ZlIHRyYW5zYWN0aW9uIG11c3Qgbm90IGNhcnJ5IG5hdGl2ZSB2YWx1ZScpO1xuICAgIH1cblxuICAgIGxldCBkZWNvZGVkOiB7IHNwZW5kZXI6IHN0cmluZzsgYW1vdW50OiBzdHJpbmcgfTtcbiAgICB0cnkge1xuICAgICAgZGVjb2RlZCA9IGRlY29kZUFwcHJvdmVDYWxsZGF0YSh0eEpzb24uZGF0YSB8fCAnJyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgcmV0dXJuIG1pc21hdGNoKFxuICAgICAgICBgd3JhcEFwcHJvdmUgdHJhbnNhY3Rpb24gbXVzdCB1c2UgRVJDLTIwIGFwcHJvdmUoYWRkcmVzcyx1aW50MjU2KSBjYWxsZGF0YSDigJQgJHsoZSBhcyBFcnJvcikubWVzc2FnZX1gXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAoZGVjb2RlZC5zcGVuZGVyLnRvTG93ZXJDYXNlKCkgIT09IHdyYXBwZXIuY29udHJhY3RBZGRyZXNzLnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgIGF3YWl0IG1pc21hdGNoKCd3cmFwQXBwcm92ZSB0cmFuc2FjdGlvbiBzcGVuZGVyIGRvZXMgbm90IG1hdGNoIHRoZSB3cmFwcGVyIGNvbnRyYWN0JywgZGVjb2RlZC5zcGVuZGVyKTtcbiAgICB9XG4gICAgaWYgKGRlY29kZWQuYW1vdW50ICE9PSB3cmFwUGFyYW1zLmFtb3VudCkge1xuICAgICAgYXdhaXQgbWlzbWF0Y2goJ3dyYXBBcHByb3ZlIHRyYW5zYWN0aW9uIGFtb3VudCBkb2VzIG5vdCBtYXRjaCB0aGUgcmVxdWVzdGVkIGFtb3VudCcsIHR4SnNvbi50bywgZGVjb2RlZC5hbW91bnQpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDb250ZW50LWJhc2VkIGd1YXJkIGZvciB0aGUgRVJDLTc5ODQgc2hpZWxkIGFwcHJvdmUgbGVnLCBmb3IgdHhzIE5PVCBkZWNsYXJlZCBgd3JhcEFwcHJvdmVgLlxuICAgKlxuICAgKiBPbiBzaWduaW5nIHBhdGhzIHRoYXQgY2Fycnkgbm8gY2FsbGVyIHR4UGFyYW1zIChwZW5kaW5nQXBwcm92YWwgcmUtc2lnbikgdGhlIGRlY2xhcmVkIHR5cGUgaXMgdGhlXG4gICAqIHNlcnZlci1wZXJzaXN0ZWQgYGludGVudC5pbnRlbnRUeXBlYCwgc28gYSBjb21wcm9taXNlZCBvciBidWdneSBXUCBjb3VsZCByZWxhYmVsIGEgbXV0YXRlZFxuICAgKiBgYXBwcm92ZSh3cmFwcGVyLCBhbW91bnQpYCBhcyBhbm90aGVyIHR5cGUgYW5kIHNraXAgYHZlcmlmeVdyYXBBcHByb3ZlVHJhbnNhY3Rpb25gLiBTbyBhblxuICAgKiBgYXBwcm92ZShhZGRyZXNzLHVpbnQyNTYpYCBzZW50IHRvIGEgcmVnaXN0ZXJlZCBFUkMtNzk4NCB1bmRlcmx5aW5nIHdob3NlIHNwZW5kZXIgaXMgdGhhdCBwYWlyJ3Mgd3JhcHBlclxuICAgKiBNVVNUIGJlIGRlY2xhcmVkIGB3cmFwQXBwcm92ZWAgKGFuZCBzbyBwYXNzIHRoZSBzcGVuZGVyL2Ftb3VudCBjaGVjaykuXG4gICAqXG4gICAqIEEgdHggaXMgcmVqZWN0ZWQgb25seSB3aGVuIEFMTCBvZiB0aGVzZSBob2xkOlxuICAgKiAgIDEuIHRoZSB0eCB0YXJnZXRzIGEgcmVnaXN0ZXJlZCBFUkMtNzk4NCB1bmRlcmx5aW5nIChgdG9gIGlzIGluIGBnZXRFcmM3OTg0V3JhcHBlcnNCeVVuZGVybHlpbmcoKWApO1xuICAgKiAgIDIuIHRoZSBjYWxsZGF0YSBpcyBgYXBwcm92ZShhZGRyZXNzLHVpbnQyNTYpYCAoc2VsZWN0b3IgbWF0Y2gpO1xuICAgKiAgIDMuIHRoZSBzcGVuZGVyIChmaXJzdCBBQkkgd29yZCkgaXMgdGhhdCB1bmRlcmx5aW5nJ3Mgd3JhcHBlcjtcbiAgICogICA0LiB0aGUgZGVjbGFyZWQgdHlwZSBpcyBub3QgYHdyYXBBcHByb3ZlYCAoZW5mb3JjZWQgYnkgdGhlIGNhbGxlciwgd2hpY2ggb25seSBpbnZva2VzIHRoaXMgbWV0aG9kIHRoZW4pLlxuICAgKlxuICAgKiBTY29wZWQgdG8gd3JhcHBlci1kaXJlY3RlZCBhcHByb3ZhbHMgb25seTogYXBwcm92YWxzIG9mIHRoZSBzYW1lIHRva2VuIHRvIGFueSBvdGhlciBzcGVuZGVyLCB0cmFuc2ZlcnMsXG4gICAqIGFuZCBldmVyeSBvdGhlciBjYWxsIGFyZSB1bnRvdWNoZWQuIEtub3duIGV4Y2VwdGlvbnMgdG8gXCJvbmx5IHRoZSBzaGllbGQgam91cm5leSBhcHByb3ZlcyB0aGUgd3JhcHBlclwiOlxuICAgKiBhIGRBcHAgKFdhbGxldENvbm5lY3QpIGFwcHJvdmluZyB0aGUgd3JhcHBlciBkaXJlY3RseSB3b3VsZCBiZSByZWplY3RlZCBoZXJlLCBhbmQgdGhlIHVuYnVpbHQgUGhhc2UtMlxuICAgKiBgYXBwcm92ZSgwKWAgYWxsb3dhbmNlLXJlc2V0IGxlZyBtdXN0IGJlIGRlY2xhcmVkIGB3cmFwQXBwcm92ZWAgKG9yIGdldCBpdHMgb3duIGJyYW5jaCkuXG4gICAqXG4gICAqIE9uIG11bHRpc2lnIHdhbGxldHMgdGhlIGFwcHJvdmUgaXMgd3JhcHBlZCBpbiBzZW5kTXVsdGlTaWcodW5kZXJseWluZywgMCwgYXBwcm92ZSguLi4pLCAuLi4pLCBzbyB0aGUgaW5uZXJcbiAgICogdGFyZ2V0IGFuZCBjYWxsZGF0YSBhcmUgZXZhbHVhdGVkIGluc3RlYWQgb2YgdGhlIG91dGVyIG9uZXMuXG4gICAqXG4gICAqIFBhcnNpbmcgaXMgYmVzdC1lZmZvcnQ6IGlmIHRoZSB0eCBjYW5ub3QgYmUgcGFyc2VkIGJ5IHRoaXMgY29pbidzIGJ1aWxkZXIgdGhlIGNoZWNrIGlzIHNraXBwZWQsIHNvIGl0IGNhblxuICAgKiBuZXZlciBpbnRyb2R1Y2UgYSBuZXcgZmFpbHVyZSBmb3IgYSB0eCB0aGF0IHdhcyB2ZXJpZmlhYmxlIGJlZm9yZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgYXNzZXJ0V3JhcHBlckFwcHJvdmVJc0RlY2xhcmVkKFxuICAgIHBhcmFtczogVmVyaWZ5RXRoVHJhbnNhY3Rpb25PcHRpb25zLFxuICAgIHRocm93UmVjaXBpZW50TWlzbWF0Y2g6IChtZXNzYWdlOiBzdHJpbmcsIG1pc21hdGNoZWRSZWNpcGllbnRzOiBSZWNpcGllbnRbXSkgPT4gUHJvbWlzZTxuZXZlcj5cbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgeyB0eFByZWJ1aWxkIH0gPSBwYXJhbXM7XG4gICAgaWYgKCF0eFByZWJ1aWxkPy50eEhleCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB3cmFwcGVyc0J5VW5kZXJseWluZyA9IGdldEVyYzc5ODRXcmFwcGVyc0J5VW5kZXJseWluZygpO1xuICAgIGlmICh3cmFwcGVyc0J5VW5kZXJseWluZy5zaXplID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gQ2hlYXAgcHJlZmlsdGVyOiBjYWxsZGF0YSBpcyBzZXJpYWxpemVkIGFzIGxpdGVyYWwgYnl0ZXMsIHNvIGFuIGFwcHJvdmUgY2FuIG9ubHkgYmUgcHJlc2VudCBpZiB0aGVcbiAgICAvLyA0LWJ5dGUgc2VsZWN0b3IgYXBwZWFycyBpbiB0aGUgaGV4LiBOZWFybHkgZXZlcnkgb3RoZXIgdHggcmV0dXJucyBoZXJlIHdpdGhvdXQgdG91Y2hpbmcgdGhlIGJ1aWxkZXIuXG4gICAgaWYgKCF0eFByZWJ1aWxkLnR4SGV4LnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoYXBwcm92ZU1ldGhvZElkLnNsaWNlKDIpLnRvTG93ZXJDYXNlKCkpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbGV0IHR4SnNvbjogeyB0bz86IHN0cmluZzsgZGF0YT86IHN0cmluZzsgdmFsdWU/OiBzdHJpbmcgfTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIoKTtcbiAgICAgIHR4QnVpbGRlci5mcm9tKHR4UHJlYnVpbGQudHhIZXgpO1xuICAgICAgdHhKc29uID0gKGF3YWl0IHR4QnVpbGRlci5idWlsZCgpKS50b0pzb24oKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIE5vdCBwYXJzZWFibGUgYnkgdGhpcyBidWlsZGVyOiBsZWF2ZSBpdCB0byB0aGUgdHlwZS1zcGVjaWZpYyBjaGVja3MgYW5kIHRoZSBzaWduZXIuXG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbGV0IHRhcmdldCA9IHR4SnNvbi50bztcbiAgICBsZXQgZGF0YSA9ICh0eEpzb24uZGF0YSB8fCAnJykudG9Mb3dlckNhc2UoKTtcbiAgICBpZiAoZGF0YS5zdGFydHNXaXRoKHNlbmRNdWx0aXNpZ01ldGhvZElkLnRvTG93ZXJDYXNlKCkpKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBpbm5lciA9IGRlY29kZU5hdGl2ZVRyYW5zZmVyRGF0YSh0eEpzb24uZGF0YSB8fCAnJyk7XG4gICAgICAgIHRhcmdldCA9IGlubmVyLnRvO1xuICAgICAgICBkYXRhID0gaW5uZXIuZGF0YS50b0xvd2VyQ2FzZSgpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIE5vdCBhIGRlY29kYWJsZSBzZW5kTXVsdGlTaWcgY2FsbDogbGVhdmUgaXQgdG8gdGhlIHR5cGUtc3BlY2lmaWMgY2hlY2tzIGFuZCB0aGUgc2lnbmVyLlxuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IHdyYXBwZXJzID0gdGFyZ2V0ID8gd3JhcHBlcnNCeVVuZGVybHlpbmcuZ2V0KHRhcmdldC50b0xvd2VyQ2FzZSgpKSA6IHVuZGVmaW5lZDtcbiAgICAvLyBSZWFkIHRoZSBzcGVuZGVyIGxlbmllbnRseSAoZmlyc3QgQUJJIHdvcmQgYWZ0ZXIgdGhlIHNlbGVjdG9yKSByYXRoZXIgdGhhbiB2aWEgdGhlIHN0cmljdCBkZWNvZGVyOiBhblxuICAgIC8vIEVSQy0yMCBpZ25vcmVzIHRyYWlsaW5nIGJ5dGVzLCBzbyBhcHByb3ZlKHdyYXBwZXIsIGFtb3VudCkgKyBqdW5rIHdvdWxkIG90aGVyd2lzZSBzbGlwIHBhc3QgYSBzdHJpY3QgcGFyc2UuXG4gICAgaWYgKCF3cmFwcGVycyB8fCAhZGF0YS5zdGFydHNXaXRoKGFwcHJvdmVNZXRob2RJZC50b0xvd2VyQ2FzZSgpKSB8fCBkYXRhLmxlbmd0aCA8IDEwICsgNjQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qgc3BlbmRlciA9IGAweCR7ZGF0YS5zbGljZSgxMCArIDI0LCAxMCArIDY0KX1gO1xuICAgIGlmICh3cmFwcGVycy5oYXMoc3BlbmRlcikpIHtcbiAgICAgIGF3YWl0IHRocm93UmVjaXBpZW50TWlzbWF0Y2goJ2FwcHJvdmUoKSBvZiBhbiBFUkMtNzk4NCB3cmFwcGVyIG11c3QgYmUgZGVjbGFyZWQgYXMgYSB3cmFwQXBwcm92ZSBpbnRlbnQnLCBbXG4gICAgICAgIHsgYWRkcmVzczogc3BlbmRlciwgYW1vdW50OiB0eEpzb24udmFsdWUgfHwgJycgfSxcbiAgICAgIF0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge1ZlcmlmeUV0aFRyYW5zYWN0aW9uT3B0aW9uc30gcGFyYW1zXG4gICAqIEBwYXJhbSB7VHJhbnNhY3Rpb25QYXJhbXN9IHBhcmFtcy50eFBhcmFtcyAtIHBhcmFtcyBvYmplY3QgcGFzc2VkIHRvIHNlbmRcbiAgICogQHBhcmFtIHtUcmFuc2FjdGlvblByZWJ1aWxkfSBwYXJhbXMudHhQcmVidWlsZCAtIHByZWJ1aWxkIG9iamVjdCByZXR1cm5lZCBieSBzZXJ2ZXJcbiAgICogQHBhcmFtIHtXYWxsZXR9IHBhcmFtcy53YWxsZXQgLSBXYWxsZXQgb2JqZWN0IHRvIG9idGFpbiBrZXlzIHRvIHZlcmlmeSBhZ2FpbnN0XG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKiBAdGhyb3dzIHtUeEludGVudE1pc21hdGNoUmVjaXBpZW50RXJyb3J9IGlmIHRyYW5zYWN0aW9uIHJlY2lwaWVudHMgZG9uJ3QgbWF0Y2ggdXNlciBpbnRlbnRcbiAgICovXG4gIGFzeW5jIHZlcmlmeVRzc1RyYW5zYWN0aW9uKHBhcmFtczogVmVyaWZ5RXRoVHJhbnNhY3Rpb25PcHRpb25zKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3QgeyB0eFBhcmFtcywgdHhQcmVidWlsZCwgd2FsbGV0IH0gPSBwYXJhbXM7XG5cbiAgICAvLyBIZWxwZXIgdG8gdGhyb3cgVHhJbnRlbnRNaXNtYXRjaFJlY2lwaWVudEVycm9yIHdpdGggcmVjaXBpZW50IGRldGFpbHNcbiAgICBjb25zdCB0aHJvd1JlY2lwaWVudE1pc21hdGNoID0gYXN5bmMgKG1lc3NhZ2U6IHN0cmluZywgbWlzbWF0Y2hlZFJlY2lwaWVudHM6IFJlY2lwaWVudFtdKTogUHJvbWlzZTxuZXZlcj4gPT4ge1xuICAgICAgY29uc3QgdHhFeHBsYW5hdGlvbiA9IGF3YWl0IHRoaXMuZ2V0VHhFeHBsYW5hdGlvbih0eFByZWJ1aWxkKTtcbiAgICAgIHRocm93IG5ldyBUeEludGVudE1pc21hdGNoUmVjaXBpZW50RXJyb3IoXG4gICAgICAgIG1lc3NhZ2UsXG4gICAgICAgIHVuZGVmaW5lZCxcbiAgICAgICAgW3R4UGFyYW1zXSxcbiAgICAgICAgdHhQcmVidWlsZD8udHhIZXgsXG4gICAgICAgIG1pc21hdGNoZWRSZWNpcGllbnRzLFxuICAgICAgICB0eEV4cGxhbmF0aW9uXG4gICAgICApO1xuICAgIH07XG5cbiAgICBpZiAoXG4gICAgICAhdHhQYXJhbXM/LnJlY2lwaWVudHMgJiZcbiAgICAgICEoXG4gICAgICAgIHR4UGFyYW1zLnByZWJ1aWxkVHg/LmNvbnNvbGlkYXRlSWQgfHxcbiAgICAgICAgdHhQcmVidWlsZD8uY29uc29saWRhdGVJZCB8fFxuICAgICAgICB0eFBhcmFtcy5zdGFraW5nUmVxdWVzdElkIHx8XG4gICAgICAgIHR4UGFyYW1zLnByZWJ1aWxkVHg/LnN0YWtpbmdSZXF1ZXN0SWQgfHxcbiAgICAgICAgKHR4UGFyYW1zLnR5cGUgJiYgTk9fUkVDSVBJRU5UX1RYX1RZUEVTLmhhcyh0eFBhcmFtcy50eXBlKSlcbiAgICAgIClcbiAgICApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyB0eFBhcmFtcycpO1xuICAgIH1cbiAgICBpZiAoIXdhbGxldCB8fCAhdHhQcmVidWlsZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtaXNzaW5nIHBhcmFtcycpO1xuICAgIH1cblxuICAgIGlmICh0eFBhcmFtcy5ob3AgJiYgdHhQYXJhbXMucmVjaXBpZW50cyAmJiB0eFBhcmFtcy5yZWNpcGllbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigndHggY2Fubm90IGJlIGJvdGggYSBiYXRjaCBhbmQgaG9wIHRyYW5zYWN0aW9uJyk7XG4gICAgfVxuXG4gICAgaWYgKHR4UGFyYW1zLnR5cGUgJiYgWyd0cmFuc2ZlciddLmluY2x1ZGVzKHR4UGFyYW1zLnR5cGUpKSB7XG4gICAgICBpZiAodHhQYXJhbXMucmVjaXBpZW50cyAmJiB0eFBhcmFtcy5yZWNpcGllbnRzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICBjb25zdCByZWNpcGllbnRzID0gdHhQYXJhbXMucmVjaXBpZW50cztcbiAgICAgICAgY29uc3QgZXhwZWN0ZWRBbW91bnQgPSByZWNpcGllbnRzWzBdLmFtb3VudC50b1N0cmluZygpO1xuICAgICAgICBjb25zdCBleHBlY3RlZERlc3RpbmF0aW9uID0gcmVjaXBpZW50c1swXS5hZGRyZXNzO1xuXG4gICAgICAgIGNvbnN0IHR4QnVpbGRlciA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25CdWlsZGVyKCk7XG4gICAgICAgIHR4QnVpbGRlci5mcm9tKHR4UHJlYnVpbGQudHhIZXgpO1xuICAgICAgICBjb25zdCB0eCA9IGF3YWl0IHR4QnVpbGRlci5idWlsZCgpO1xuICAgICAgICBjb25zdCB0eEpzb24gPSB0eC50b0pzb24oKTtcbiAgICAgICAgaWYgKHR4SnNvbi5kYXRhID09PSAnMHgnKSB7XG4gICAgICAgICAgaWYgKGV4cGVjdGVkQW1vdW50ICE9PSB0eEpzb24udmFsdWUpIHtcbiAgICAgICAgICAgIGF3YWl0IHRocm93UmVjaXBpZW50TWlzbWF0Y2goXG4gICAgICAgICAgICAgICd0aGUgdHJhbnNhY3Rpb24gYW1vdW50IGluIHR4UHJlYnVpbGQgZG9lcyBub3QgbWF0Y2ggdGhlIHZhbHVlIGdpdmVuIGJ5IGNsaWVudCcsXG4gICAgICAgICAgICAgIFt7IGFkZHJlc3M6IHR4SnNvbi50bywgYW1vdW50OiB0eEpzb24udmFsdWUgfV1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChleHBlY3RlZERlc3RpbmF0aW9uLnRvTG93ZXJDYXNlKCkgIT09IHR4SnNvbi50by50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgICAgICBhd2FpdCB0aHJvd1JlY2lwaWVudE1pc21hdGNoKCdkZXN0aW5hdGlvbiBhZGRyZXNzIGRvZXMgbm90IG1hdGNoIHdpdGggdGhlIHJlY2lwaWVudCBhZGRyZXNzJywgW1xuICAgICAgICAgICAgICB7IGFkZHJlc3M6IHR4SnNvbi50bywgYW1vdW50OiB0eEpzb24udmFsdWUgfSxcbiAgICAgICAgICAgIF0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmICh0eEpzb24uZGF0YS5zdGFydHNXaXRoKCcweGE5MDU5Y2JiJykpIHtcbiAgICAgICAgICBjb25zdCBbcmVjaXBpZW50QWRkcmVzcywgYW1vdW50XSA9IGdldFJhd0RlY29kZWQoXG4gICAgICAgICAgICBbJ2FkZHJlc3MnLCAndWludDI1NiddLFxuICAgICAgICAgICAgZ2V0QnVmZmVyZWRCeXRlQ29kZSgnMHhhOTA1OWNiYicsIHR4SnNvbi5kYXRhKVxuICAgICAgICAgICk7XG5cbiAgICAgICAgICAvLyBDaGVjayBpZiByZWNpcGllbnRzWzBdLmRhdGEgZXhpc3RzIChXYWxsZXRDb25uZWN0IGZsb3cpXG4gICAgICAgICAgbGV0IGV4cGVjdGVkUmVjaXBpZW50QWRkcmVzczogc3RyaW5nO1xuICAgICAgICAgIGxldCBleHBlY3RlZFRva2VuQW1vdW50OiBzdHJpbmc7XG4gICAgICAgICAgY29uc3QgcmVjaXBpZW50RGF0YSA9IChyZWNpcGllbnRzWzBdIGFzIGFueSkuZGF0YTtcblxuICAgICAgICAgIGlmIChyZWNpcGllbnREYXRhICYmIHJlY2lwaWVudERhdGEuc3RhcnRzV2l0aCgnMHhhOTA1OWNiYicpKSB7XG4gICAgICAgICAgICAvLyBXYWxsZXRDb25uZWN0OiBkZWNvZGUgZXhwZWN0ZWQgcmVjaXBpZW50IGFuZCBhbW91bnQgZnJvbSByZWNpcGllbnRzWzBdLmRhdGFcbiAgICAgICAgICAgIGNvbnN0IFtleHBlY3RlZFJlY2lwaWVudCwgZXhwZWN0ZWRBbW91bnRdID0gZ2V0UmF3RGVjb2RlZChcbiAgICAgICAgICAgICAgWydhZGRyZXNzJywgJ3VpbnQyNTYnXSxcbiAgICAgICAgICAgICAgZ2V0QnVmZmVyZWRCeXRlQ29kZSgnMHhhOTA1OWNiYicsIHJlY2lwaWVudERhdGEpXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgZXhwZWN0ZWRSZWNpcGllbnRBZGRyZXNzID0gYWRkSGV4UHJlZml4KGV4cGVjdGVkUmVjaXBpZW50LnRvU3RyaW5nKCkpLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgICAgICBleHBlY3RlZFRva2VuQW1vdW50ID0gZXhwZWN0ZWRBbW91bnQudG9TdHJpbmcoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gTm9ybWFsIGZsb3c6IHVzZSByZWNpcGllbnRzWzBdLmFkZHJlc3MgYW5kIHJlY2lwaWVudHNbMF0uYW1vdW50XG4gICAgICAgICAgICBleHBlY3RlZFJlY2lwaWVudEFkZHJlc3MgPSBleHBlY3RlZERlc3RpbmF0aW9uLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgICAgICBleHBlY3RlZFRva2VuQW1vdW50ID0gZXhwZWN0ZWRBbW91bnQ7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKGV4cGVjdGVkVG9rZW5BbW91bnQgIT09IGFtb3VudC50b1N0cmluZygpKSB7XG4gICAgICAgICAgICBhd2FpdCB0aHJvd1JlY2lwaWVudE1pc21hdGNoKFxuICAgICAgICAgICAgICAndGhlIHRyYW5zYWN0aW9uIGFtb3VudCBpbiB0eFByZWJ1aWxkIGRvZXMgbm90IG1hdGNoIHRoZSB2YWx1ZSBnaXZlbiBieSBjbGllbnQnLFxuICAgICAgICAgICAgICBbeyBhZGRyZXNzOiBhZGRIZXhQcmVmaXgocmVjaXBpZW50QWRkcmVzcy50b1N0cmluZygpKSwgYW1vdW50OiBhbW91bnQudG9TdHJpbmcoKSB9XVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAoZXhwZWN0ZWRSZWNpcGllbnRBZGRyZXNzICE9PSBhZGRIZXhQcmVmaXgocmVjaXBpZW50QWRkcmVzcy50b1N0cmluZygpKS50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgICAgICBhd2FpdCB0aHJvd1JlY2lwaWVudE1pc21hdGNoKCdkZXN0aW5hdGlvbiBhZGRyZXNzIGRvZXMgbm90IG1hdGNoIHdpdGggdGhlIHJlY2lwaWVudCBhZGRyZXNzJywgW1xuICAgICAgICAgICAgICB7IGFkZHJlc3M6IGFkZEhleFByZWZpeChyZWNpcGllbnRBZGRyZXNzLnRvU3RyaW5nKCkpLCBhbW91bnQ6IGFtb3VudC50b1N0cmluZygpIH0sXG4gICAgICAgICAgICBdKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAodHhQYXJhbXMudHlwZSAmJiBbJ2RlZmlBcHByb3ZlJywgJ2RlZmlEZXBvc2l0JywgJ2RlZmlXaXRoZHJhdyddLmluY2x1ZGVzKHR4UGFyYW1zLnR5cGUpKSB7XG4gICAgICBpZiAoIXR4UHJlYnVpbGQudHhIZXgpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdtaXNzaW5nIHR4SGV4IGluIHR4UHJlYnVpbGQnKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgYmFzZUFkZHJlc3MgPSB3YWxsZXQuY29pblNwZWNpZmljKCk/LmJhc2VBZGRyZXNzO1xuICAgICAgY29uc3QgZGVmaVBhcmFtcyA9IHR4UGFyYW1zLmRlZmlQYXJhbXM7XG4gICAgICBpZiAoIWJhc2VBZGRyZXNzIHx8ICFkZWZpUGFyYW1zKSB7XG4gICAgICAgIGF3YWl0IHRocm93UmVjaXBpZW50TWlzbWF0Y2goJ0RlRmkgdHJhbnNhY3Rpb24gaXMgbWlzc2luZyByZXF1aXJlZCBpbnRlbnQgcGFyYW1ldGVycycsIFtdKTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuXG4gICAgICBjb25zdCB2YWxpZGF0ZWRCYXNlQWRkcmVzcyA9IGJhc2VBZGRyZXNzIGFzIHN0cmluZztcbiAgICAgIGNvbnN0IHZhbGlkYXRlZERlZmlQYXJhbXMgPSBkZWZpUGFyYW1zO1xuICAgICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIoKTtcbiAgICAgIHR4QnVpbGRlci5mcm9tKHR4UHJlYnVpbGQudHhIZXgpO1xuICAgICAgY29uc3QgdHhKc29uID0gKGF3YWl0IHR4QnVpbGRlci5idWlsZCgpKS50b0pzb24oKTtcbiAgICAgIGNvbnN0IG1pc21hdGNoID0gYXN5bmMgKG1lc3NhZ2U6IHN0cmluZywgYWRkcmVzcyA9IHR4SnNvbi50bywgYW1vdW50ID0gdHhKc29uLnZhbHVlKTogUHJvbWlzZTxuZXZlcj4gPT5cbiAgICAgICAgdGhyb3dSZWNpcGllbnRNaXNtYXRjaChtZXNzYWdlLCBbeyBhZGRyZXNzOiBhZGRyZXNzIHx8ICcnLCBhbW91bnQ6IGFtb3VudCB8fCAnJyB9XSk7XG4gICAgICBjb25zdCBkYXRhID0gdHhKc29uLmRhdGEgfHwgJyc7XG5cbiAgICAgIGlmICh0eFBhcmFtcy50eXBlID09PSAnZGVmaUFwcHJvdmUnKSB7XG4gICAgICAgIGNvbnN0IHNlbGVjdG9yID0gYWRkSGV4UHJlZml4KG9wdGlvbmFsRGVwcy5ldGhBYmkubWV0aG9kSUQoJ2FwcHJvdmUnLCBbJ2FkZHJlc3MnLCAndWludDI1NiddKS50b1N0cmluZygnaGV4JykpO1xuICAgICAgICBpZiAoIWRhdGEuc3RhcnRzV2l0aChzZWxlY3RvcikpIHtcbiAgICAgICAgICBhd2FpdCBtaXNtYXRjaCgnZGVmaUFwcHJvdmUgdHJhbnNhY3Rpb24gbXVzdCB1c2UgRVJDLTIwIGFwcHJvdmUoYWRkcmVzcyx1aW50MjU2KSBjYWxsZGF0YScpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IFssIGFtb3VudF0gPSBnZXRSYXdEZWNvZGVkKFsnYWRkcmVzcycsICd1aW50MjU2J10sIGdldEJ1ZmZlcmVkQnl0ZUNvZGUoc2VsZWN0b3IsIGRhdGEpKTtcbiAgICAgICAgaWYgKGFtb3VudC50b1N0cmluZygpICE9PSB2YWxpZGF0ZWREZWZpUGFyYW1zLmFtb3VudCkge1xuICAgICAgICAgIGF3YWl0IG1pc21hdGNoKFxuICAgICAgICAgICAgJ2RlZmlBcHByb3ZlIHRyYW5zYWN0aW9uIGFtb3VudCBkb2VzIG5vdCBtYXRjaCB0aGUgcmVxdWVzdGVkIGFtb3VudCcsXG4gICAgICAgICAgICB0eEpzb24udG8sXG4gICAgICAgICAgICBhbW91bnQudG9TdHJpbmcoKVxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAodHhQYXJhbXMudHlwZSA9PT0gJ2RlZmlEZXBvc2l0Jykge1xuICAgICAgICBjb25zdCBzZWxlY3RvciA9IGFkZEhleFByZWZpeChvcHRpb25hbERlcHMuZXRoQWJpLm1ldGhvZElEKCdkZXBvc2l0JywgWyd1aW50MjU2JywgJ2FkZHJlc3MnXSkudG9TdHJpbmcoJ2hleCcpKTtcbiAgICAgICAgaWYgKCFkYXRhLnN0YXJ0c1dpdGgoc2VsZWN0b3IpKSB7XG4gICAgICAgICAgYXdhaXQgbWlzbWF0Y2goJ2RlZmlEZXBvc2l0IHRyYW5zYWN0aW9uIG11c3QgdXNlIGRlcG9zaXQodWludDI1NixhZGRyZXNzKSBjYWxsZGF0YScpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IFthbW91bnQsIHJlY2VpdmVyXSA9IGdldFJhd0RlY29kZWQoWyd1aW50MjU2JywgJ2FkZHJlc3MnXSwgZ2V0QnVmZmVyZWRCeXRlQ29kZShzZWxlY3RvciwgZGF0YSkpO1xuICAgICAgICBjb25zdCBkZWNvZGVkUmVjZWl2ZXIgPSBhZGRIZXhQcmVmaXgocmVjZWl2ZXIudG9TdHJpbmcoKSkudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgaWYgKGRlY29kZWRSZWNlaXZlciAhPT0gdmFsaWRhdGVkQmFzZUFkZHJlc3MudG9Mb3dlckNhc2UoKSkge1xuICAgICAgICAgIGF3YWl0IG1pc21hdGNoKFxuICAgICAgICAgICAgJ2RlZmlEZXBvc2l0IHRyYW5zYWN0aW9uIHJlY2VpdmVyIGRvZXMgbm90IG1hdGNoIHdhbGxldCBiYXNlIGFkZHJlc3MnLFxuICAgICAgICAgICAgZGVjb2RlZFJlY2VpdmVyLFxuICAgICAgICAgICAgYW1vdW50LnRvU3RyaW5nKClcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGlmIChhbW91bnQudG9TdHJpbmcoKSAhPT0gdmFsaWRhdGVkRGVmaVBhcmFtcy5hbW91bnQpIHtcbiAgICAgICAgICBhd2FpdCBtaXNtYXRjaChcbiAgICAgICAgICAgICdkZWZpRGVwb3NpdCB0cmFuc2FjdGlvbiBhbW91bnQgZG9lcyBub3QgbWF0Y2ggdGhlIHJlcXVlc3RlZCBhbW91bnQnLFxuICAgICAgICAgICAgZGVjb2RlZFJlY2VpdmVyLFxuICAgICAgICAgICAgYW1vdW50LnRvU3RyaW5nKClcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBzZWxlY3RvciA9IGFkZEhleFByZWZpeChcbiAgICAgICAgICBvcHRpb25hbERlcHMuZXRoQWJpLm1ldGhvZElEKCdyZWRlZW0nLCBbJ3VpbnQyNTYnLCAnYWRkcmVzcycsICdhZGRyZXNzJ10pLnRvU3RyaW5nKCdoZXgnKVxuICAgICAgICApO1xuICAgICAgICBpZiAoIWRhdGEuc3RhcnRzV2l0aChzZWxlY3RvcikpIHtcbiAgICAgICAgICBhd2FpdCBtaXNtYXRjaCgnZGVmaVdpdGhkcmF3IHRyYW5zYWN0aW9uIG11c3QgdXNlIHJlZGVlbSh1aW50MjU2LGFkZHJlc3MsYWRkcmVzcykgY2FsbGRhdGEnKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBbc2hhcmVzLCByZWNlaXZlciwgb3duZXJdID0gZ2V0UmF3RGVjb2RlZChcbiAgICAgICAgICBbJ3VpbnQyNTYnLCAnYWRkcmVzcycsICdhZGRyZXNzJ10sXG4gICAgICAgICAgZ2V0QnVmZmVyZWRCeXRlQ29kZShzZWxlY3RvciwgZGF0YSlcbiAgICAgICAgKTtcbiAgICAgICAgY29uc3QgZGVjb2RlZFJlY2VpdmVyID0gYWRkSGV4UHJlZml4KHJlY2VpdmVyLnRvU3RyaW5nKCkpLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGNvbnN0IGRlY29kZWRPd25lciA9IGFkZEhleFByZWZpeChvd25lci50b1N0cmluZygpKS50b0xvd2VyQ2FzZSgpO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgZGVjb2RlZFJlY2VpdmVyICE9PSB2YWxpZGF0ZWRCYXNlQWRkcmVzcy50b0xvd2VyQ2FzZSgpIHx8XG4gICAgICAgICAgZGVjb2RlZE93bmVyICE9PSB2YWxpZGF0ZWRCYXNlQWRkcmVzcy50b0xvd2VyQ2FzZSgpXG4gICAgICAgICkge1xuICAgICAgICAgIGF3YWl0IG1pc21hdGNoKFxuICAgICAgICAgICAgJ2RlZmlXaXRoZHJhdyB0cmFuc2FjdGlvbiByZWNlaXZlciBhbmQgb3duZXIgbXVzdCBtYXRjaCB3YWxsZXQgYmFzZSBhZGRyZXNzJyxcbiAgICAgICAgICAgIGRlY29kZWRSZWNlaXZlcixcbiAgICAgICAgICAgIHNoYXJlcy50b1N0cmluZygpXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoc2hhcmVzLnRvU3RyaW5nKCkgIT09IHZhbGlkYXRlZERlZmlQYXJhbXMuYW1vdW50KSB7XG4gICAgICAgICAgYXdhaXQgbWlzbWF0Y2goXG4gICAgICAgICAgICAnZGVmaVdpdGhkcmF3IHRyYW5zYWN0aW9uIHNoYXJlcyBkbyBub3QgbWF0Y2ggdGhlIHJlcXVlc3RlZCBhbW91bnQnLFxuICAgICAgICAgICAgZGVjb2RlZFJlY2VpdmVyLFxuICAgICAgICAgICAgc2hhcmVzLnRvU3RyaW5nKClcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHR4UGFyYW1zLnR5cGUgPT09ICd3cmFwQXBwcm92ZScpIHtcbiAgICAgIGF3YWl0IHRoaXMudmVyaWZ5V3JhcEFwcHJvdmVUcmFuc2FjdGlvbihwYXJhbXMsIHRocm93UmVjaXBpZW50TWlzbWF0Y2gpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBEZWNsYXJlZCBhcyBhbnl0aGluZyBlbHNlOiBvbmx5IGFuIGFwcHJvdmUgb2YgYSByZWdpc3RlcmVkIHdyYXBwZXIgaXMgcmVqZWN0ZWQgKHNlZSB0aGUgbWV0aG9kIGRvYyk7XG4gICAgICAvLyBmb3IgZXZlcnkgb3RoZXIgdHggdGhpcyByZXR1cm5zIHdpdGhvdXQgcGFyc2luZyBvciBhZnRlciBvbmUgYmVzdC1lZmZvcnQgcGFyc2UuXG4gICAgICBhd2FpdCB0aGlzLmFzc2VydFdyYXBwZXJBcHByb3ZlSXNEZWNsYXJlZChwYXJhbXMsIHRocm93UmVjaXBpZW50TWlzbWF0Y2gpO1xuICAgIH1cblxuICAgIC8vIFZlcmlmeSBjb25zb2xpZGF0aW9uIHRyYW5zYWN0aW9ucyBzZW5kIHRvIGJhc2UgYWRkcmVzc1xuICAgIGlmIChwYXJhbXMudmVyaWZpY2F0aW9uPy5jb25zb2xpZGF0aW9uVG9CYXNlQWRkcmVzcykge1xuICAgICAgY29uc3QgY29pblNwZWNpZmljID0gd2FsbGV0LmNvaW5TcGVjaWZpYygpO1xuICAgICAgaWYgKCFjb2luU3BlY2lmaWMgfHwgIWNvaW5TcGVjaWZpYy5iYXNlQWRkcmVzcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VuYWJsZSB0byBkZXRlcm1pbmUgYmFzZSBhZGRyZXNzIGZvciBjb25zb2xpZGF0aW9uJyk7XG4gICAgICB9XG4gICAgICBjb25zdCBiYXNlQWRkcmVzcyA9IGNvaW5TcGVjaWZpYy5iYXNlQWRkcmVzcztcblxuICAgICAgaWYgKCF0eFByZWJ1aWxkLnR4SGV4KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyB0eEhleCBpbiB0eFByZWJ1aWxkJyk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHR4QnVpbGRlciA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25CdWlsZGVyKCk7XG4gICAgICB0eEJ1aWxkZXIuZnJvbSh0eFByZWJ1aWxkLnR4SGV4KTtcbiAgICAgIGNvbnN0IHR4ID0gYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCk7XG4gICAgICBjb25zdCB0eEpzb24gPSB0eC50b0pzb24oKTtcblxuICAgICAgLy8gVmVyaWZ5IHRoZSB0cmFuc2FjdGlvbiByZWNpcGllbnQgbWF0Y2hlcyB0aGUgYmFzZSBhZGRyZXNzXG4gICAgICBpZiAoIXR4SnNvbi50bykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvbnNvbGlkYXRpb24gdHJhbnNhY3Rpb24gaXMgbWlzc2luZyByZWNpcGllbnQgYWRkcmVzcycpO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBlcmMyMFRyYW5zZmVyU2VsZWN0b3IgPSBhZGRIZXhQcmVmaXgoXG4gICAgICAgIG9wdGlvbmFsRGVwcy5ldGhBYmkubWV0aG9kSUQoJ3RyYW5zZmVyJywgWydhZGRyZXNzJywgJ3VpbnQyNTYnXSkudG9TdHJpbmcoJ2hleCcpXG4gICAgICApO1xuXG4gICAgICBpZiAodHhKc29uLmRhdGEgJiYgdHhKc29uLmRhdGEuc3RhcnRzV2l0aChlcmMyMFRyYW5zZmVyU2VsZWN0b3IpKSB7XG4gICAgICAgIC8vIEZvciBFUkMyMCB0b2tlbiBjb25zb2xpZGF0aW9ucywgdHhKc29uLnRvIGlzIHRoZSB0b2tlbiBjb250cmFjdCBhZGRyZXNzLFxuICAgICAgICAvLyBub3QgdGhlIGFjdHVhbCB0cmFuc2ZlciByZWNpcGllbnQuIFRoZSByZWFsIHJlY2lwaWVudCBpcyBlbmNvZGVkIGluIHRoZVxuICAgICAgICAvLyB0cmFuc2ZlcihhZGRyZXNzLHVpbnQyNTYpIGNhbGxkYXRhLiBEZWNvZGUgaXQgYW5kIHZlcmlmeSBhZ2FpbnN0IGJhc2VBZGRyZXNzLlxuICAgICAgICBjb25zdCBbcmVjaXBpZW50QWRkcmVzc10gPSBnZXRSYXdEZWNvZGVkKFxuICAgICAgICAgIFsnYWRkcmVzcycsICd1aW50MjU2J10sXG4gICAgICAgICAgZ2V0QnVmZmVyZWRCeXRlQ29kZShlcmMyMFRyYW5zZmVyU2VsZWN0b3IsIHR4SnNvbi5kYXRhKVxuICAgICAgICApO1xuICAgICAgICBjb25zdCBkZWNvZGVkUmVjaXBpZW50ID0gYWRkSGV4UHJlZml4KHJlY2lwaWVudEFkZHJlc3MudG9TdHJpbmcoKSkudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgaWYgKGRlY29kZWRSZWNpcGllbnQgIT09IGJhc2VBZGRyZXNzLnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgICAgICBhd2FpdCB0aHJvd1JlY2lwaWVudE1pc21hdGNoKCdDb25zb2xpZGF0aW9uIHRyYW5zYWN0aW9uIHJlY2lwaWVudCBkb2VzIG5vdCBtYXRjaCB3YWxsZXQgYmFzZSBhZGRyZXNzJywgW1xuICAgICAgICAgICAgeyBhZGRyZXNzOiBkZWNvZGVkUmVjaXBpZW50LCBhbW91bnQ6IHR4SnNvbi52YWx1ZSB9LFxuICAgICAgICAgIF0pO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHR4SnNvbi50by50b0xvd2VyQ2FzZSgpICE9PSBiYXNlQWRkcmVzcy50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIC8vIE5hdGl2ZSBjb2luIGNvbnNvbGlkYXRpb246IHR4SnNvbi50byBpcyB0aGUgYWN0dWFsIHJlY2lwaWVudFxuICAgICAgICBhd2FpdCB0aHJvd1JlY2lwaWVudE1pc21hdGNoKCdDb25zb2xpZGF0aW9uIHRyYW5zYWN0aW9uIHJlY2lwaWVudCBkb2VzIG5vdCBtYXRjaCB3YWxsZXQgYmFzZSBhZGRyZXNzJywgW1xuICAgICAgICAgIHsgYWRkcmVzczogdHhKc29uLnRvLCBhbW91bnQ6IHR4SnNvbi52YWx1ZSB9LFxuICAgICAgICBdKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZnkgdGhhdCBhIHRyYW5zYWN0aW9uIHByZWJ1aWxkIGNvbXBsaWVzIHdpdGggdGhlIG9yaWdpbmFsIGludGVudGlvblxuICAgKlxuICAgKiBAcGFyYW0ge1ZlcmlmeUV0aFRyYW5zYWN0aW9uT3B0aW9uc30gcGFyYW1zXG4gICAqIEBwYXJhbSB7VHJhbnNhY3Rpb25QYXJhbXN9IHBhcmFtcy50eFBhcmFtcyAtIHBhcmFtcyBvYmplY3QgcGFzc2VkIHRvIHNlbmRcbiAgICogQHBhcmFtIHtUcmFuc2FjdGlvblByZWJ1aWxkfSBwYXJhbXMudHhQcmVidWlsZCAtIHByZWJ1aWxkIG9iamVjdCByZXR1cm5lZCBieSBzZXJ2ZXJcbiAgICogQHBhcmFtIHtXYWxsZXR9IHBhcmFtcy53YWxsZXQgLSBXYWxsZXQgb2JqZWN0IHRvIG9idGFpbiBrZXlzIHRvIHZlcmlmeSBhZ2FpbnN0XG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKiBAdGhyb3dzIHtUeEludGVudE1pc21hdGNoRXJyb3J9IGlmIHRyYW5zYWN0aW9uIHZhbGlkYXRpb24gZmFpbHNcbiAgICogQHRocm93cyB7VHhJbnRlbnRNaXNtYXRjaFJlY2lwaWVudEVycm9yfSBpZiB0cmFuc2FjdGlvbiByZWNpcGllbnRzIGRvbid0IG1hdGNoIHVzZXIgaW50ZW50XG4gICAqL1xuICBhc3luYyB2ZXJpZnlUcmFuc2FjdGlvbihwYXJhbXM6IFZlcmlmeUV0aFRyYW5zYWN0aW9uT3B0aW9ucyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IGV0aE5ldHdvcmsgPSB0aGlzLmdldE5ldHdvcmsoKTtcbiAgICBjb25zdCB7IHR4UGFyYW1zLCB0eFByZWJ1aWxkLCB3YWxsZXQsIHdhbGxldFR5cGUgfSA9IHBhcmFtcztcblxuICAgIGlmICh3YWxsZXRUeXBlID09PSAndHNzJykge1xuICAgICAgcmV0dXJuIHRoaXMudmVyaWZ5VHNzVHJhbnNhY3Rpb24ocGFyYW1zKTtcbiAgICB9XG5cbiAgICAvLyBIZWxwZXIgdG8gdGhyb3cgVHhJbnRlbnRNaXNtYXRjaFJlY2lwaWVudEVycm9yIHdpdGggcmVjaXBpZW50IGRldGFpbHNcbiAgICBjb25zdCB0aHJvd1JlY2lwaWVudE1pc21hdGNoID0gYXN5bmMgKG1lc3NhZ2U6IHN0cmluZywgbWlzbWF0Y2hlZFJlY2lwaWVudHM6IFJlY2lwaWVudFtdKTogUHJvbWlzZTxuZXZlcj4gPT4ge1xuICAgICAgY29uc3QgdHhFeHBsYW5hdGlvbiA9IGF3YWl0IHRoaXMuZ2V0VHhFeHBsYW5hdGlvbih0eFByZWJ1aWxkKTtcbiAgICAgIHRocm93IG5ldyBUeEludGVudE1pc21hdGNoUmVjaXBpZW50RXJyb3IoXG4gICAgICAgIG1lc3NhZ2UsXG4gICAgICAgIHVuZGVmaW5lZCxcbiAgICAgICAgW3R4UGFyYW1zXSxcbiAgICAgICAgdHhQcmVidWlsZD8udHhIZXgsXG4gICAgICAgIG1pc21hdGNoZWRSZWNpcGllbnRzLFxuICAgICAgICB0eEV4cGxhbmF0aW9uXG4gICAgICApO1xuICAgIH07XG4gICAgLy8gRVJDLTc5ODQgc2hpZWxkIGFwcHJvdmFsIGZvciBub24tVFNTIChvbmNoYWluIG11bHRpc2lnKSB3YWxsZXRzOiBpbnRlbnQtYmFzZWQgd2l0aCBubyBjbGllbnRcbiAgICAvLyByZWNpcGllbnRzIOKAlCB0aGUgbXVsdGlzaWcgcHJlYnVpbGQgY2FycmllcyB0aGUgc2VuZE11bHRpU2lnLXdyYXBwZWQgYXBwcm92ZSBjYWxsZGF0YSBpbnN0ZWFkLlxuICAgIC8vIENoZWNrZWQgYmVmb3JlIHRoZSBnZW5lcmljIHJlY2lwaWVudCBndWFyZHMsIHdoaWNoIHJlcXVpcmUgdHhQYXJhbXMucmVjaXBpZW50cy5cbiAgICBpZiAodHhQYXJhbXM/LnR5cGUgPT09ICd3cmFwQXBwcm92ZScpIHtcbiAgICAgIGF3YWl0IHRoaXMudmVyaWZ5TXVsdGlzaWdXcmFwQXBwcm92ZVRyYW5zYWN0aW9uKHBhcmFtcywgdGhyb3dSZWNpcGllbnRNaXNtYXRjaCk7XG4gICAgICBhd2FpdCB0aGlzLnRocm93SWZQcmVidWlsZENvaW5NaXNtYXRjaCh0eFBhcmFtcywgdHhQcmVidWlsZCk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICBpZiAoIXR4UGFyYW1zPy5yZWNpcGllbnRzIHx8ICF0eFByZWJ1aWxkPy5yZWNpcGllbnRzIHx8ICF3YWxsZXQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyBwYXJhbXMnKTtcbiAgICB9XG5cbiAgICAvLyBFdmVyeSBkZWNsYXJlZCB0eXBlIHRoYXQgZGlkIG5vdCByZXR1cm4gYWJvdmU6IG9ubHkgYW4gYXBwcm92ZSBvZiBhIHJlZ2lzdGVyZWQgd3JhcHBlciBpcyByZWplY3RlZCwgc2VlblxuICAgIC8vIHRocm91Z2ggc2VuZE11bHRpU2lnIChzZWUgYXNzZXJ0V3JhcHBlckFwcHJvdmVJc0RlY2xhcmVkKS5cbiAgICBhd2FpdCB0aGlzLmFzc2VydFdyYXBwZXJBcHByb3ZlSXNEZWNsYXJlZChwYXJhbXMsIHRocm93UmVjaXBpZW50TWlzbWF0Y2gpO1xuXG4gICAgY29uc3QgcmVjaXBpZW50cyA9IHR4UGFyYW1zLnJlY2lwaWVudHMhO1xuXG4gICAgaWYgKHR4UGFyYW1zLmhvcCAmJiByZWNpcGllbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigndHggY2Fubm90IGJlIGJvdGggYSBiYXRjaCBhbmQgaG9wIHRyYW5zYWN0aW9uJyk7XG4gICAgfVxuICAgIGlmICh0eFByZWJ1aWxkLnJlY2lwaWVudHMubGVuZ3RoID4gMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgJHt0aGlzLmdldENoYWluKCl9IGRvZXNuJ3Qgc3VwcG9ydCBzZW5kaW5nIHRvIG1vcmUgdGhhbiAxIGRlc3RpbmF0aW9uIGFkZHJlc3Mgd2l0aGluIGEgc2luZ2xlIHRyYW5zYWN0aW9uLiBUcnkgYWdhaW4sIHVzaW5nIG9ubHkgYSBzaW5nbGUgcmVjaXBpZW50LmBcbiAgICAgICk7XG4gICAgfVxuICAgIGlmICh0eFBhcmFtcy50eXBlID09PSAnZW5hYmxldG9rZW4nKSB7XG4gICAgICAvLyBFUkMtNzk4NCB0b2tlbiBlbmFibGVtZW50OiBpbnRlcmNlcHQgYmVmb3JlIGhvcC9iYXRjaC9zaW5nbGUgcm91dGluZy5cbiAgICAgIC8vIEhhbmRsZXMgYm90aCBiYXNlLWFkZHJlc3MgKHR4UHJlYnVpbGQg4oaSIEFDTCkgYW5kIGZvcndhcmRlciAodHhQcmVidWlsZCDihpIgZm9yd2FyZGVyKSB3YWxsZXRzLFxuICAgICAgLy8gZm9yIHNpbmdsZSBvciBtdWx0aXBsZSB0b2tlbnMgYXQgb25jZS5cbiAgICAgIGF3YWl0IHRoaXMudmVyaWZ5TXVsdGlzaWdFbmFibGVUb2tlblRyYW5zYWN0aW9uKHBhcmFtcywgdGhyb3dSZWNpcGllbnRNaXNtYXRjaCk7XG4gICAgfSBlbHNlIGlmICh0eFBhcmFtcy5ob3AgJiYgdHhQcmVidWlsZC5ob3BUcmFuc2FjdGlvbikge1xuICAgICAgLy8gQ2hlY2sgcmVjaXBpZW50IGFtb3VudCBmb3IgaG9wIHRyYW5zYWN0aW9uXG4gICAgICBpZiAocmVjaXBpZW50cy5sZW5ndGggIT09IDEpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBob3AgdHJhbnNhY3Rpb24gb25seSBzdXBwb3J0cyAxIHJlY2lwaWVudCBidXQgJHtyZWNpcGllbnRzLmxlbmd0aH0gZm91bmRgKTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgdHggc2VuZHMgdG8gaG9wIGFkZHJlc3NcbiAgICAgIGNvbnN0IGRlY29kZWRIb3BUeCA9IG9wdGlvbmFsRGVwcy5FdGhUeC5UcmFuc2FjdGlvbkZhY3RvcnkuZnJvbVNlcmlhbGl6ZWREYXRhKFxuICAgICAgICBvcHRpb25hbERlcHMuZXRoVXRpbC50b0J1ZmZlcih0eFByZWJ1aWxkLmhvcFRyYW5zYWN0aW9uLnR4KVxuICAgICAgKTtcbiAgICAgIGNvbnN0IGV4cGVjdGVkSG9wQWRkcmVzcyA9IG9wdGlvbmFsRGVwcy5ldGhVdGlsLnN0cmlwSGV4UHJlZml4KGRlY29kZWRIb3BUeC5nZXRTZW5kZXJBZGRyZXNzKCkudG9TdHJpbmcoKSk7XG4gICAgICBjb25zdCBhY3R1YWxIb3BBZGRyZXNzID0gb3B0aW9uYWxEZXBzLmV0aFV0aWwuc3RyaXBIZXhQcmVmaXgodHhQcmVidWlsZC5yZWNpcGllbnRzWzBdLmFkZHJlc3MpO1xuICAgICAgaWYgKGV4cGVjdGVkSG9wQWRkcmVzcy50b0xvd2VyQ2FzZSgpICE9PSBhY3R1YWxIb3BBZGRyZXNzLnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgICAgYXdhaXQgdGhyb3dSZWNpcGllbnRNaXNtYXRjaCgncmVjaXBpZW50IGFkZHJlc3Mgb2YgdHhQcmVidWlsZCBkb2VzIG5vdCBtYXRjaCBob3AgYWRkcmVzcycsIFtcbiAgICAgICAgICB7IGFkZHJlc3M6IHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hZGRyZXNzLCBhbW91bnQ6IHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hbW91bnQudG9TdHJpbmcoKSB9LFxuICAgICAgICBdKTtcbiAgICAgIH1cblxuICAgICAgLy8gQ29udmVydCBUcmFuc2FjdGlvblJlY2lwaWVudCBhcnJheSB0byBSZWNpcGllbnQgYXJyYXlcbiAgICAgIGNvbnN0IGhvcFJlY2lwaWVudHM6IFJlY2lwaWVudFtdID0gcmVjaXBpZW50cy5tYXAoKHIpID0+IHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBhZGRyZXNzOiByLmFkZHJlc3MsXG4gICAgICAgICAgYW1vdW50OiB0eXBlb2Ygci5hbW91bnQgPT09ICdudW1iZXInID8gci5hbW91bnQudG9TdHJpbmcoKSA6IHIuYW1vdW50LFxuICAgICAgICB9O1xuICAgICAgfSk7XG5cbiAgICAgIC8vIENoZWNrIGRlc3RpbmF0aW9uIGFkZHJlc3MgYW5kIGFtb3VudFxuICAgICAgYXdhaXQgdGhpcy52YWxpZGF0ZUhvcFByZWJ1aWxkKHdhbGxldCwgdHhQcmVidWlsZC5ob3BUcmFuc2FjdGlvbiwgeyByZWNpcGllbnRzOiBob3BSZWNpcGllbnRzIH0pO1xuICAgIH0gZWxzZSBpZiAocmVjaXBpZW50cy5sZW5ndGggPiAxKSB7XG4gICAgICAvLyBDaGVjayB0b3RhbCBhbW91bnQgZm9yIGJhdGNoIHRyYW5zYWN0aW9uXG4gICAgICBpZiAodHhQYXJhbXMudG9rZW5OYW1lKSB7XG4gICAgICAgIGNvbnN0IGV4cGVjdGVkVG90YWxBbW91bnQgPSBuZXcgQmlnTnVtYmVyKDApO1xuICAgICAgICBpZiAoIWV4cGVjdGVkVG90YWxBbW91bnQuaXNFcXVhbFRvKHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hbW91bnQpKSB7XG4gICAgICAgICAgYXdhaXQgdGhyb3dSZWNpcGllbnRNaXNtYXRjaChcbiAgICAgICAgICAgICdiYXRjaCB0b2tlbiB0cmFuc2FjdGlvbiBhbW91bnQgaW4gdHhQcmVidWlsZCBzaG91bGQgYmUgemVybyBmb3IgdG9rZW4gdHJhbnNmZXJzJyxcbiAgICAgICAgICAgIFt7IGFkZHJlc3M6IHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hZGRyZXNzLCBhbW91bnQ6IHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hbW91bnQudG9TdHJpbmcoKSB9XVxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxldCBleHBlY3RlZFRvdGFsQW1vdW50ID0gbmV3IEJpZ051bWJlcigwKTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCByZWNpcGllbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgZXhwZWN0ZWRUb3RhbEFtb3VudCA9IGV4cGVjdGVkVG90YWxBbW91bnQucGx1cyhyZWNpcGllbnRzW2ldLmFtb3VudCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFleHBlY3RlZFRvdGFsQW1vdW50LmlzRXF1YWxUbyh0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYW1vdW50KSkge1xuICAgICAgICAgIGF3YWl0IHRocm93UmVjaXBpZW50TWlzbWF0Y2goXG4gICAgICAgICAgICAnYmF0Y2ggdHJhbnNhY3Rpb24gYW1vdW50IGluIHR4UHJlYnVpbGQgcmVjZWl2ZWQgZnJvbSBCaXRHbyBzZXJ2ZXJzIGRvZXMgbm90IG1hdGNoIHR4UGFyYW1zIHN1cHBsaWVkIGJ5IGNsaWVudCcsXG4gICAgICAgICAgICBbeyBhZGRyZXNzOiB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYWRkcmVzcywgYW1vdW50OiB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYW1vdW50LnRvU3RyaW5nKCkgfV1cbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGJhdGNoIHRyYW5zYWN0aW9uIGlzIHNlbnQgdG8gdGhlIGJhdGNoZXIgY29udHJhY3QgYWRkcmVzcyBmb3IgdGhlIGNoYWluXG4gICAgICBjb25zdCBiYXRjaGVyQ29udHJhY3RBZGRyZXNzID0gZXRoTmV0d29yaz8uYmF0Y2hlckNvbnRyYWN0QWRkcmVzcztcbiAgICAgIGlmIChcbiAgICAgICAgIWJhdGNoZXJDb250cmFjdEFkZHJlc3MgfHxcbiAgICAgICAgYmF0Y2hlckNvbnRyYWN0QWRkcmVzcy50b0xvd2VyQ2FzZSgpICE9PSB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYWRkcmVzcy50b0xvd2VyQ2FzZSgpXG4gICAgICApIHtcbiAgICAgICAgYXdhaXQgdGhyb3dSZWNpcGllbnRNaXNtYXRjaCgncmVjaXBpZW50IGFkZHJlc3Mgb2YgdHhQcmVidWlsZCBkb2VzIG5vdCBtYXRjaCBiYXRjaGVyIGFkZHJlc3MnLCBbXG4gICAgICAgICAgeyBhZGRyZXNzOiB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYWRkcmVzcywgYW1vdW50OiB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYW1vdW50LnRvU3RyaW5nKCkgfSxcbiAgICAgICAgXSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIENoZWNrIHJlY2lwaWVudCBhZGRyZXNzIGFuZCBhbW91bnQgZm9yIG5vcm1hbCB0cmFuc2FjdGlvblxuICAgICAgaWYgKHJlY2lwaWVudHMubGVuZ3RoICE9PSAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgbm9ybWFsIHRyYW5zYWN0aW9uIG9ubHkgc3VwcG9ydHMgMSByZWNpcGllbnQgYnV0ICR7cmVjaXBpZW50cy5sZW5ndGh9IGZvdW5kYCk7XG4gICAgICB9XG4gICAgICBjb25zdCBleHBlY3RlZEFtb3VudCA9IG5ldyBCaWdOdW1iZXIocmVjaXBpZW50c1swXS5hbW91bnQpO1xuICAgICAgaWYgKCFleHBlY3RlZEFtb3VudC5pc0VxdWFsVG8odHhQcmVidWlsZC5yZWNpcGllbnRzWzBdLmFtb3VudCkpIHtcbiAgICAgICAgYXdhaXQgdGhyb3dSZWNpcGllbnRNaXNtYXRjaChcbiAgICAgICAgICAnbm9ybWFsIHRyYW5zYWN0aW9uIGFtb3VudCBpbiB0eFByZWJ1aWxkIHJlY2VpdmVkIGZyb20gQml0R28gc2VydmVycyBkb2VzIG5vdCBtYXRjaCB0eFBhcmFtcyBzdXBwbGllZCBieSBjbGllbnQnLFxuICAgICAgICAgIFt7IGFkZHJlc3M6IHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hZGRyZXNzLCBhbW91bnQ6IHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hbW91bnQudG9TdHJpbmcoKSB9XVxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuaXNFVEhBZGRyZXNzKHJlY2lwaWVudHNbMF0uYWRkcmVzcykgJiYgcmVjaXBpZW50c1swXS5hZGRyZXNzICE9PSB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYWRkcmVzcykge1xuICAgICAgICBhd2FpdCB0aHJvd1JlY2lwaWVudE1pc21hdGNoKFxuICAgICAgICAgICdkZXN0aW5hdGlvbiBhZGRyZXNzIGluIG5vcm1hbCB0eFByZWJ1aWxkIGRvZXMgbm90IG1hdGNoIHRoYXQgaW4gdHhQYXJhbXMgc3VwcGxpZWQgYnkgY2xpZW50JyxcbiAgICAgICAgICBbeyBhZGRyZXNzOiB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYWRkcmVzcywgYW1vdW50OiB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0uYW1vdW50LnRvU3RyaW5nKCkgfV1cbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gQ2hlY2sgY29pbiBpcyBjb3JyZWN0IGZvciBhbGwgdHJhbnNhY3Rpb24gdHlwZXNcbiAgICBhd2FpdCB0aGlzLnRocm93SWZQcmVidWlsZENvaW5NaXNtYXRjaCh0eFBhcmFtcywgdHhQcmVidWlsZCk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgYWRkcmVzcyBpcyB2YWxpZCBldGggYWRkcmVzc1xuICAgKiBAcGFyYW0gYWRkcmVzc1xuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIC8qKlxuICAgKiBWZXJpZmllcyBhbiBFUkMtNzk4NCB0b2tlbiBlbmFibGVtZW50IChkZWNyeXB0aW9uIGRlbGVnYXRpb24pIHRyYW5zYWN0aW9uIGZvciBtdWx0aXNpZyB3YWxsZXRzLlxuICAgKlxuICAgKiBUd28gd2FsbGV0IHNoYXBlcyBhcmUgc3VwcG9ydGVkOlxuICAgKlxuICAgKiAgQmFzZS1hZGRyZXNzIHdhbGxldCDigJQgdHhQcmVidWlsZCB0YXJnZXRzIHRoZSBaYW1hIEFDTCBjb250cmFjdCBkaXJlY3RseTpcbiAgICogICAgdHhQYXJhbXMucmVjaXBpZW50cyAgOiBvbmUgZW50cnkgcGVyIHRva2VuLCBhbGwgd2l0aCB0aGUgd2FsbGV0IGJhc2UgYWRkcmVzcywgYW1vdW50ICcwJ1xuICAgKiAgICB0eFByZWJ1aWxkLnJlY2lwaWVudHNbMF0gOiB0aGUgWmFtYSBBQ0wgY29udHJhY3QgYWRkcmVzc1xuICAgKlxuICAgKiAgRm9yd2FyZGVyIHdhbGxldCDigJQgdHhQcmVidWlsZCB0YXJnZXRzIHRoZSBmb3J3YXJkZXIgd2hpY2ggY2FsbHMgdGhlIEFDTCB2aWEgY2FsbEZyb21QYXJlbnQ6XG4gICAqICAgIHR4UGFyYW1zLnJlY2lwaWVudHMgIDogb25lIGVudHJ5IHBlciB0b2tlbiwgYWxsIHdpdGggdGhlIGZvcndhcmRlciBhZGRyZXNzLCBhbW91bnQgJzAnXG4gICAqICAgIHR4UHJlYnVpbGQucmVjaXBpZW50c1swXSA6IHRoZSBmb3J3YXJkZXIgYWRkcmVzcyAobWF0Y2hlcyB0eFBhcmFtcyByZWNpcGllbnRzKVxuICAgKlxuICAgKiBJbiBib3RoIGNhc2VzIGFsbCBhbW91bnRzIG11c3QgYmUgMCBhbmQgYWxsIHR4UGFyYW1zIHJlY2lwaWVudHMgbXVzdCBzaGFyZSB0aGUgc2FtZSBhZGRyZXNzLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB2ZXJpZnlNdWx0aXNpZ0VuYWJsZVRva2VuVHJhbnNhY3Rpb24oXG4gICAgcGFyYW1zOiBWZXJpZnlFdGhUcmFuc2FjdGlvbk9wdGlvbnMsXG4gICAgdGhyb3dSZWNpcGllbnRNaXNtYXRjaDogKG1lc3NhZ2U6IHN0cmluZywgbWlzbWF0Y2hlZFJlY2lwaWVudHM6IFJlY2lwaWVudFtdKSA9PiBQcm9taXNlPG5ldmVyPlxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IHR4UGFyYW1zLCB0eFByZWJ1aWxkIH0gPSBwYXJhbXM7XG4gICAgY29uc3QgcmVjaXBpZW50cyA9IHR4UGFyYW1zLnJlY2lwaWVudHMhO1xuXG4gICAgaWYgKCFyZWNpcGllbnRzIHx8IHJlY2lwaWVudHMubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3Rva2VuIGVuYWJsZW1lbnQgdHJhbnNhY3Rpb24gbXVzdCBoYXZlIGF0IGxlYXN0IG9uZSByZWNpcGllbnQgaW4gdHhQYXJhbXMnKTtcbiAgICB9XG5cbiAgICAvLyBBbGwgdHhQYXJhbXMgcmVjaXBpZW50cyBtdXN0IGhhdmUgYW1vdW50IDBcbiAgICBmb3IgKGNvbnN0IHIgb2YgcmVjaXBpZW50cykge1xuICAgICAgaWYgKCFuZXcgQmlnTnVtYmVyKHIuYW1vdW50KS5pc0VxdWFsVG8oMCkpIHtcbiAgICAgICAgYXdhaXQgdGhyb3dSZWNpcGllbnRNaXNtYXRjaCgndG9rZW4gZW5hYmxlbWVudCB0eFBhcmFtcyByZWNpcGllbnRzIG11c3QgYWxsIGhhdmUgYW1vdW50IDAnLCBbXG4gICAgICAgICAgeyBhZGRyZXNzOiByLmFkZHJlc3MsIGFtb3VudDogU3RyaW5nKHIuYW1vdW50KSB9LFxuICAgICAgICBdKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyB0eFByZWJ1aWxkIHJlY2lwaWVudCBhbW91bnQgbXVzdCBiZSAwXG4gICAgaWYgKCFuZXcgQmlnTnVtYmVyKHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hbW91bnQpLmlzRXF1YWxUbygwKSkge1xuICAgICAgYXdhaXQgdGhyb3dSZWNpcGllbnRNaXNtYXRjaCgndG9rZW4gZW5hYmxlbWVudCB0cmFuc2FjdGlvbiBleHBlY3RlZCBhbW91bnQgMCBpbiB0eFByZWJ1aWxkJywgW1xuICAgICAgICB7IGFkZHJlc3M6IHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hZGRyZXNzLCBhbW91bnQ6IHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hbW91bnQudG9TdHJpbmcoKSB9LFxuICAgICAgXSk7XG4gICAgfVxuXG4gICAgLy8gQWxsIHR4UGFyYW1zIHJlY2lwaWVudHMgbXVzdCBzaGFyZSB0aGUgc2FtZSBhZGRyZXNzIOKAlFxuICAgIC8vIGEgc2luZ2xlIGRlbGVnYXRpb24gdHggY292ZXJzIG9uZSB3YWxsZXQsIG5vdCBtdWx0aXBsZSB3YWxsZXRzIHNpbXVsdGFuZW91c2x5LlxuICAgIGNvbnN0IGZpcnN0QWRkcmVzcyA9IHJlY2lwaWVudHNbMF0uYWRkcmVzcy50b0xvd2VyQ2FzZSgpO1xuICAgIGZvciAoY29uc3QgciBvZiByZWNpcGllbnRzKSB7XG4gICAgICBpZiAoci5hZGRyZXNzLnRvTG93ZXJDYXNlKCkgIT09IGZpcnN0QWRkcmVzcykge1xuICAgICAgICBhd2FpdCB0aHJvd1JlY2lwaWVudE1pc21hdGNoKCd0b2tlbiBlbmFibGVtZW50IHR4UGFyYW1zIHJlY2lwaWVudHMgbXVzdCBhbGwgaGF2ZSB0aGUgc2FtZSBhZGRyZXNzJywgW1xuICAgICAgICAgIHsgYWRkcmVzczogci5hZGRyZXNzLCBhbW91bnQ6IFN0cmluZyhyLmFtb3VudCkgfSxcbiAgICAgICAgXSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgcHJlYnVpbGRBZGRyZXNzID0gdHhQcmVidWlsZC5yZWNpcGllbnRzWzBdLmFkZHJlc3MudG9Mb3dlckNhc2UoKTtcbiAgICBjb25zdCB6YW1hQWNsQ29udHJhY3RBZGRyZXNzID0gKHRoaXMuZ2V0TmV0d29yaygpIGFzIEV0aExpa2VOZXR3b3JrKT8uemFtYUFjbENvbnRyYWN0QWRkcmVzcztcblxuICAgIC8vIEJhc2UtYWRkcmVzcyB3YWxsZXQ6IHR4UHJlYnVpbGQgdGFyZ2V0cyB0aGUgWmFtYSBBQ0wgY29udHJhY3QgZGlyZWN0bHkuXG4gICAgLy8gdHhQYXJhbXMgcmVjaXBpZW50cyBjYXJyeSB0aGUgd2FsbGV0IGJhc2UgYWRkcmVzcyDigJQgdGhlIGludGVudGlvbmFsIGFkZHJlc3MgbWlzbWF0Y2ggaXMgZXhwZWN0ZWQuXG4gICAgaWYgKHphbWFBY2xDb250cmFjdEFkZHJlc3MgJiYgcHJlYnVpbGRBZGRyZXNzID09PSB6YW1hQWNsQ29udHJhY3RBZGRyZXNzLnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBGb3J3YXJkZXIgd2FsbGV0OiB0eFByZWJ1aWxkIHRhcmdldHMgdGhlIGZvcndhcmRlciBhZGRyZXNzLCB3aGljaCBtdXN0IG1hdGNoIHR4UGFyYW1zIHJlY2lwaWVudHMuXG4gICAgaWYgKHByZWJ1aWxkQWRkcmVzcyA9PT0gZmlyc3RBZGRyZXNzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gTmVpdGhlciByZWNvZ25pc2VkIHNoYXBlIOKAlCB0eFByZWJ1aWxkIGFkZHJlc3MgaXMgbm90IHRoZSBBQ0wgY29udHJhY3QgYW5kIGRvZXNuJ3QgbWF0Y2ggdHhQYXJhbXNcbiAgICBhd2FpdCB0aHJvd1JlY2lwaWVudE1pc21hdGNoKFxuICAgICAgJ3Rva2VuIGVuYWJsZW1lbnQgdHhQcmVidWlsZCByZWNpcGllbnQgbXVzdCBiZSB0aGUgWmFtYSBBQ0wgY29udHJhY3QgKGJhc2UtYWRkcmVzcyB3YWxsZXQpIG9yIG1hdGNoIHR4UGFyYW1zIHJlY2lwaWVudHMgKGZvcndhcmRlciB3YWxsZXQpJyxcbiAgICAgIFt7IGFkZHJlc3M6IHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hZGRyZXNzLCBhbW91bnQ6IHR4UHJlYnVpbGQucmVjaXBpZW50c1swXS5hbW91bnQudG9TdHJpbmcoKSB9XVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogU2hhcmVkIHRhaWwgY2hlY2sgZm9yIHZlcmlmeSBwYXRoczogdGhlIHByZWJ1aWxkIG11c3QgYmUgZm9yIHRoZSBjb2luIHBlcmZvcm1pbmcgdGhlIHZlcmlmaWNhdGlvbi5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgdGhyb3dJZlByZWJ1aWxkQ29pbk1pc21hdGNoKFxuICAgIHR4UGFyYW1zOiBFdGhUcmFuc2FjdGlvblBhcmFtcyxcbiAgICB0eFByZWJ1aWxkOiBUcmFuc2FjdGlvblByZWJ1aWxkXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLnZlcmlmeUNvaW4odHhQcmVidWlsZCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgdHhFeHBsYW5hdGlvbiA9IGF3YWl0IHRoaXMuZ2V0VHhFeHBsYW5hdGlvbih0eFByZWJ1aWxkKTtcbiAgICB0aHJvdyBuZXcgVHhJbnRlbnRNaXNtYXRjaEVycm9yKFxuICAgICAgJ2NvaW4gaW4gdHhQcmVidWlsZCBkaWQgbm90IG1hdGNoIHRoYXQgaW4gdHhQYXJhbXMgc3VwcGxpZWQgYnkgY2xpZW50JyxcbiAgICAgIHVuZGVmaW5lZCxcbiAgICAgIFt0eFBhcmFtc10sXG4gICAgICB0eFByZWJ1aWxkPy50eEhleCxcbiAgICAgIHR4RXhwbGFuYXRpb25cbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFZlcmlmaWVzIGFuIEVSQy03OTg0IHdyYXBBcHByb3ZlIChzaGllbGQgYXBwcm92YWwpIHRyYW5zYWN0aW9uIGZvciBub24tVFNTIChvbmNoYWluIG11bHRpc2lnKSB3YWxsZXRzLlxuICAgKlxuICAgKiBNdWx0aXNpZyBzaGFwZSAoc2VydmVyLWJ1aWx0IHVuc2lnbmVkIHR4KTpcbiAgICogICB0eC50byAgID0gd2FsbGV0IGNvbnRyYWN0XG4gICAqICAgdHguZGF0YSA9IHNlbmRNdWx0aVNpZyh1bmRlcmx5aW5nRXJjMjAsIDAsIGFwcHJvdmUod3JhcHBlciwgYW1vdW50KSwgZXhwaXJlVGltZSwgc2VxdWVuY2VJZCwgc2lnbmF0dXJlKVxuICAgKiAgIHR4UHJlYnVpbGQucmVjaXBpZW50c1swXSA9IHsgYWRkcmVzczogdW5kZXJseWluZ0VyYzIwLCBhbW91bnQ6ICcwJywgZGF0YTogYXBwcm92ZSBjYWxsZGF0YSB9XG4gICAqXG4gICAqIENoZWNrczogdGhlIHNlbmRNdWx0aVNpZyBpbm5lciB0YXJnZXQgaXMgdGhlIHdyYXBwZXIgcGFpcidzIHVuZGVybHlpbmcgRVJDLTIwLCB0aGUgc2VuZE11bHRpU2lnXG4gICAqIHZhbHVlIGlzIDAsIHRoZSBpbm5lciBjYWxsZGF0YSBpcyBhcHByb3ZlKHdyYXBwZXIsIHdyYXBQYXJhbXMuYW1vdW50KSBleGFjdGx5LCBhbmQg4oCUIHdoZW4gdGhlXG4gICAqIHByZWJ1aWxkIGNhcnJpZXMgYnVpbGQgcmVjaXBpZW50cyDigJQgdGhhdCB0aGV5IG1pcnJvciB0aGUgc2VuZE11bHRpU2lnIGFyZ3MgZW5jb2RlZCBpbiB0eEhleFxuICAgKiAodGhlIGNsaWVudCBzaWducyB0aGUgb3BlcmF0aW9uIGhhc2ggb3ZlciB0aG9zZSByZWNpcGllbnRzKS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgdmVyaWZ5TXVsdGlzaWdXcmFwQXBwcm92ZVRyYW5zYWN0aW9uKFxuICAgIHBhcmFtczogVmVyaWZ5RXRoVHJhbnNhY3Rpb25PcHRpb25zLFxuICAgIHRocm93UmVjaXBpZW50TWlzbWF0Y2g6IChtZXNzYWdlOiBzdHJpbmcsIG1pc21hdGNoZWRSZWNpcGllbnRzOiBSZWNpcGllbnRbXSkgPT4gUHJvbWlzZTxuZXZlcj5cbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgeyB0eFBhcmFtcywgdHhQcmVidWlsZCB9ID0gcGFyYW1zO1xuICAgIGlmICghdHhQYXJhbXMpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWlzc2luZyBwYXJhbXMnKTtcbiAgICB9XG4gICAgY29uc3Qgd3JhcFBhcmFtcyA9IHR4UGFyYW1zLndyYXBQYXJhbXM7XG4gICAgaWYgKCF0eFByZWJ1aWxkPy50eEhleCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtaXNzaW5nIHR4SGV4IGluIHR4UHJlYnVpbGQnKTtcbiAgICB9XG4gICAgaWYgKCF3cmFwUGFyYW1zPy50b2tlbk5hbWUgfHwgIXdyYXBQYXJhbXMuYW1vdW50KSB7XG4gICAgICByZXR1cm4gdGhyb3dSZWNpcGllbnRNaXNtYXRjaCgnd3JhcEFwcHJvdmUgdHJhbnNhY3Rpb24gcmVxdWlyZXMgd3JhcFBhcmFtcyB3aXRoIHRva2VuTmFtZSBhbmQgYW1vdW50JywgW10pO1xuICAgIH1cblxuICAgIGxldCB3cmFwcGVyOiBFcmM3OTg0Q29pbjtcbiAgICBsZXQgdW5kZXJseWluZ0VyYzIwQWRkcmVzczogc3RyaW5nO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzdGF0aWNzQ29pbiA9IGNvaW5zLmdldCh3cmFwUGFyYW1zLnRva2VuTmFtZSk7XG4gICAgICBpZiAoIShzdGF0aWNzQ29pbiBpbnN0YW5jZW9mIEVyYzc5ODRDb2luKSB8fCAhc3RhdGljc0NvaW4udW5kZXJseWluZ0VyYzIwQWRkcmVzcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ25vdCBhbiBFUkMtNzk4NCBwYWlyJyk7XG4gICAgICB9XG4gICAgICB3cmFwcGVyID0gc3RhdGljc0NvaW47XG4gICAgICB1bmRlcmx5aW5nRXJjMjBBZGRyZXNzID0gc3RhdGljc0NvaW4udW5kZXJseWluZ0VyYzIwQWRkcmVzcy50b0xvd2VyQ2FzZSgpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIHRocm93UmVjaXBpZW50TWlzbWF0Y2goXG4gICAgICAgIGB3cmFwQXBwcm92ZTogJyR7d3JhcFBhcmFtcy50b2tlbk5hbWV9JyBpcyBub3QgYSByZWdpc3RlcmVkIEVSQy03OTg0IHNoaWVsZCBwYWlyYCxcbiAgICAgICAgW11cbiAgICAgICk7XG4gICAgfVxuXG4gICAgY29uc3QgdHhCdWlsZGVyID0gdGhpcy5nZXRUcmFuc2FjdGlvbkJ1aWxkZXIoKTtcbiAgICB0eEJ1aWxkZXIuZnJvbSh0eFByZWJ1aWxkLnR4SGV4KTtcbiAgICBjb25zdCB0eEpzb24gPSAoYXdhaXQgdHhCdWlsZGVyLmJ1aWxkKCkpLnRvSnNvbigpO1xuXG4gICAgbGV0IGlubmVyVG86IHN0cmluZztcbiAgICBsZXQgaW5uZXJBbW91bnQ6IHN0cmluZztcbiAgICBsZXQgaW5uZXJEYXRhOiBzdHJpbmc7XG4gICAgY29uc3QgZGF0YSA9IHR4SnNvbi5kYXRhIHx8ICcnO1xuICAgIGlmICghZGF0YS50b0xvd2VyQ2FzZSgpLnN0YXJ0c1dpdGgoc2VuZE11bHRpc2lnTWV0aG9kSWQudG9Mb3dlckNhc2UoKSkpIHtcbiAgICAgIHJldHVybiB0aHJvd1JlY2lwaWVudE1pc21hdGNoKFxuICAgICAgICAnd3JhcEFwcHJvdmUgbXVsdGlzaWcgdHJhbnNhY3Rpb24gbXVzdCBlbmNvZGUgdGhlIGFwcHJvdmUgY2FsbCBpbnNpZGUgc2VuZE11bHRpU2lnJyxcbiAgICAgICAgW11cbiAgICAgICk7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGRlY29kZWQgPSBkZWNvZGVOYXRpdmVUcmFuc2ZlckRhdGEoZGF0YSk7XG4gICAgICBpbm5lclRvID0gZGVjb2RlZC50bztcbiAgICAgIGlubmVyQW1vdW50ID0gZGVjb2RlZC5hbW91bnQ7XG4gICAgICBpbm5lckRhdGEgPSBkZWNvZGVkLmRhdGE7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgcmV0dXJuIHRocm93UmVjaXBpZW50TWlzbWF0Y2goXG4gICAgICAgIGB3cmFwQXBwcm92ZSBtdWx0aXNpZyB0cmFuc2FjdGlvbiBmYWlsZWQgdG8gZGVjb2RlIHNlbmRNdWx0aVNpZyBjYWxsZGF0YSDigJQgJHsoZSBhcyBFcnJvcikubWVzc2FnZX1gLFxuICAgICAgICBbXVxuICAgICAgKTtcbiAgICB9XG5cbiAgICBjb25zdCBtaXNtYXRjaCA9IChtZXNzYWdlOiBzdHJpbmcsIGFkZHJlc3MgPSBpbm5lclRvLCBhbW91bnQgPSBpbm5lckFtb3VudCk6IFByb21pc2U8bmV2ZXI+ID0+XG4gICAgICB0aHJvd1JlY2lwaWVudE1pc21hdGNoKG1lc3NhZ2UsIFt7IGFkZHJlc3M6IGFkZHJlc3MgfHwgJycsIGFtb3VudDogYW1vdW50IHx8ICcnIH1dKTtcblxuICAgIGlmIChpbm5lclRvLnRvTG93ZXJDYXNlKCkgIT09IHVuZGVybHlpbmdFcmMyMEFkZHJlc3MpIHtcbiAgICAgIGF3YWl0IG1pc21hdGNoKCd3cmFwQXBwcm92ZSB0cmFuc2FjdGlvbiBtdXN0IHRhcmdldCB0aGUgdW5kZXJseWluZyBFUkMtMjAgb2YgdGhlIHdyYXBwZXInKTtcbiAgICB9XG4gICAgaWYgKGlubmVyQW1vdW50ICE9PSAnMCcpIHtcbiAgICAgIGF3YWl0IG1pc21hdGNoKCd3cmFwQXBwcm92ZSB0cmFuc2FjdGlvbiBtdXN0IG5vdCBjYXJyeSBuYXRpdmUgdmFsdWUgaW5zaWRlIHNlbmRNdWx0aVNpZycpO1xuICAgIH1cblxuICAgIGxldCBhcHByb3ZlOiB7IHNwZW5kZXI6IHN0cmluZzsgYW1vdW50OiBzdHJpbmcgfTtcbiAgICB0cnkge1xuICAgICAgYXBwcm92ZSA9IGRlY29kZUFwcHJvdmVDYWxsZGF0YShpbm5lckRhdGEgfHwgJycpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHJldHVybiBtaXNtYXRjaChcbiAgICAgICAgYHdyYXBBcHByb3ZlIHRyYW5zYWN0aW9uIG11c3QgdXNlIEVSQy0yMCBhcHByb3ZlKGFkZHJlc3MsdWludDI1NikgY2FsbGRhdGEg4oCUICR7KGUgYXMgRXJyb3IpLm1lc3NhZ2V9YFxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKGFwcHJvdmUuc3BlbmRlci50b0xvd2VyQ2FzZSgpICE9PSB3cmFwcGVyLmNvbnRyYWN0QWRkcmVzcy50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICBhd2FpdCBtaXNtYXRjaCgnd3JhcEFwcHJvdmUgdHJhbnNhY3Rpb24gc3BlbmRlciBkb2VzIG5vdCBtYXRjaCB0aGUgd3JhcHBlciBjb250cmFjdCcsIGFwcHJvdmUuc3BlbmRlcik7XG4gICAgfVxuICAgIGlmIChhcHByb3ZlLmFtb3VudCAhPT0gd3JhcFBhcmFtcy5hbW91bnQpIHtcbiAgICAgIGF3YWl0IG1pc21hdGNoKCd3cmFwQXBwcm92ZSB0cmFuc2FjdGlvbiBhbW91bnQgZG9lcyBub3QgbWF0Y2ggdGhlIHJlcXVlc3RlZCBhbW91bnQnLCBpbm5lclRvLCBhcHByb3ZlLmFtb3VudCk7XG4gICAgfVxuXG4gICAgLy8gQ3Jvc3MtY2hlY2sgdGhlIGJ1aWxkIHJlY2lwaWVudHMgdGhlIGNsaWVudCBzaWducyB0aGUgb3BlcmF0aW9uIGhhc2ggYWdhaW5zdCDigJQgdGhleSBtdXN0XG4gICAgLy8gbWlycm9yIHRoZSBzZW5kTXVsdGlTaWcgYXJncyBlbmNvZGVkIGluIHR4SGV4LlxuICAgIGNvbnN0IHByZWJ1aWxkUmVjaXBpZW50ID0gdHhQcmVidWlsZC5yZWNpcGllbnRzPy5bMF07XG4gICAgaWYgKHByZWJ1aWxkUmVjaXBpZW50KSB7XG4gICAgICBjb25zdCByZWNpcGllbnREZXRhaWwgPSBbXG4gICAgICAgIHsgYWRkcmVzczogcHJlYnVpbGRSZWNpcGllbnQuYWRkcmVzcywgYW1vdW50OiBwcmVidWlsZFJlY2lwaWVudC5hbW91bnQ/LnRvU3RyaW5nKCkgPz8gJycgfSxcbiAgICAgIF07XG4gICAgICBpZiAocHJlYnVpbGRSZWNpcGllbnQuYWRkcmVzcz8udG9Mb3dlckNhc2UoKSAhPT0gaW5uZXJUby50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIGF3YWl0IHRocm93UmVjaXBpZW50TWlzbWF0Y2goXG4gICAgICAgICAgJ3dyYXBBcHByb3ZlIHByZWJ1aWxkIHJlY2lwaWVudCBhZGRyZXNzIGRvZXMgbm90IG1hdGNoIHRoZSBzZW5kTXVsdGlTaWcgdGFyZ2V0IGVuY29kZWQgaW4gdHhIZXgnLFxuICAgICAgICAgIHJlY2lwaWVudERldGFpbFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgbGV0IHJlY2lwaWVudERhdGE6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICAgIGlmICgnZGF0YScgaW4gcHJlYnVpbGRSZWNpcGllbnQgJiYgdHlwZW9mIHByZWJ1aWxkUmVjaXBpZW50LmRhdGEgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIHJlY2lwaWVudERhdGEgPSBwcmVidWlsZFJlY2lwaWVudC5kYXRhO1xuICAgICAgfVxuICAgICAgaWYgKHJlY2lwaWVudERhdGEgJiYgc3RyaXBIZXhQcmVmaXgocmVjaXBpZW50RGF0YSkudG9Mb3dlckNhc2UoKSAhPT0gc3RyaXBIZXhQcmVmaXgoaW5uZXJEYXRhKS50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIGF3YWl0IHRocm93UmVjaXBpZW50TWlzbWF0Y2goXG4gICAgICAgICAgJ3dyYXBBcHByb3ZlIHByZWJ1aWxkIHJlY2lwaWVudCBkYXRhIGRvZXMgbm90IG1hdGNoIHRoZSBzZW5kTXVsdGlTaWcgY2FsbGRhdGEgZW5jb2RlZCBpbiB0eEhleCcsXG4gICAgICAgICAgcmVjaXBpZW50RGV0YWlsXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBpc0VUSEFkZHJlc3MoYWRkcmVzczogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICEhYWRkcmVzcy5tYXRjaCgvMHhbYS1mQS1GMC05XXs0MH0vKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUcmFuc2Zvcm0gbWVzc2FnZSB0byBhY2NvbW1vZGF0ZSBzcGVjaWZpYyBibG9ja2NoYWluIHJlcXVpcmVtZW50cy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2UgLSB0aGUgbWVzc2FnZSB0byBwcmVwYXJlXG4gICAqIEByZXR1cm4ge3N0cmluZ30gdGhlIHByZXBhcmVkIG1lc3NhZ2UgYXMgYSBoZXggZW5jb2RlZCBzdHJpbmcuXG4gICAqL1xuICBlbmNvZGVNZXNzYWdlKG1lc3NhZ2U6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgcHJlZml4ID0gYFxcdTAwMTlFdGhlcmV1bSBTaWduZWQgTWVzc2FnZTpcXG4ke21lc3NhZ2UubGVuZ3RofWA7XG4gICAgcmV0dXJuIEJ1ZmZlci5mcm9tKHByZWZpeC5jb25jYXQobWVzc2FnZSkpLnRvU3RyaW5nKCdoZXgnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUcmFuc2Zvcm0gdGhlIFR5cGVkIGRhdGEgdG8gYWNjb21vZGF0ZSB0aGUgYmxvY2tjaGFpbiByZXF1aXJlbWVudHMgKEVJUC03MTIpXG4gICAqIEBwYXJhbSB7VHlwZWREYXRhfSB0eXBlZERhdGEgLSB0aGUgdHlwZWQgZGF0YSB0byBwcmVwYXJlXG4gICAqIEByZXR1cm4ge0J1ZmZlcn0gYSBidWZmZXIgb2YgdGhlIHJlc3VsdFxuICAgKi9cbiAgZW5jb2RlVHlwZWREYXRhKHR5cGVkRGF0YTogVHlwZWREYXRhKTogQnVmZmVyIHtcbiAgICBjb25zdCB2ZXJzaW9uID0gdHlwZWREYXRhLnZlcnNpb247XG4gICAgaWYgKHZlcnNpb24gPT09IFNpZ25UeXBlZERhdGFWZXJzaW9uLlYxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NpZ25UeXBlZERhdGEgdjEgaXMgbm90IHN1cHBvcnRlZCBkdWUgdG8gc2VjdXJpdHkgY29uY2VybnMnKTtcbiAgICB9XG4gICAgY29uc3QgdHlwZWREYXRhUmF3ID0gSlNPTi5wYXJzZSh0eXBlZERhdGEudHlwZWREYXRhUmF3KTtcbiAgICBjb25zdCBzYW5pdGl6ZWREYXRhID0gVHlwZWREYXRhVXRpbHMuc2FuaXRpemVEYXRhKHR5cGVkRGF0YVJhdyBhcyB1bmtub3duIGFzIFR5cGVkTWVzc2FnZTxhbnk+KTtcbiAgICBjb25zdCBwYXJ0czogQnVmZmVyW10gPSBbQnVmZmVyLmZyb20oJzE5MDEnLCAnaGV4JyldO1xuICAgIGNvbnN0IGVpcDcxMkRvbWFpbiA9ICdFSVA3MTJEb21haW4nO1xuICAgIHBhcnRzLnB1c2goVHlwZWREYXRhVXRpbHMuaGFzaFN0cnVjdChlaXA3MTJEb21haW4sIHNhbml0aXplZERhdGEuZG9tYWluLCBzYW5pdGl6ZWREYXRhLnR5cGVzLCB2ZXJzaW9uKSk7XG5cbiAgICBpZiAoc2FuaXRpemVkRGF0YS5wcmltYXJ5VHlwZSAhPT0gZWlwNzEyRG9tYWluKSB7XG4gICAgICBwYXJ0cy5wdXNoKFxuICAgICAgICBUeXBlZERhdGFVdGlscy5oYXNoU3RydWN0KFxuICAgICAgICAgIHNhbml0aXplZERhdGEucHJpbWFyeVR5cGUgYXMgc3RyaW5nLFxuICAgICAgICAgIHNhbml0aXplZERhdGEubWVzc2FnZSxcbiAgICAgICAgICBzYW5pdGl6ZWREYXRhLnR5cGVzLFxuICAgICAgICAgIHZlcnNpb25cbiAgICAgICAgKVxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIEJ1ZmZlci5jb25jYXQocGFydHMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkIHRoZSBkYXRhIHRvIHRyYW5zZmVyIGFuIEVSQy03MjEgb3IgRVJDLTExNTUgdG9rZW4gdG8gYW5vdGhlciBhZGRyZXNzXG4gICAqIEBwYXJhbSBwYXJhbXNcbiAgICovXG4gIGJ1aWxkTmZ0VHJhbnNmZXJEYXRhKHBhcmFtczogQnVpbGROZnRUcmFuc2ZlckRhdGFPcHRpb25zKTogc3RyaW5nIHtcbiAgICBjb25zdCB7IHRva2VuQ29udHJhY3RBZGRyZXNzLCByZWNpcGllbnRBZGRyZXNzLCBmcm9tQWRkcmVzcyB9ID0gcGFyYW1zO1xuICAgIHN3aXRjaCAocGFyYW1zLnR5cGUpIHtcbiAgICAgIGNhc2UgJ0VSQzcyMSc6IHtcbiAgICAgICAgY29uc3QgdG9rZW5JZCA9IHBhcmFtcy50b2tlbklkO1xuICAgICAgICBjb25zdCBjb250cmFjdERhdGEgPSBuZXcgRVJDNzIxVHJhbnNmZXJCdWlsZGVyKClcbiAgICAgICAgICAudG9rZW5Db250cmFjdEFkZHJlc3ModG9rZW5Db250cmFjdEFkZHJlc3MpXG4gICAgICAgICAgLnRvKHJlY2lwaWVudEFkZHJlc3MpXG4gICAgICAgICAgLmZyb20oZnJvbUFkZHJlc3MpXG4gICAgICAgICAgLnRva2VuSWQodG9rZW5JZClcbiAgICAgICAgICAuYnVpbGQoKTtcbiAgICAgICAgcmV0dXJuIGNvbnRyYWN0RGF0YTtcbiAgICAgIH1cblxuICAgICAgY2FzZSAnRVJDMTE1NSc6IHtcbiAgICAgICAgY29uc3QgZW50cmllcyA9IHBhcmFtcy5lbnRyaWVzO1xuICAgICAgICBjb25zdCB0cmFuc2ZlckJ1aWxkZXIgPSBuZXcgRVJDMTE1NVRyYW5zZmVyQnVpbGRlcigpXG4gICAgICAgICAgLnRva2VuQ29udHJhY3RBZGRyZXNzKHRva2VuQ29udHJhY3RBZGRyZXNzKVxuICAgICAgICAgIC50byhyZWNpcGllbnRBZGRyZXNzKVxuICAgICAgICAgIC5mcm9tKGZyb21BZGRyZXNzKTtcblxuICAgICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgICAgICB0cmFuc2ZlckJ1aWxkZXIuZW50cnkocGFyc2VJbnQoZW50cnkudG9rZW5JZCwgMTApLCBlbnRyeS5hbW91bnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRyYW5zZmVyQnVpbGRlci5idWlsZCgpO1xuICAgICAgfVxuXG4gICAgICBkZWZhdWx0OlxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFVuc3VwcG9ydGVkIE5GVCB0eXBlOiAke3BhcmFtcy50eXBlfWApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGZXRjaCB0aGUgZ2FzIHByaWNlIGZyb20gdGhlIGV4cGxvcmVyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB3cm9uZ0NoYWluQ29pbiAtIHRoZSBjb2luIHRoYXQgd2UncmUgZ2V0dGluZyBnYXMgcHJpY2UgZm9yXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcGlLZXkgLSBvcHRpb25hbCBBUEkga2V5IHRvIHVzZSBpbnN0ZWFkIG9mIHRoZSBvbmUgZnJvbSB0aGUgZW52aXJvbm1lbnRcbiAgICovXG4gIGFzeW5jIGdldEdhc1ByaWNlRnJvbUV4dGVybmFsQVBJKHdyb25nQ2hhaW5Db2luOiBzdHJpbmcsIGFwaUtleT86IHN0cmluZyk6IFByb21pc2U8Qk4+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzID0gYXdhaXQgdGhpcy5yZWNvdmVyeUJsb2NrY2hhaW5FeHBsb3JlclF1ZXJ5KFxuICAgICAgICB7XG4gICAgICAgICAgY2hhaW5pZDogdGhpcy5nZXRDaGFpbklkKCkudG9TdHJpbmcoKSxcbiAgICAgICAgICBtb2R1bGU6ICdwcm94eScsXG4gICAgICAgICAgYWN0aW9uOiAnZXRoX2dhc1ByaWNlJyxcbiAgICAgICAgfSxcbiAgICAgICAgYXBpS2V5XG4gICAgICApO1xuICAgICAgY29uc3QgZ2FzUHJpY2UgPSBuZXcgQk4ocmVzLnJlc3VsdC5zbGljZSgyKSwgMTYpO1xuICAgICAgY29uc29sZS5sb2coYCBHb3QgZ2FzIHByaWNlOiAke2dhc1ByaWNlfWApO1xuICAgICAgcmV0dXJuIGdhc1ByaWNlO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGlmIChlIGluc3RhbmNlb2YgUmVjb3ZlcnlQcm92aWRlckVycm9yKSB7XG4gICAgICAgIHRocm93IGU7XG4gICAgICB9XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgZ2FzIHByaWNlLiBQbGVhc2UgbWFrZSBzdXJlIHRvIHVzZSB0aGUgYXBpIGtleSBvZiAke3dyb25nQ2hhaW5Db2lufWApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGZXRjaCB0aGUgZ2FzIGxpbWl0IGZyb20gdGhlIGV4cGxvcmVyXG4gICAqIEBwYXJhbSBpbnRlbmRlZENoYWluXG4gICAqIEBwYXJhbSBmcm9tXG4gICAqIEBwYXJhbSB0b1xuICAgKiBAcGFyYW0gZGF0YVxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXBpS2V5IC0gb3B0aW9uYWwgQVBJIGtleSB0byB1c2UgaW5zdGVhZCBvZiB0aGUgb25lIGZyb20gdGhlIGVudmlyb25tZW50XG4gICAqL1xuICBhc3luYyBnZXRHYXNMaW1pdEZyb21FeHRlcm5hbEFQSShcbiAgICBpbnRlbmRlZENoYWluOiBzdHJpbmcsXG4gICAgZnJvbTogc3RyaW5nLFxuICAgIHRvOiBzdHJpbmcsXG4gICAgZGF0YTogc3RyaW5nLFxuICAgIGFwaUtleT86IHN0cmluZ1xuICApOiBQcm9taXNlPEJOPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IHRoaXMucmVjb3ZlcnlCbG9ja2NoYWluRXhwbG9yZXJRdWVyeShcbiAgICAgICAge1xuICAgICAgICAgIGNoYWluaWQ6IHRoaXMuZ2V0Q2hhaW5JZCgpLnRvU3RyaW5nKCksXG4gICAgICAgICAgbW9kdWxlOiAncHJveHknLFxuICAgICAgICAgIGFjdGlvbjogJ2V0aF9lc3RpbWF0ZUdhcycsXG4gICAgICAgICAgZnJvbSxcbiAgICAgICAgICB0byxcbiAgICAgICAgICBkYXRhLFxuICAgICAgICB9LFxuICAgICAgICBhcGlLZXlcbiAgICAgICk7XG4gICAgICBjb25zdCBnYXNMaW1pdCA9IG5ldyBCTihyZXMucmVzdWx0LnNsaWNlKDIpLCAxNik7XG4gICAgICBjb25zb2xlLmxvZyhgR290IGdhcyBsaW1pdDogJHtnYXNMaW1pdH1gKTtcbiAgICAgIHJldHVybiBnYXNMaW1pdDtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBpZiAoZSBpbnN0YW5jZW9mIFJlY292ZXJ5UHJvdmlkZXJFcnJvcikge1xuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgRmFpbGVkIHRvIGdldCBnYXMgbGltaXQuIFBsZWFzZSBtYWtlIHN1cmUgdG8gdXNlIHRoZSBwcml2YXRlS2V5IGFrYSB1c2VyS2V5IG9mICR7aW50ZW5kZWRDaGFpbn0gd2FsbGV0ICR7dG99YFxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBiYWxhbmNlIG9mIGJpdGdvRmVlQWRkcmVzcyB0byBlbnN1cmUgZnVuZHMgYXJlIGF2YWlsYWJsZSB0byBwYXkgZmVlc1xuICAgKiBAcGFyYW0gYml0Z29GZWVBZGRyZXNzXG4gICAqIEBwYXJhbSBnYXNQcmljZVxuICAgKiBAcGFyYW0gZ2FzTGltaXRcbiAgICogQHBhcmFtIGFwaUtleSAtIG9wdGlvbmFsIEFQSSBrZXkgdG8gdXNlIGluc3RlYWQgb2YgdGhlIG9uZSBmcm9tIHRoZSBlbnZpcm9ubWVudFxuICAgKi9cbiAgYXN5bmMgZW5zdXJlU3VmZmljaWVudEJhbGFuY2UoYml0Z29GZWVBZGRyZXNzOiBzdHJpbmcsIGdhc1ByaWNlOiBCTiwgZ2FzTGltaXQ6IEJOLCBhcGlLZXk/OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBiaXRnb0ZlZUFkZHJlc3NCYWxhbmNlID0gYXdhaXQgdGhpcy5xdWVyeUFkZHJlc3NCYWxhbmNlKGJpdGdvRmVlQWRkcmVzcywgYXBpS2V5KTtcbiAgICBjb25zdCB0b3RhbEdhc05lZWRlZCA9IE51bWJlcihnYXNQcmljZS5tdWwoZ2FzTGltaXQpKTtcbiAgICBjb25zdCB3ZWlUb0d3ZWkgPSAxMCAqKiA5O1xuICAgIGlmIChiaXRnb0ZlZUFkZHJlc3NCYWxhbmNlLmx0KHRvdGFsR2FzTmVlZGVkKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgRmVlIGFkZHJlc3MgJHtiaXRnb0ZlZUFkZHJlc3N9IGhhcyBiYWxhbmNlICR7KGJpdGdvRmVlQWRkcmVzc0JhbGFuY2UgLyB3ZWlUb0d3ZWkpLnRvU3RyaW5nKCl9IEd3ZWkuYCArXG4gICAgICAgICAgYFRoaXMgYWRkcmVzcyBtdXN0IGhhdmUgYSBiYWxhbmNlIG9mIGF0IGxlYXN0ICR7KHRvdGFsR2FzTmVlZGVkIC8gd2VpVG9Hd2VpKS50b1N0cmluZygpfWAgK1xuICAgICAgICAgIGAgR3dlaSB0byBwZXJmb3JtIHJlY292ZXJpZXMuIFRyeSBzZW5kaW5nIHNvbWUgJHt0aGlzLmdldENoYWluKCl9IHRvIHRoaXMgYWRkcmVzcyB0aGVuIHJldHJ5LmBcbiAgICAgICk7XG4gICAgfVxuICB9XG59XG4iXX0=
|