@atomicfinance/bitcoin-wallet-provider 2.5.0 → 3.0.1

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.
@@ -1,412 +1,500 @@
1
- Object.defineProperty(exports, "__esModule", {
2
- value: true
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
3
13
  });
4
- exports.default = void 0;
5
- var _provider = _interopRequireDefault(require("@atomicfinance/provider"));
6
- var _types = require("@atomicfinance/types");
7
- var _bitcoinUtils = require("@liquality/bitcoin-utils");
8
- var _assert = _interopRequireDefault(require("assert"));
9
- var bitcoin = _interopRequireWildcard(require("bitcoinjs-lib"));
10
- var _secp256K1 = _interopRequireDefault(require("secp256k1"));
11
- function _interopRequireDefault(obj) {
12
- return obj && obj.__esModule ? obj : {
13
- default: obj
14
- };
15
- }
16
- function _interopRequireWildcard(obj) {
17
- if (obj && obj.__esModule) {
18
- return obj;
19
- } else {
20
- var newObj = {};
21
- if (obj != null) {
22
- for(var key in obj){
23
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
24
- var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {};
25
- if (desc.get || desc.set) {
26
- Object.defineProperty(newObj, key, desc);
27
- } else {
28
- newObj[key] = obj[key];
29
- }
30
- }
31
- }
32
- }
33
- newObj.default = obj;
34
- return newObj;
35
- }
36
- }
37
- const FEE_PER_BYTE_FALLBACK = 5;
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ var __importDefault = (this && this.__importDefault) || function (mod) {
22
+ return (mod && mod.__esModule) ? mod : { "default": mod };
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.AddressSearchType = void 0;
26
+ const bitcoin_utils_1 = require("@atomicfinance/bitcoin-utils");
27
+ const errors_1 = require("@atomicfinance/errors");
28
+ const types_1 = require("@atomicfinance/types");
29
+ const utils_1 = require("@atomicfinance/utils");
30
+ const bitcoin = __importStar(require("bitcoinjs-lib"));
31
+ const memoizee_1 = __importDefault(require("memoizee"));
38
32
  const ADDRESS_GAP = 20;
39
33
  const NONCHANGE_ADDRESS = 0;
40
34
  const CHANGE_ADDRESS = 1;
41
35
  const NONCHANGE_OR_CHANGE_ADDRESS = 2;
42
- let BitcoinWalletProvider = class BitcoinWalletProvider extends _provider.default {
43
- async buildSweepTransactionWithSetOutputs(externalChangeAddress, feePerByte, _outputs, fixedInputs) {
44
- return this._buildSweepTransaction(externalChangeAddress, feePerByte, _outputs, fixedInputs);
45
- }
46
- getUnusedAddressesBlacklist() {
47
- return this._unusedAddressesBlacklist;
48
- }
49
- setUnusedAddressesBlacklist(unusedAddressesBlacklist) {
50
- this._unusedAddressesBlacklist = unusedAddressesBlacklist;
51
- }
52
- setMaxAddressesToDerive(maxAddressesToDerive) {
53
- this._maxAddressesToDerive = maxAddressesToDerive;
54
- }
55
- getMaxAddressesToDerive() {
56
- return this._maxAddressesToDerive;
57
- }
58
- _createMultisigPayment(m, pubkeys) {
59
- if (m > pubkeys.length) {
60
- throw new Error(`not enough keys supplied (got ${pubkeys.length} keys, but need at least ${m} to redeem)`);
36
+ var AddressSearchType;
37
+ (function (AddressSearchType) {
38
+ AddressSearchType[AddressSearchType["EXTERNAL"] = 0] = "EXTERNAL";
39
+ AddressSearchType[AddressSearchType["CHANGE"] = 1] = "CHANGE";
40
+ AddressSearchType[AddressSearchType["EXTERNAL_OR_CHANGE"] = 2] = "EXTERNAL_OR_CHANGE";
41
+ })(AddressSearchType = exports.AddressSearchType || (exports.AddressSearchType = {}));
42
+ exports.default = (superclass) => {
43
+ class BitcoinWalletProvider extends superclass {
44
+ constructor(...args) {
45
+ const options = args[0];
46
+ const { network, baseDerivationPath, addressType = types_1.bitcoin.AddressType.BECH32, } = options;
47
+ const addressTypes = Object.values(types_1.bitcoin.AddressType);
48
+ if (!addressTypes.includes(addressType)) {
49
+ throw new Error(`addressType must be one of ${addressTypes.join(',')}`);
50
+ }
51
+ super(options);
52
+ this._baseDerivationPath = baseDerivationPath;
53
+ this._network = network;
54
+ this._addressType = addressType;
55
+ this._derivationCache = {};
56
+ this._unusedAddressesBlacklist = {};
57
+ this._maxAddressesToDerive = 5000;
61
58
  }
62
- // Create m-of-n multisig
63
- const p2ms = bitcoin.payments.p2ms({
64
- m: m,
65
- pubkeys: pubkeys.map((key)=>Buffer.from(key, 'hex')
66
- ),
67
- network: this._network
68
- });
69
- // Create p2wsh for multisig
70
- const p2wsh = bitcoin.payments.p2wsh({
71
- redeem: p2ms,
72
- network: this._network
73
- });
74
- return p2wsh;
75
- }
76
- /**
77
- * Creates a native-segwit multi-signature address (P2MS in P2WSH) with n signatures of m required keys
78
- * https://developer.bitcoin.org/reference/rpc/createmultisig.html
79
- * @param m the number of required signatures
80
- * @param pubkeys n possible pubkeys in total
81
- * @returns a json object containing the `address` and `redeemScript`
82
- */ createMultisig(m, pubkeys) {
83
- const p2wsh = this._createMultisigPayment(m, pubkeys);
84
- return {
85
- address: p2wsh.address,
86
- redeemScript: p2wsh.redeem?.output?.toString('hex')
87
- };
88
- }
89
- /**
90
- * Creates a PSBT of a native-segwit multi-signature address (P2MS in P2WSH) with n signatures of m required keys
91
- * https://developer.bitcoin.org/reference/rpc/createmultisig.html
92
- * https://developer.bitcoin.org/reference/rpc/createpsbt.html
93
- * @param m the number of required signatures
94
- * @param pubkeys n possible pubkeys in total
95
- * @param inputs the Inputs to the PSBT
96
- * @param ouputs the Outputs to the PSBT
97
- * @returns a base64 encoded psbt string
98
- */ buildMultisigPSBT(m, pubkeys, inputs, outputs) {
99
- (0, _assert).default(inputs.length > 0, 'no inputs found');
100
- (0, _assert).default(outputs.length > 0, 'no outputs found');
101
- const p2wsh = this._createMultisigPayment(m, pubkeys);
102
- // Verify pubkeyhash for all inputs matches the p2wsh hash
103
- (0, _assert).default(inputs.every((input)=>p2wsh.output.toString('hex') === input.scriptPubKey
104
- ), 'address pubkeyhash does not match input scriptPubKey');
105
- // creator
106
- const psbt = new bitcoin.Psbt({
107
- network: this._network
108
- });
109
- // updater
110
- inputs.forEach((input)=>{
111
- psbt.addInput({
112
- hash: input.txid,
113
- index: input.vout,
114
- witnessUtxo: {
115
- script: p2wsh.output,
116
- value: input.value
117
- },
118
- witnessScript: p2wsh.redeem.output
119
- });
120
- });
121
- outputs.forEach((output)=>{
122
- psbt.addOutput({
123
- address: output.to,
124
- value: output.value
125
- });
126
- });
127
- return psbt.toBase64();
128
- }
129
- /**
130
- * Update a PSBT with input information from our wallet and then sign inputs that we can sign for
131
- * https://developer.bitcoin.org/reference/rpc/walletprocesspsbt.html
132
- * @param psbt a base64 encoded psbt string (P2WSH only)
133
- * @returns a base64 encoded signed psbt string
134
- */ async walletProcessPSBT(psbtString) {
135
- const psbt = bitcoin.Psbt.fromBase64(psbtString);
136
- await Promise.all(psbt.data.inputs.map(async (input, i)=>{
137
- (0, _assert).default(psbt.getInputType(i).slice(0, 5) === 'p2wsh', 'only accepts P2WSH inputs');
138
- const scriptStack = bitcoin.script.decompile(input.witnessScript);
139
- const pubkeys = scriptStack.filter((data)=>Buffer.isBuffer(data) && _secp256K1.default.publicKeyVerify(data)
140
- );
141
- await Promise.all(pubkeys.map(async (key)=>{
142
- // create address using pubkey
143
- const { address: addressString } = bitcoin.payments.p2wpkh({
144
- pubkey: key,
145
- network: this._network
146
- });
147
- // Retrieve address object from wallet using address
148
- const address = await this.quickFindAddress([
149
- addressString,
150
- ]);
151
- // exit if address doesn't exist in wallet
152
- if (!address) return;
153
- // derive keypair
154
- const keyPair = await this.getMethod('keyPair')(address.derivationPath);
155
- // sign PSBT using keypair
156
- psbt.signInput(i, keyPair);
157
- }));
158
- }));
159
- psbt.validateSignaturesOfAllInputs(); // ensure all signatures are valid!
160
- return psbt.toBase64();
161
- }
162
- /**
163
- * Finalize the inputs of a PSBT. If the transaction is fully signed, it will
164
- * produce a network serialized transaction which can be broadcast with sendrawtransaction
165
- * https://developer.bitcoin.org/reference/rpc/finalizepsbt.html
166
- * @param psbt a base64 encoded psbt string
167
- * @returns a json object containing `psbt` in base64, `hex` for transaction and `complete` for if
168
- * the transaction has a complete set of signatures
169
- */ finalizePSBT(psbtString) {
170
- const psbt = bitcoin.Psbt.fromBase64(psbtString);
171
- try {
172
- psbt.validateSignaturesOfAllInputs(); // ensure all signatures are valid!
173
- psbt.finalizeAllInputs();
174
- } catch (error) {
175
- return {
176
- psbt: psbt.toBase64(),
177
- complete: false
178
- };
59
+ getDerivationCache() {
60
+ return this._derivationCache;
179
61
  }
180
- return {
181
- psbt: psbt.toBase64(),
182
- hex: psbt.extractTransaction().toHex(),
183
- complete: true
184
- };
185
- }
186
- async getUnusedAddress(change = false, numAddressPerCall = 100) {
187
- const addressType = change ? CHANGE_ADDRESS : NONCHANGE_ADDRESS;
188
- const key = change ? 'change' : 'nonChange';
189
- const address = await this._getUsedUnusedAddresses(numAddressPerCall, addressType).then(({ unusedAddress })=>unusedAddress[key]
190
- );
191
- this._unusedAddressesBlacklist[address.address] = true;
192
- return address;
193
- }
194
- async _getUsedUnusedAddresses(numAddressPerCall = 100, addressType) {
195
- const usedAddresses = [];
196
- const addressCountMap = {
197
- change: 0,
198
- nonChange: 0
199
- };
200
- const unusedAddressMap = {
201
- change: null,
202
- nonChange: null
203
- };
204
- let addrList;
205
- let addressIndex = 0;
206
- let changeAddresses = [];
207
- let nonChangeAddresses = [];
208
- /* eslint-disable no-unmodified-loop-condition */ while(addressType === NONCHANGE_OR_CHANGE_ADDRESS && (addressCountMap.change < ADDRESS_GAP || addressCountMap.nonChange < ADDRESS_GAP) || addressType === NONCHANGE_ADDRESS && addressCountMap.nonChange < ADDRESS_GAP || addressType === CHANGE_ADDRESS && addressCountMap.change < ADDRESS_GAP){
209
- /* eslint-enable no-unmodified-loop-condition */ addrList = [];
210
- if ((addressType === NONCHANGE_OR_CHANGE_ADDRESS || addressType === CHANGE_ADDRESS) && addressCountMap.change < ADDRESS_GAP) {
211
- // Scanning for change addr
212
- changeAddresses = await this.client.wallet.getAddresses(addressIndex, numAddressPerCall, true);
213
- addrList = addrList.concat(changeAddresses);
214
- } else {
215
- changeAddresses = [];
62
+ async setDerivationCache(derivationCache) {
63
+ const address = await this.getDerivationPathAddress(Object.keys(derivationCache)[0]);
64
+ if (derivationCache[address.derivationPath].address !== address.address) {
65
+ throw new Error(`derivationCache at ${address.derivationPath} does not match`);
216
66
  }
217
- if ((addressType === NONCHANGE_OR_CHANGE_ADDRESS || addressType === NONCHANGE_ADDRESS) && addressCountMap.nonChange < ADDRESS_GAP) {
218
- // Scanning for non change addr
219
- nonChangeAddresses = await this.quickGetAddresses(addressIndex, numAddressPerCall, false);
220
- addrList = addrList.concat(nonChangeAddresses);
67
+ this._derivationCache = derivationCache;
68
+ }
69
+ sendOptionsToOutputs(transactions) {
70
+ const targets = [];
71
+ transactions.forEach((tx) => {
72
+ if (tx.to && tx.value && tx.value.gt(0)) {
73
+ targets.push({
74
+ address: utils_1.addressToString(tx.to),
75
+ value: tx.value.toNumber(),
76
+ });
77
+ }
78
+ if (tx.data) {
79
+ const scriptBuffer = bitcoin.script.compile([
80
+ bitcoin.script.OPS.OP_RETURN,
81
+ Buffer.from(tx.data, 'hex'),
82
+ ]);
83
+ targets.push({
84
+ value: 0,
85
+ script: scriptBuffer,
86
+ });
87
+ }
88
+ });
89
+ return targets;
90
+ }
91
+ async buildTransaction(output, feePerByte) {
92
+ return this._buildTransaction([output], feePerByte);
93
+ }
94
+ async buildBatchTransaction(outputs) {
95
+ return this._buildTransaction(outputs);
96
+ }
97
+ async _sendTransaction(transactions, feePerByte) {
98
+ const { hex, fee } = await this._buildTransaction(transactions, feePerByte);
99
+ await this.getMethod('sendRawTransaction')(hex);
100
+ return bitcoin_utils_1.normalizeTransactionObject(bitcoin_utils_1.decodeRawTransaction(hex, this._network), fee);
101
+ }
102
+ async sendTransaction(options) {
103
+ return this._sendTransaction(this.sendOptionsToOutputs([options]), options.fee);
104
+ }
105
+ async sendBatchTransaction(transactions) {
106
+ return this._sendTransaction(this.sendOptionsToOutputs(transactions));
107
+ }
108
+ async buildSweepTransaction(externalChangeAddress, feePerByte) {
109
+ return this._buildSweepTransaction(externalChangeAddress, feePerByte);
110
+ }
111
+ async sendSweepTransaction(externalChangeAddress, feePerByte) {
112
+ const { hex, fee } = await this._buildSweepTransaction(utils_1.addressToString(externalChangeAddress), feePerByte);
113
+ await this.getMethod('sendRawTransaction')(hex);
114
+ return bitcoin_utils_1.normalizeTransactionObject(bitcoin_utils_1.decodeRawTransaction(hex, this._network), fee);
115
+ }
116
+ getUnusedAddressesBlacklist() {
117
+ return this._unusedAddressesBlacklist;
118
+ }
119
+ setUnusedAddressesBlacklist(unusedAddressesBlacklist) {
120
+ this._unusedAddressesBlacklist = unusedAddressesBlacklist;
121
+ }
122
+ setMaxAddressesToDerive(maxAddressesToDerive) {
123
+ this._maxAddressesToDerive = maxAddressesToDerive;
124
+ }
125
+ getMaxAddressesToDerive() {
126
+ return this._maxAddressesToDerive;
127
+ }
128
+ async updateTransactionFee(tx, newFeePerByte) {
129
+ const txHash = typeof tx === 'string' ? tx : tx.hash;
130
+ const transaction = (await this.getMethod('getTransactionByHash')(txHash))._raw;
131
+ const fixedInputs = [transaction.vin[0]]; // TODO: should this pick more than 1 input? RBF doesn't mandate it
132
+ const lookupAddresses = transaction.vout.map((vout) => vout.scriptPubKey.addresses[0]);
133
+ const changeAddress = await this.findAddress(lookupAddresses, true);
134
+ const changeOutput = transaction.vout.find((vout) => vout.scriptPubKey.addresses[0] === changeAddress.address);
135
+ let outputs = transaction.vout;
136
+ if (changeOutput) {
137
+ outputs = outputs.filter((vout) => vout.scriptPubKey.addresses[0] !==
138
+ changeOutput.scriptPubKey.addresses[0]);
221
139
  }
222
- const transactionCounts = await this.getMethod('getAddressTransactionCounts')(addrList);
223
- for (const address of addrList){
224
- const isUsed = transactionCounts[address.address] > 0 || this._unusedAddressesBlacklist[address.address];
225
- const isChangeAddress = changeAddresses.find((a)=>address.address === a.address
226
- );
227
- const key = isChangeAddress ? 'change' : 'nonChange';
228
- if (isUsed) {
229
- usedAddresses.push(address);
230
- addressCountMap[key] = 0;
231
- unusedAddressMap[key] = null;
232
- } else {
233
- addressCountMap[key]++;
234
- if (!unusedAddressMap[key]) {
235
- unusedAddressMap[key] = address;
140
+ // TODO more checks?
141
+ const transactions = outputs.map((output) => ({
142
+ address: output.scriptPubKey.addresses[0],
143
+ value: new types_1.BigNumber(output.value).times(1e8).toNumber(),
144
+ }));
145
+ const { hex, fee } = await this._buildTransaction(transactions, newFeePerByte, fixedInputs);
146
+ await this.getMethod('sendRawTransaction')(hex);
147
+ return bitcoin_utils_1.normalizeTransactionObject(bitcoin_utils_1.decodeRawTransaction(hex, this._network), fee);
148
+ }
149
+ async getUnusedAddress(change = false, numAddressPerCall = 100) {
150
+ const addressType = change ? CHANGE_ADDRESS : NONCHANGE_ADDRESS;
151
+ const key = change ? 'change' : 'nonChange';
152
+ const address = await this._getUsedUnusedAddresses(numAddressPerCall, addressType).then(({ unusedAddress }) => unusedAddress[key]);
153
+ this._unusedAddressesBlacklist[address.address] = true;
154
+ return address;
155
+ }
156
+ async _getUsedUnusedAddresses(numAddressPerCall = 100, addressType) {
157
+ const usedAddresses = [];
158
+ const addressCountMap = { change: 0, nonChange: 0 };
159
+ const unusedAddressMap = { change: null, nonChange: null };
160
+ let addrList;
161
+ let addressIndex = 0;
162
+ let changeAddresses = [];
163
+ let nonChangeAddresses = [];
164
+ /* eslint-disable no-unmodified-loop-condition */
165
+ while ((addressType === NONCHANGE_OR_CHANGE_ADDRESS &&
166
+ (addressCountMap.change < ADDRESS_GAP ||
167
+ addressCountMap.nonChange < ADDRESS_GAP)) ||
168
+ (addressType === NONCHANGE_ADDRESS &&
169
+ addressCountMap.nonChange < ADDRESS_GAP) ||
170
+ (addressType === CHANGE_ADDRESS && addressCountMap.change < ADDRESS_GAP)) {
171
+ /* eslint-enable no-unmodified-loop-condition */
172
+ addrList = [];
173
+ if ((addressType === NONCHANGE_OR_CHANGE_ADDRESS ||
174
+ addressType === CHANGE_ADDRESS) &&
175
+ addressCountMap.change < ADDRESS_GAP) {
176
+ // Scanning for change addr
177
+ changeAddresses = await this.client.wallet.getAddresses(addressIndex, numAddressPerCall, true);
178
+ addrList = addrList.concat(changeAddresses);
179
+ }
180
+ else {
181
+ changeAddresses = [];
182
+ }
183
+ if ((addressType === NONCHANGE_OR_CHANGE_ADDRESS ||
184
+ addressType === NONCHANGE_ADDRESS) &&
185
+ addressCountMap.nonChange < ADDRESS_GAP) {
186
+ // Scanning for non change addr
187
+ nonChangeAddresses = await this.getAddresses(addressIndex, numAddressPerCall, false);
188
+ addrList = addrList.concat(nonChangeAddresses);
189
+ }
190
+ const transactionCounts = await this.getMethod('getAddressTransactionCounts')(addrList);
191
+ for (const address of addrList) {
192
+ const isUsed = transactionCounts[address.address] > 0 ||
193
+ this._unusedAddressesBlacklist[address.address];
194
+ const isChangeAddress = changeAddresses.find((a) => address.address === a.address);
195
+ const key = isChangeAddress ? 'change' : 'nonChange';
196
+ if (isUsed) {
197
+ usedAddresses.push(address);
198
+ addressCountMap[key] = 0;
199
+ unusedAddressMap[key] = null;
200
+ }
201
+ else {
202
+ addressCountMap[key]++;
203
+ if (!unusedAddressMap[key]) {
204
+ unusedAddressMap[key] = address;
205
+ }
236
206
  }
237
207
  }
208
+ addressIndex += numAddressPerCall;
238
209
  }
239
- addressIndex += numAddressPerCall;
210
+ let firstUnusedAddress;
211
+ const indexNonChange = unusedAddressMap.nonChange
212
+ ? unusedAddressMap.nonChange.index
213
+ : Infinity;
214
+ const indexChange = unusedAddressMap.change
215
+ ? unusedAddressMap.change.index
216
+ : Infinity;
217
+ if (indexNonChange <= indexChange)
218
+ firstUnusedAddress = unusedAddressMap.nonChange;
219
+ else
220
+ firstUnusedAddress = unusedAddressMap.change;
221
+ return {
222
+ usedAddresses,
223
+ unusedAddress: unusedAddressMap,
224
+ firstUnusedAddress,
225
+ };
240
226
  }
241
- let firstUnusedAddress;
242
- const indexNonChange = unusedAddressMap.nonChange ? unusedAddressMap.nonChange.index : Infinity;
243
- const indexChange = unusedAddressMap.change ? unusedAddressMap.change.index : Infinity;
244
- if (indexNonChange <= indexChange) firstUnusedAddress = unusedAddressMap.nonChange;
245
- else firstUnusedAddress = unusedAddressMap.change;
246
- return {
247
- usedAddresses,
248
- unusedAddress: unusedAddressMap,
249
- firstUnusedAddress
250
- };
251
- }
252
- async sendSweepTransactionWithSetOutputs(externalChangeAddress, feePerByte, _outputs, fixedInputs) {
253
- const { hex , fee } = await this._buildSweepTransaction(externalChangeAddress, feePerByte, _outputs, fixedInputs);
254
- await this.getMethod('sendRawTransaction')(hex);
255
- return (0, _bitcoinUtils).normalizeTransactionObject((0, _bitcoinUtils).decodeRawTransaction(hex, this._network), fee);
256
- }
257
- async _buildSweepTransaction(externalChangeAddress, feePerByte, _outputs = [], fixedInputs) {
258
- const _feePerByte = feePerByte || await this.getMethod('getFeePerByte')() || FEE_PER_BYTE_FALLBACK;
259
- const inputs = [];
260
- const outputs = [];
261
- try {
262
- const inputsForAmount = await this.getMethod('getInputsForAmount')(_outputs, _feePerByte, fixedInputs, 100, true);
263
- if (inputsForAmount.change) {
264
- throw Error('There should not be any change for sweeping transaction');
227
+ async getWalletAddress(address) {
228
+ const foundAddress = await this.findAddress([address]);
229
+ if (foundAddress)
230
+ return foundAddress;
231
+ throw new Error('Wallet does not contain address');
232
+ }
233
+ getAddressFromPublicKey(publicKey) {
234
+ return this.getPaymentVariantFromPublicKey(publicKey).address;
235
+ }
236
+ getPaymentVariantFromPublicKey(publicKey) {
237
+ if (this._addressType === types_1.bitcoin.AddressType.LEGACY) {
238
+ return bitcoin.payments.p2pkh({
239
+ pubkey: publicKey,
240
+ network: this._network,
241
+ });
242
+ }
243
+ else if (this._addressType === types_1.bitcoin.AddressType.P2SH_SEGWIT) {
244
+ return bitcoin.payments.p2sh({
245
+ redeem: bitcoin.payments.p2wpkh({
246
+ pubkey: publicKey,
247
+ network: this._network,
248
+ }),
249
+ network: this._network,
250
+ });
265
251
  }
266
- inputs.push(...inputsForAmount.inputs || []);
267
- outputs.push(...inputsForAmount.outputs || []);
268
- } catch (e) {
269
- if (fixedInputs.length === 0) {
270
- throw Error(`Inputs for amount doesn't exist and no fixedInputs provided`);
252
+ else if (this._addressType === types_1.bitcoin.AddressType.BECH32) {
253
+ return bitcoin.payments.p2wpkh({
254
+ pubkey: publicKey,
255
+ network: this._network,
256
+ });
271
257
  }
272
- const inputsForAmount = await this._getInputForAmountWithoutUtxoCheck(_outputs, _feePerByte, fixedInputs);
273
- inputs.push(...inputsForAmount.inputs.map((utxo)=>_types.Input.fromUTXO(utxo)
274
- ) || []);
275
- outputs.push(...inputsForAmount.outputs || []);
276
258
  }
277
- _outputs.forEach((output)=>{
278
- const spliceIndex = outputs.findIndex((sweepOutput)=>output.value === sweepOutput.value
279
- );
280
- outputs.splice(spliceIndex, 1);
281
- });
282
- _outputs.push({
283
- to: externalChangeAddress,
284
- value: outputs[0].value
285
- });
286
- return this._buildTransactionWithoutUtxoCheck(_outputs, _feePerByte, inputs);
287
- }
288
- _getInputForAmountWithoutUtxoCheck(_outputs, _feePerByte, fixedInputs) {
289
- const utxoBalance = fixedInputs.reduce((a, b)=>a + (b['value'] || 0)
290
- , 0);
291
- const outputBalance = _outputs.reduce((a, b)=>a + (b['value'] || 0)
292
- , 0);
293
- const amountToSend = utxoBalance - _feePerByte * ((_outputs.length + 1) * 39 + fixedInputs.length * 153); // todo better calculation
294
- const targets = _outputs.map((target, i)=>({
295
- id: 'main',
296
- value: target.value
297
- })
298
- );
299
- if (amountToSend - outputBalance > 0) {
300
- targets.push({
301
- id: 'main',
302
- value: amountToSend - outputBalance
259
+ async getDerivationPathAddress(path) {
260
+ if (path in this._derivationCache) {
261
+ return this._derivationCache[path];
262
+ }
263
+ const baseDerivationNode = await this.baseDerivationNode();
264
+ const subPath = path.replace(this._baseDerivationPath + '/', '');
265
+ const publicKey = baseDerivationNode.derivePath(subPath).publicKey;
266
+ const address = this.getAddressFromPublicKey(publicKey);
267
+ const addressObject = new types_1.Address({
268
+ address,
269
+ publicKey: publicKey.toString('hex'),
270
+ derivationPath: path,
303
271
  });
272
+ this._derivationCache[path] = addressObject;
273
+ return addressObject;
304
274
  }
305
- return (0, _bitcoinUtils).selectCoins(fixedInputs, targets, Math.ceil(_feePerByte), fixedInputs);
306
- }
307
- async _buildTransactionWithoutUtxoCheck(outputs, feePerByte, fixedInputs) {
308
- const network = this._network;
309
- const { fee } = this._getInputForAmountWithoutUtxoCheck(outputs, feePerByte, fixedInputs);
310
- const inputs = fixedInputs;
311
- const txb = new bitcoin.TransactionBuilder(network);
312
- for (const output of outputs){
313
- const to = output.to; // Allow for OP_RETURN
314
- txb.addOutput(to, output.value);
275
+ /**
276
+ * getAddresses is an optimized version of upstream CAL's getAddresses.
277
+ * It removes the call to `asyncSetImmediate()`, speeding up the function by a factor of 6x.
278
+ *
279
+ * @param startingIndex
280
+ * @param numAddresses
281
+ * @param change
282
+ * @returns {Promise<Address[]>}
283
+ */
284
+ async getAddresses(startingIndex = 0, numAddresses = 1, change = false) {
285
+ if (numAddresses < 1) {
286
+ throw new Error('You must return at least one address');
287
+ }
288
+ const addresses = [];
289
+ const lastIndex = startingIndex + numAddresses;
290
+ const changeVal = change ? '1' : '0';
291
+ for (let currentIndex = startingIndex; currentIndex < lastIndex; currentIndex++) {
292
+ const subPath = changeVal + '/' + currentIndex;
293
+ const path = this._baseDerivationPath + '/' + subPath;
294
+ const addressObject = await this.getDerivationPathAddress(path);
295
+ addresses.push(addressObject);
296
+ }
297
+ return addresses;
315
298
  }
316
- const prevOutScriptType = 'p2wpkh';
317
- for(let i = 0; i < inputs.length; i++){
318
- const wallet = await this.getMethod('getWalletAddress')(inputs[i].address);
319
- const keyPair = await this.getMethod('keyPair')(wallet.derivationPath);
320
- const paymentVariant = this.getMethod('getPaymentVariantFromPublicKey')(keyPair.publicKey);
321
- txb.addInput(inputs[i].txid, inputs[i].vout, 0, paymentVariant.output);
299
+ /**
300
+ * findAddress is an optimized version of upstream CAL's findAddress.
301
+ *
302
+ * It searches through both change and non-change addresses (if change arg is not provided) each iteration.
303
+ *
304
+ * This is in contrast to the original findAddress function which searches
305
+ * through all non-change addresses before moving on to change addresses.
306
+ *
307
+ * @param addresses
308
+ * @returns {Promise<Address>}
309
+ */
310
+ async findAddress(addresses, change = null) {
311
+ const addressesPerCall = 20;
312
+ let index = 0;
313
+ while (index < this._maxAddressesToDerive) {
314
+ const walletAddresses = [];
315
+ if (change === null || change === false) {
316
+ walletAddresses.push(...(await this.getAddresses(index, addressesPerCall, false)));
317
+ }
318
+ if (change === null || change === true) {
319
+ walletAddresses.push(...(await this.getAddresses(index, addressesPerCall, true)));
320
+ }
321
+ const walletAddress = walletAddresses.find((walletAddr) => addresses.find((addr) => walletAddr.address === addr));
322
+ if (walletAddress) {
323
+ // Increment max addresses to derive by 100 if found within 100 addresses of maxAddressesToDerive
324
+ this._maxAddressesToDerive = Math.max(this._maxAddressesToDerive, index + 100);
325
+ return walletAddress;
326
+ }
327
+ index += addressesPerCall;
328
+ }
329
+ }
330
+ async getUsedAddresses(numAddressPerCall = 100) {
331
+ return this._getUsedUnusedAddresses(numAddressPerCall, AddressSearchType.EXTERNAL_OR_CHANGE).then(({ usedAddresses }) => usedAddresses);
322
332
  }
323
- for(let i1 = 0; i1 < inputs.length; i1++){
324
- const wallet = await this.getMethod('getWalletAddress')(inputs[i1].address);
325
- const keyPair = await this.getMethod('keyPair')(wallet.derivationPath);
326
- const paymentVariant = this.getMethod('getPaymentVariantFromPublicKey')(keyPair.publicKey);
327
- const needsWitness = true;
328
- const signParams = {
329
- prevOutScriptType,
330
- vin: i1,
331
- keyPair,
332
- witnessValue: 0
333
+ async withCachedUtxos(func) {
334
+ const originalGetMethod = this.getMethod;
335
+ const memoizedGetFeePerByte = memoizee_1.default(this.getMethod('getFeePerByte'), {
336
+ primitive: true,
337
+ });
338
+ const memoizedGetUnspentTransactions = memoizee_1.default(this.getMethod('getUnspentTransactions'), { primitive: true });
339
+ const memoizedGetAddressTransactionCounts = memoizee_1.default(this.getMethod('getAddressTransactionCounts'), {
340
+ primitive: true,
341
+ });
342
+ this.getMethod = (method, requestor = this) => {
343
+ if (method === 'getFeePerByte')
344
+ return memoizedGetFeePerByte;
345
+ if (method === 'getUnspentTransactions')
346
+ return memoizedGetUnspentTransactions;
347
+ else if (method === 'getAddressTransactionCounts')
348
+ return memoizedGetAddressTransactionCounts;
349
+ else
350
+ return originalGetMethod.bind(this)(method, requestor);
333
351
  };
334
- if (needsWitness) {
335
- signParams.witnessValue = inputs[i1].value;
336
- }
337
- txb.sign(signParams);
352
+ const result = await func.bind(this)();
353
+ this.getMethod = originalGetMethod;
354
+ return result;
338
355
  }
339
- return {
340
- hex: txb.build().toHex(),
341
- fee
342
- };
343
- }
344
- /**
345
- * quickGetAddresses is an optimized version of getAddresses.
346
- * It removes the call to `asyncSetImmediate()`, speeding up the function by a factor of 6x.
347
- *
348
- * @param startingIndex
349
- * @param numAddresses
350
- * @param change
351
- * @returns {Promise<Address[]>}
352
- */ async quickGetAddresses(startingIndex = 0, numAddresses = 1, change = false) {
353
- if (numAddresses < 1) {
354
- throw new Error('You must return at least one address');
356
+ async getTotalFee(opts, max) {
357
+ const targets = this.sendOptionsToOutputs([opts]);
358
+ if (!max) {
359
+ const { fee } = await this.getInputsForAmount(targets, opts.fee);
360
+ return fee;
361
+ }
362
+ else {
363
+ const { fee } = await this.getInputsForAmount(targets.filter((t) => !t.value), opts.fee, [], 100, true);
364
+ return fee;
365
+ }
355
366
  }
356
- const addresses = [];
357
- const lastIndex = startingIndex + numAddresses;
358
- const changeVal = change ? '1' : '0';
359
- // Original wallet provider is fetched to get the base derivation path
360
- const originalProvider = this.client.getProviderForMethod('getAddresses');
361
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
362
- const baseDerivationPath = originalProvider._baseDerivationPath;
363
- const getDerivationPathAddressFn = this.client.getMethod('getDerivationPathAddress');
364
- for(let currentIndex = startingIndex; currentIndex < lastIndex; currentIndex++){
365
- const subPath = changeVal + '/' + currentIndex;
366
- const path = baseDerivationPath + '/' + subPath;
367
- const addressObject = await getDerivationPathAddressFn(path);
368
- addresses.push(addressObject);
367
+ async getTotalFees(transactions, max) {
368
+ const fees = await this.withCachedUtxos(async () => {
369
+ const fees = {};
370
+ for (const tx of transactions) {
371
+ const fee = await this.getTotalFee(tx, max);
372
+ fees[tx.fee] = new types_1.BigNumber(fee);
373
+ }
374
+ return fees;
375
+ });
376
+ return fees;
369
377
  }
370
- return addresses;
371
- }
372
- /**
373
- * quickFindAddress is an optimized version of findAddress.
374
- *
375
- * It searches through both change and non-change addresses each iteration.
376
- *
377
- * This is in contrast to the original findAddress function which searches
378
- * through all non-change addresses before moving on to change addresses.
379
- *
380
- * @param addresses
381
- * @returns {Promise<Address[]>}
382
- */ async quickFindAddress(addresses) {
383
- const addressesPerCall = 20;
384
- let index = 0;
385
- while(index < this._maxAddressesToDerive){
386
- const walletNonChangeAddresses = await this.quickGetAddresses(index, addressesPerCall, true);
387
- const walletChangeAddresses = await this.quickGetAddresses(index, addressesPerCall, false);
388
- const walletAddresses = [
389
- ...walletNonChangeAddresses,
390
- ...walletChangeAddresses,
391
- ];
392
- const walletAddress = walletAddresses.find((walletAddr)=>addresses.find((addr)=>walletAddr.address === addr
393
- )
394
- );
395
- if (walletAddress) {
396
- // Increment max addresses to derive by 100 if found within 100 addresses of maxAddressesToDerive
397
- this._maxAddressesToDerive = Math.max(this._maxAddressesToDerive, index + 100);
398
- return walletAddress;
378
+ async getInputsForAmount(_targets, feePerByte, fixedInputs = [], numAddressPerCall = 100, sweep = false) {
379
+ let addressIndex = 0;
380
+ let changeAddresses = [];
381
+ let externalAddresses = [];
382
+ const addressCountMap = {
383
+ change: 0,
384
+ nonChange: 0,
385
+ };
386
+ const feePerBytePromise = this.getMethod('getFeePerByte')();
387
+ let utxos = [];
388
+ while (addressCountMap.change < ADDRESS_GAP ||
389
+ addressCountMap.nonChange < ADDRESS_GAP) {
390
+ let addrList = [];
391
+ if (addressCountMap.change < ADDRESS_GAP) {
392
+ // Scanning for change addr
393
+ changeAddresses = await this.getAddresses(addressIndex, numAddressPerCall, true);
394
+ addrList = addrList.concat(changeAddresses);
395
+ }
396
+ else {
397
+ changeAddresses = [];
398
+ }
399
+ if (addressCountMap.nonChange < ADDRESS_GAP) {
400
+ // Scanning for non change addr
401
+ externalAddresses = await this.getAddresses(addressIndex, numAddressPerCall, false);
402
+ addrList = addrList.concat(externalAddresses);
403
+ }
404
+ const fixedUtxos = [];
405
+ if (fixedInputs.length > 0) {
406
+ for (const input of fixedInputs) {
407
+ const txHex = await this.getMethod('getRawTransactionByHash')(input.txid);
408
+ const tx = bitcoin_utils_1.decodeRawTransaction(txHex, this._network);
409
+ const value = new types_1.BigNumber(tx.vout[input.vout].value)
410
+ .times(1e8)
411
+ .toNumber();
412
+ const address = tx.vout[input.vout].scriptPubKey.addresses[0];
413
+ const walletAddress = await this.getWalletAddress(address);
414
+ const utxo = {
415
+ ...input,
416
+ value,
417
+ address,
418
+ derivationPath: walletAddress.derivationPath,
419
+ };
420
+ fixedUtxos.push(utxo);
421
+ }
422
+ }
423
+ if (!sweep || fixedUtxos.length === 0) {
424
+ const _utxos = await this.getMethod('getUnspentTransactions')(addrList);
425
+ utxos.push(..._utxos.map((utxo) => {
426
+ const addr = addrList.find((a) => a.address === utxo.address);
427
+ return {
428
+ ...utxo,
429
+ derivationPath: addr.derivationPath,
430
+ };
431
+ }));
432
+ }
433
+ else {
434
+ utxos = fixedUtxos;
435
+ }
436
+ const utxoBalance = utxos.reduce((a, b) => a + (b.value || 0), 0);
437
+ const transactionCounts = await this.getMethod('getAddressTransactionCounts')(addrList);
438
+ if (!feePerByte)
439
+ feePerByte = await feePerBytePromise;
440
+ const minRelayFee = await this.getMethod('getMinRelayFee')();
441
+ if (feePerByte < minRelayFee) {
442
+ throw new Error(`Fee supplied (${feePerByte} sat/b) too low. Minimum relay fee is ${minRelayFee} sat/b`);
443
+ }
444
+ let targets;
445
+ if (sweep) {
446
+ const outputBalance = _targets.reduce((a, b) => a + (b['value'] || 0), 0);
447
+ const sweepOutputSize = 39;
448
+ const paymentOutputSize = _targets.filter((t) => t.value && t.address).length * 39;
449
+ const scriptOutputSize = _targets
450
+ .filter((t) => !t.value && t.script)
451
+ .reduce((size, t) => size + 39 + t.script.byteLength, 0);
452
+ const outputSize = sweepOutputSize + paymentOutputSize + scriptOutputSize;
453
+ const inputSize = utxos.length * 153;
454
+ const sweepFee = feePerByte * (inputSize + outputSize);
455
+ const amountToSend = new types_1.BigNumber(utxoBalance).minus(sweepFee);
456
+ targets = _targets.map((target) => ({
457
+ id: 'main',
458
+ value: target.value,
459
+ script: target.script,
460
+ }));
461
+ targets.push({
462
+ id: 'main',
463
+ value: amountToSend.minus(outputBalance).toNumber(),
464
+ });
465
+ }
466
+ else {
467
+ targets = _targets.map((target) => ({
468
+ id: 'main',
469
+ value: target.value,
470
+ script: target.script,
471
+ }));
472
+ }
473
+ const { inputs, outputs, change, fee } = bitcoin_utils_1.selectCoins(utxos, targets, Math.ceil(feePerByte), fixedUtxos);
474
+ if (inputs && outputs) {
475
+ return {
476
+ inputs,
477
+ change,
478
+ outputs,
479
+ fee,
480
+ };
481
+ }
482
+ for (const address of addrList) {
483
+ const isUsed = transactionCounts[address.address];
484
+ const isChangeAddress = changeAddresses.find((a) => address.address === a.address);
485
+ const key = isChangeAddress ? 'change' : 'nonChange';
486
+ if (isUsed) {
487
+ addressCountMap[key] = 0;
488
+ }
489
+ else {
490
+ addressCountMap[key]++;
491
+ }
492
+ }
493
+ addressIndex += numAddressPerCall;
399
494
  }
400
- index += addressesPerCall;
495
+ throw new errors_1.InsufficientBalanceError('Not enough balance');
401
496
  }
402
497
  }
403
- constructor(network){
404
- super();
405
- this._network = network;
406
- this._unusedAddressesBlacklist = {};
407
- this._maxAddressesToDerive = 5000;
408
- }
498
+ return BitcoinWalletProvider;
409
499
  };
410
- exports.default = BitcoinWalletProvider;
411
-
412
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL2xpYi9CaXRjb2luV2FsbGV0UHJvdmlkZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFByb3ZpZGVyIGZyb20gJ0BhdG9taWNmaW5hbmNlL3Byb3ZpZGVyJztcbmltcG9ydCB7XG4gIENyZWF0ZU11bHRpc2lnUmVzcG9uc2UsXG4gIGZpbmFsaXplUFNCVFJlc3BvbnNlLFxuICBGaW5hbmNlV2FsbGV0UHJvdmlkZXIsXG4gIElucHV0LFxuICBPdXRwdXQsXG59IGZyb20gJ0BhdG9taWNmaW5hbmNlL3R5cGVzJztcbmltcG9ydCB7IEJpdGNvaW5OZXR3b3JrIH0gZnJvbSAnQGxpcXVhbGl0eS9iaXRjb2luLW5ldHdvcmtzJztcbmltcG9ydCB7XG4gIGRlY29kZVJhd1RyYW5zYWN0aW9uLFxuICBub3JtYWxpemVUcmFuc2FjdGlvbk9iamVjdCxcbiAgc2VsZWN0Q29pbnMsXG59IGZyb20gJ0BsaXF1YWxpdHkvYml0Y29pbi11dGlscyc7XG5pbXBvcnQgeyBBZGRyZXNzLCBiaXRjb2luIGFzIGJULCBUcmFuc2FjdGlvbiB9IGZyb20gJ0BsaXF1YWxpdHkvdHlwZXMnO1xuaW1wb3J0IGFzc2VydCBmcm9tICdhc3NlcnQnO1xuaW1wb3J0ICogYXMgYml0Y29pbiBmcm9tICdiaXRjb2luanMtbGliJztcbmltcG9ydCBzZWNwMjU2azEgZnJvbSAnc2VjcDI1NmsxJztcblxuY29uc3QgRkVFX1BFUl9CWVRFX0ZBTExCQUNLID0gNTtcbmNvbnN0IEFERFJFU1NfR0FQID0gMjA7XG5jb25zdCBOT05DSEFOR0VfQUREUkVTUyA9IDA7XG5jb25zdCBDSEFOR0VfQUREUkVTUyA9IDE7XG5jb25zdCBOT05DSEFOR0VfT1JfQ0hBTkdFX0FERFJFU1MgPSAyO1xuXG50eXBlIFVudXNlZEFkZHJlc3Nlc0JsYWNrbGlzdCA9IHtcbiAgW2FkZHJlc3M6IHN0cmluZ106IHRydWU7XG59O1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBCaXRjb2luV2FsbGV0UHJvdmlkZXJcbiAgZXh0ZW5kcyBQcm92aWRlclxuICBpbXBsZW1lbnRzIFBhcnRpYWw8RmluYW5jZVdhbGxldFByb3ZpZGVyPiB7XG4gIF9uZXR3b3JrOiBCaXRjb2luTmV0d29yaztcbiAgX3VudXNlZEFkZHJlc3Nlc0JsYWNrbGlzdDogVW51c2VkQWRkcmVzc2VzQmxhY2tsaXN0O1xuICBfbWF4QWRkcmVzc2VzVG9EZXJpdmU6IG51bWJlcjtcblxuICBjb25zdHJ1Y3RvcihuZXR3b3JrOiBCaXRjb2luTmV0d29yaykge1xuICAgIHN1cGVyKCk7XG5cbiAgICB0aGlzLl9uZXR3b3JrID0gbmV0d29yaztcbiAgICB0aGlzLl91bnVzZWRBZGRyZXNzZXNCbGFja2xpc3QgPSB7fTtcbiAgICB0aGlzLl9tYXhBZGRyZXNzZXNUb0Rlcml2ZSA9IDUwMDA7XG4gIH1cblxuICBhc3luYyBidWlsZFN3ZWVwVHJhbnNhY3Rpb25XaXRoU2V0T3V0cHV0cyhcbiAgICBleHRlcm5hbENoYW5nZUFkZHJlc3M6IHN0cmluZyxcbiAgICBmZWVQZXJCeXRlOiBudW1iZXIsXG4gICAgX291dHB1dHM6IE91dHB1dFtdLFxuICAgIGZpeGVkSW5wdXRzOiBJbnB1dFtdLFxuICApIHtcbiAgICByZXR1cm4gdGhpcy5fYnVpbGRTd2VlcFRyYW5zYWN0aW9uKFxuICAgICAgZXh0ZXJuYWxDaGFuZ2VBZGRyZXNzLFxuICAgICAgZmVlUGVyQnl0ZSxcbiAgICAgIF9vdXRwdXRzLFxuICAgICAgZml4ZWRJbnB1dHMsXG4gICAgKTtcbiAgfVxuXG4gIGdldFVudXNlZEFkZHJlc3Nlc0JsYWNrbGlzdCgpOiBVbnVzZWRBZGRyZXNzZXNCbGFja2xpc3Qge1xuICAgIHJldHVybiB0aGlzLl91bnVzZWRBZGRyZXNzZXNCbGFja2xpc3Q7XG4gIH1cblxuICBzZXRVbnVzZWRBZGRyZXNzZXNCbGFja2xpc3QoXG4gICAgdW51c2VkQWRkcmVzc2VzQmxhY2tsaXN0OiBVbnVzZWRBZGRyZXNzZXNCbGFja2xpc3QsXG4gICkge1xuICAgIHRoaXMuX3VudXNlZEFkZHJlc3Nlc0JsYWNrbGlzdCA9IHVudXNlZEFkZHJlc3Nlc0JsYWNrbGlzdDtcbiAgfVxuXG4gIHNldE1heEFkZHJlc3Nlc1RvRGVyaXZlKG1heEFkZHJlc3Nlc1RvRGVyaXZlOiBudW1iZXIpIHtcbiAgICB0aGlzLl9tYXhBZGRyZXNzZXNUb0Rlcml2ZSA9IG1heEFkZHJlc3Nlc1RvRGVyaXZlO1xuICB9XG5cbiAgZ2V0TWF4QWRkcmVzc2VzVG9EZXJpdmUoKSB7XG4gICAgcmV0dXJuIHRoaXMuX21heEFkZHJlc3Nlc1RvRGVyaXZlO1xuICB9XG5cbiAgX2NyZWF0ZU11bHRpc2lnUGF5bWVudChtOiBudW1iZXIsIHB1YmtleXM6IHN0cmluZ1tdKTogYml0Y29pbi5QYXltZW50IHtcbiAgICBpZiAobSA+IHB1YmtleXMubGVuZ3RoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBub3QgZW5vdWdoIGtleXMgc3VwcGxpZWQgKGdvdCAke3B1YmtleXMubGVuZ3RofSBrZXlzLCBidXQgbmVlZCBhdCBsZWFzdCAke219IHRvIHJlZGVlbSlgLFxuICAgICAgKTtcbiAgICB9XG4gICAgLy8gQ3JlYXRlIG0tb2YtbiBtdWx0aXNpZ1xuICAgIGNvbnN0IHAybXMgPSBiaXRjb2luLnBheW1lbnRzLnAybXMoe1xuICAgICAgbTogbSxcbiAgICAgIHB1YmtleXM6IHB1YmtleXMubWFwKChrZXk6IHN0cmluZykgPT4gQnVmZmVyLmZyb20oa2V5LCAnaGV4JykpLFxuICAgICAgbmV0d29yazogdGhpcy5fbmV0d29yayxcbiAgICB9KTtcblxuICAgIC8vIENyZWF0ZSBwMndzaCBmb3IgbXVsdGlzaWdcbiAgICBjb25zdCBwMndzaCA9IGJpdGNvaW4ucGF5bWVudHMucDJ3c2goe1xuICAgICAgcmVkZWVtOiBwMm1zLFxuICAgICAgbmV0d29yazogdGhpcy5fbmV0d29yayxcbiAgICB9KTtcblxuICAgIHJldHVybiBwMndzaDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbmF0aXZlLXNlZ3dpdCBtdWx0aS1zaWduYXR1cmUgYWRkcmVzcyAoUDJNUyBpbiBQMldTSCkgd2l0aCBuIHNpZ25hdHVyZXMgb2YgbSByZXF1aXJlZCBrZXlzXG4gICAqIGh0dHBzOi8vZGV2ZWxvcGVyLmJpdGNvaW4ub3JnL3JlZmVyZW5jZS9ycGMvY3JlYXRlbXVsdGlzaWcuaHRtbFxuICAgKiBAcGFyYW0gbSB0aGUgbnVtYmVyIG9mIHJlcXVpcmVkIHNpZ25hdHVyZXNcbiAgICogQHBhcmFtIHB1YmtleXMgbiBwb3NzaWJsZSBwdWJrZXlzIGluIHRvdGFsXG4gICAqIEByZXR1cm5zIGEganNvbiBvYmplY3QgY29udGFpbmluZyB0aGUgYGFkZHJlc3NgIGFuZCBgcmVkZWVtU2NyaXB0YFxuICAgKi9cbiAgY3JlYXRlTXVsdGlzaWcobTogbnVtYmVyLCBwdWJrZXlzOiBzdHJpbmdbXSk6IENyZWF0ZU11bHRpc2lnUmVzcG9uc2Uge1xuICAgIGNvbnN0IHAyd3NoID0gdGhpcy5fY3JlYXRlTXVsdGlzaWdQYXltZW50KG0sIHB1YmtleXMpO1xuICAgIHJldHVybiB7XG4gICAgICBhZGRyZXNzOiBwMndzaC5hZGRyZXNzLFxuICAgICAgcmVkZWVtU2NyaXB0OiBwMndzaC5yZWRlZW0/Lm91dHB1dD8udG9TdHJpbmcoJ2hleCcpLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIFBTQlQgb2YgYSBuYXRpdmUtc2Vnd2l0IG11bHRpLXNpZ25hdHVyZSBhZGRyZXNzIChQMk1TIGluIFAyV1NIKSB3aXRoIG4gc2lnbmF0dXJlcyBvZiBtIHJlcXVpcmVkIGtleXNcbiAgICogaHR0cHM6Ly9kZXZlbG9wZXIuYml0Y29pbi5vcmcvcmVmZXJlbmNlL3JwYy9jcmVhdGVtdWx0aXNpZy5odG1sXG4gICAqIGh0dHBzOi8vZGV2ZWxvcGVyLmJpdGNvaW4ub3JnL3JlZmVyZW5jZS9ycGMvY3JlYXRlcHNidC5odG1sXG4gICAqIEBwYXJhbSBtIHRoZSBudW1iZXIgb2YgcmVxdWlyZWQgc2lnbmF0dXJlc1xuICAgKiBAcGFyYW0gcHVia2V5cyBuIHBvc3NpYmxlIHB1YmtleXMgaW4gdG90YWxcbiAgICogQHBhcmFtIGlucHV0cyB0aGUgSW5wdXRzIHRvIHRoZSBQU0JUXG4gICAqIEBwYXJhbSBvdXB1dHMgdGhlIE91dHB1dHMgdG8gdGhlIFBTQlRcbiAgICogQHJldHVybnMgYSBiYXNlNjQgZW5jb2RlZCBwc2J0IHN0cmluZ1xuICAgKi9cbiAgYnVpbGRNdWx0aXNpZ1BTQlQoXG4gICAgbTogbnVtYmVyLFxuICAgIHB1YmtleXM6IHN0cmluZ1tdLFxuICAgIGlucHV0czogSW5wdXRbXSxcbiAgICBvdXRwdXRzOiBPdXRwdXRbXSxcbiAgKTogc3RyaW5nIHtcbiAgICBhc3NlcnQoaW5wdXRzLmxlbmd0aCA+IDAsICdubyBpbnB1dHMgZm91bmQnKTtcbiAgICBhc3NlcnQob3V0cHV0cy5sZW5ndGggPiAwLCAnbm8gb3V0cHV0cyBmb3VuZCcpO1xuXG4gICAgY29uc3QgcDJ3c2ggPSB0aGlzLl9jcmVhdGVNdWx0aXNpZ1BheW1lbnQobSwgcHVia2V5cyk7XG5cbiAgICAvLyBWZXJpZnkgcHVia2V5aGFzaCBmb3IgYWxsIGlucHV0cyBtYXRjaGVzIHRoZSBwMndzaCBoYXNoXG4gICAgYXNzZXJ0KFxuICAgICAgaW5wdXRzLmV2ZXJ5KFxuICAgICAgICAoaW5wdXQ6IElucHV0KSA9PiBwMndzaC5vdXRwdXQudG9TdHJpbmcoJ2hleCcpID09PSBpbnB1dC5zY3JpcHRQdWJLZXksXG4gICAgICApLFxuICAgICAgJ2FkZHJlc3MgcHVia2V5aGFzaCBkb2VzIG5vdCBtYXRjaCBpbnB1dCBzY3JpcHRQdWJLZXknLFxuICAgICk7XG5cbiAgICAvLyBjcmVhdG9yXG4gICAgY29uc3QgcHNidCA9IG5ldyBiaXRjb2luLlBzYnQoeyBuZXR3b3JrOiB0aGlzLl9uZXR3b3JrIH0pO1xuXG4gICAgLy8gdXBkYXRlclxuICAgIGlucHV0cy5mb3JFYWNoKChpbnB1dDogSW5wdXQpID0+IHtcbiAgICAgIHBzYnQuYWRkSW5wdXQoe1xuICAgICAgICBoYXNoOiBpbnB1dC50eGlkLFxuICAgICAgICBpbmRleDogaW5wdXQudm91dCxcbiAgICAgICAgd2l0bmVzc1V0eG86IHsgc2NyaXB0OiBwMndzaC5vdXRwdXQsIHZhbHVlOiBpbnB1dC52YWx1ZSB9LFxuICAgICAgICB3aXRuZXNzU2NyaXB0OiBwMndzaC5yZWRlZW0ub3V0cHV0LFxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICBvdXRwdXRzLmZvckVhY2goKG91dHB1dDogT3V0cHV0KSA9PiB7XG4gICAgICBwc2J0LmFkZE91dHB1dCh7XG4gICAgICAgIGFkZHJlc3M6IG91dHB1dC50byxcbiAgICAgICAgdmFsdWU6IG91dHB1dC52YWx1ZSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHBzYnQudG9CYXNlNjQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgYSBQU0JUIHdpdGggaW5wdXQgaW5mb3JtYXRpb24gZnJvbSBvdXIgd2FsbGV0IGFuZCB0aGVuIHNpZ24gaW5wdXRzIHRoYXQgd2UgY2FuIHNpZ24gZm9yXG4gICAqIGh0dHBzOi8vZGV2ZWxvcGVyLmJpdGNvaW4ub3JnL3JlZmVyZW5jZS9ycGMvd2FsbGV0cHJvY2Vzc3BzYnQuaHRtbFxuICAgKiBAcGFyYW0gcHNidCBhIGJhc2U2NCBlbmNvZGVkIHBzYnQgc3RyaW5nIChQMldTSCBvbmx5KVxuICAgKiBAcmV0dXJucyBhIGJhc2U2NCBlbmNvZGVkIHNpZ25lZCBwc2J0IHN0cmluZ1xuICAgKi9cbiAgYXN5bmMgd2FsbGV0UHJvY2Vzc1BTQlQocHNidFN0cmluZzogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBjb25zdCBwc2J0ID0gYml0Y29pbi5Qc2J0LmZyb21CYXNlNjQocHNidFN0cmluZyk7XG5cbiAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgIHBzYnQuZGF0YS5pbnB1dHMubWFwKGFzeW5jIChpbnB1dCwgaTogbnVtYmVyKSA9PiB7XG4gICAgICAgIGFzc2VydChcbiAgICAgICAgICBwc2J0LmdldElucHV0VHlwZShpKS5zbGljZSgwLCA1KSA9PT0gJ3Ayd3NoJyxcbiAgICAgICAgICAnb25seSBhY2NlcHRzIFAyV1NIIGlucHV0cycsXG4gICAgICAgICk7XG5cbiAgICAgICAgY29uc3Qgc2NyaXB0U3RhY2sgPSBiaXRjb2luLnNjcmlwdC5kZWNvbXBpbGUoaW5wdXQud2l0bmVzc1NjcmlwdCk7XG4gICAgICAgIGNvbnN0IHB1YmtleXMgPSBzY3JpcHRTdGFjay5maWx0ZXIoXG4gICAgICAgICAgKGRhdGEpID0+IEJ1ZmZlci5pc0J1ZmZlcihkYXRhKSAmJiBzZWNwMjU2azEucHVibGljS2V5VmVyaWZ5KGRhdGEpLFxuICAgICAgICApO1xuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICAgIHB1YmtleXMubWFwKGFzeW5jIChrZXkpID0+IHtcbiAgICAgICAgICAgIC8vIGNyZWF0ZSBhZGRyZXNzIHVzaW5nIHB1YmtleVxuICAgICAgICAgICAgY29uc3QgeyBhZGRyZXNzOiBhZGRyZXNzU3RyaW5nIH0gPSBiaXRjb2luLnBheW1lbnRzLnAyd3BraCh7XG4gICAgICAgICAgICAgIHB1YmtleToga2V5IGFzIEJ1ZmZlcixcbiAgICAgICAgICAgICAgbmV0d29yazogdGhpcy5fbmV0d29yayxcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBSZXRyaWV2ZSBhZGRyZXNzIG9iamVjdCBmcm9tIHdhbGxldCB1c2luZyBhZGRyZXNzXG4gICAgICAgICAgICBjb25zdCBhZGRyZXNzOiBBZGRyZXNzID0gYXdhaXQgdGhpcy5xdWlja0ZpbmRBZGRyZXNzKFtcbiAgICAgICAgICAgICAgYWRkcmVzc1N0cmluZyxcbiAgICAgICAgICAgIF0pO1xuXG4gICAgICAgICAgICAvLyBleGl0IGlmIGFkZHJlc3MgZG9lc24ndCBleGlzdCBpbiB3YWxsZXRcbiAgICAgICAgICAgIGlmICghYWRkcmVzcykgcmV0dXJuO1xuXG4gICAgICAgICAgICAvLyBkZXJpdmUga2V5cGFpclxuICAgICAgICAgICAgY29uc3Qga2V5UGFpciA9IGF3YWl0IHRoaXMuZ2V0TWV0aG9kKCdrZXlQYWlyJykoXG4gICAgICAgICAgICAgIGFkZHJlc3MuZGVyaXZhdGlvblBhdGgsXG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAvLyBzaWduIFBTQlQgdXNpbmcga2V5cGFpclxuICAgICAgICAgICAgcHNidC5zaWduSW5wdXQoaSwga2V5UGFpcik7XG4gICAgICAgICAgfSksXG4gICAgICAgICk7XG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgcHNidC52YWxpZGF0ZVNpZ25hdHVyZXNPZkFsbElucHV0cygpOyAvLyBlbnN1cmUgYWxsIHNpZ25hdHVyZXMgYXJlIHZhbGlkIVxuICAgIHJldHVybiBwc2J0LnRvQmFzZTY0KCk7XG4gIH1cblxuICAvKipcbiAgICogRmluYWxpemUgdGhlIGlucHV0cyBvZiBhIFBTQlQuIElmIHRoZSB0cmFuc2FjdGlvbiBpcyBmdWxseSBzaWduZWQsIGl0IHdpbGxcbiAgICogcHJvZHVjZSBhIG5ldHdvcmsgc2VyaWFsaXplZCB0cmFuc2FjdGlvbiB3aGljaCBjYW4gYmUgYnJvYWRjYXN0IHdpdGggc2VuZHJhd3RyYW5zYWN0aW9uXG4gICAqIGh0dHBzOi8vZGV2ZWxvcGVyLmJpdGNvaW4ub3JnL3JlZmVyZW5jZS9ycGMvZmluYWxpemVwc2J0Lmh0bWxcbiAgICogQHBhcmFtIHBzYnQgYSBiYXNlNjQgZW5jb2RlZCBwc2J0IHN0cmluZ1xuICAgKiBAcmV0dXJucyBhIGpzb24gb2JqZWN0IGNvbnRhaW5pbmcgYHBzYnRgIGluIGJhc2U2NCwgYGhleGAgZm9yIHRyYW5zYWN0aW9uIGFuZCBgY29tcGxldGVgIGZvciBpZlxuICAgKiB0aGUgdHJhbnNhY3Rpb24gaGFzIGEgY29tcGxldGUgc2V0IG9mIHNpZ25hdHVyZXNcbiAgICovXG4gIGZpbmFsaXplUFNCVChwc2J0U3RyaW5nOiBzdHJpbmcpOiBmaW5hbGl6ZVBTQlRSZXNwb25zZSB7XG4gICAgY29uc3QgcHNidCA9IGJpdGNvaW4uUHNidC5mcm9tQmFzZTY0KHBzYnRTdHJpbmcpO1xuXG4gICAgdHJ5IHtcbiAgICAgIHBzYnQudmFsaWRhdGVTaWduYXR1cmVzT2ZBbGxJbnB1dHMoKTsgLy8gZW5zdXJlIGFsbCBzaWduYXR1cmVzIGFyZSB2YWxpZCFcbiAgICAgIHBzYnQuZmluYWxpemVBbGxJbnB1dHMoKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgcHNidDogcHNidC50b0Jhc2U2NCgpLFxuICAgICAgICBjb21wbGV0ZTogZmFsc2UsXG4gICAgICB9O1xuICAgIH1cbiAgICByZXR1cm4ge1xuICAgICAgcHNidDogcHNidC50b0Jhc2U2NCgpLFxuICAgICAgaGV4OiBwc2J0LmV4dHJhY3RUcmFuc2FjdGlvbigpLnRvSGV4KCksXG4gICAgICBjb21wbGV0ZTogdHJ1ZSxcbiAgICB9O1xuICB9XG5cbiAgYXN5bmMgZ2V0VW51c2VkQWRkcmVzcyhjaGFuZ2UgPSBmYWxzZSwgbnVtQWRkcmVzc1BlckNhbGwgPSAxMDApIHtcbiAgICBjb25zdCBhZGRyZXNzVHlwZSA9IGNoYW5nZSA/IENIQU5HRV9BRERSRVNTIDogTk9OQ0hBTkdFX0FERFJFU1M7XG4gICAgY29uc3Qga2V5ID0gY2hhbmdlID8gJ2NoYW5nZScgOiAnbm9uQ2hhbmdlJztcblxuICAgIGNvbnN0IGFkZHJlc3MgPSBhd2FpdCB0aGlzLl9nZXRVc2VkVW51c2VkQWRkcmVzc2VzKFxuICAgICAgbnVtQWRkcmVzc1BlckNhbGwsXG4gICAgICBhZGRyZXNzVHlwZSxcbiAgICApLnRoZW4oKHsgdW51c2VkQWRkcmVzcyB9KSA9PiB1bnVzZWRBZGRyZXNzW2tleV0pO1xuICAgIHRoaXMuX3VudXNlZEFkZHJlc3Nlc0JsYWNrbGlzdFthZGRyZXNzLmFkZHJlc3NdID0gdHJ1ZTtcblxuICAgIHJldHVybiBhZGRyZXNzO1xuICB9XG5cbiAgYXN5bmMgX2dldFVzZWRVbnVzZWRBZGRyZXNzZXMobnVtQWRkcmVzc1BlckNhbGwgPSAxMDAsIGFkZHJlc3NUeXBlKSB7XG4gICAgY29uc3QgdXNlZEFkZHJlc3NlcyA9IFtdO1xuICAgIGNvbnN0IGFkZHJlc3NDb3VudE1hcCA9IHsgY2hhbmdlOiAwLCBub25DaGFuZ2U6IDAgfTtcbiAgICBjb25zdCB1bnVzZWRBZGRyZXNzTWFwID0geyBjaGFuZ2U6IG51bGwsIG5vbkNoYW5nZTogbnVsbCB9O1xuXG4gICAgbGV0IGFkZHJMaXN0O1xuICAgIGxldCBhZGRyZXNzSW5kZXggPSAwO1xuICAgIGxldCBjaGFuZ2VBZGRyZXNzZXM6IEFkZHJlc3NbXSA9IFtdO1xuICAgIGxldCBub25DaGFuZ2VBZGRyZXNzZXM6IEFkZHJlc3NbXSA9IFtdO1xuXG4gICAgLyogZXNsaW50LWRpc2FibGUgbm8tdW5tb2RpZmllZC1sb29wLWNvbmRpdGlvbiAqL1xuICAgIHdoaWxlIChcbiAgICAgIChhZGRyZXNzVHlwZSA9PT0gTk9OQ0hBTkdFX09SX0NIQU5HRV9BRERSRVNTICYmXG4gICAgICAgIChhZGRyZXNzQ291bnRNYXAuY2hhbmdlIDwgQUREUkVTU19HQVAgfHxcbiAgICAgICAgICBhZGRyZXNzQ291bnRNYXAubm9uQ2hhbmdlIDwgQUREUkVTU19HQVApKSB8fFxuICAgICAgKGFkZHJlc3NUeXBlID09PSBOT05DSEFOR0VfQUREUkVTUyAmJlxuICAgICAgICBhZGRyZXNzQ291bnRNYXAubm9uQ2hhbmdlIDwgQUREUkVTU19HQVApIHx8XG4gICAgICAoYWRkcmVzc1R5cGUgPT09IENIQU5HRV9BRERSRVNTICYmIGFkZHJlc3NDb3VudE1hcC5jaGFuZ2UgPCBBRERSRVNTX0dBUClcbiAgICApIHtcbiAgICAgIC8qIGVzbGludC1lbmFibGUgbm8tdW5tb2RpZmllZC1sb29wLWNvbmRpdGlvbiAqL1xuICAgICAgYWRkckxpc3QgPSBbXTtcblxuICAgICAgaWYgKFxuICAgICAgICAoYWRkcmVzc1R5cGUgPT09IE5PTkNIQU5HRV9PUl9DSEFOR0VfQUREUkVTUyB8fFxuICAgICAgICAgIGFkZHJlc3NUeXBlID09PSBDSEFOR0VfQUREUkVTUykgJiZcbiAgICAgICAgYWRkcmVzc0NvdW50TWFwLmNoYW5nZSA8IEFERFJFU1NfR0FQXG4gICAgICApIHtcbiAgICAgICAgLy8gU2Nhbm5pbmcgZm9yIGNoYW5nZSBhZGRyXG4gICAgICAgIGNoYW5nZUFkZHJlc3NlcyA9IGF3YWl0IHRoaXMuY2xpZW50LndhbGxldC5nZXRBZGRyZXNzZXMoXG4gICAgICAgICAgYWRkcmVzc0luZGV4LFxuICAgICAgICAgIG51bUFkZHJlc3NQZXJDYWxsLFxuICAgICAgICAgIHRydWUsXG4gICAgICAgICk7XG4gICAgICAgIGFkZHJMaXN0ID0gYWRkckxpc3QuY29uY2F0KGNoYW5nZUFkZHJlc3Nlcyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjaGFuZ2VBZGRyZXNzZXMgPSBbXTtcbiAgICAgIH1cblxuICAgICAgaWYgKFxuICAgICAgICAoYWRkcmVzc1R5cGUgPT09IE5PTkNIQU5HRV9PUl9DSEFOR0VfQUREUkVTUyB8fFxuICAgICAgICAgIGFkZHJlc3NUeXBlID09PSBOT05DSEFOR0VfQUREUkVTUykgJiZcbiAgICAgICAgYWRkcmVzc0NvdW50TWFwLm5vbkNoYW5nZSA8IEFERFJFU1NfR0FQXG4gICAgICApIHtcbiAgICAgICAgLy8gU2Nhbm5pbmcgZm9yIG5vbiBjaGFuZ2UgYWRkclxuICAgICAgICBub25DaGFuZ2VBZGRyZXNzZXMgPSBhd2FpdCB0aGlzLnF1aWNrR2V0QWRkcmVzc2VzKFxuICAgICAgICAgIGFkZHJlc3NJbmRleCxcbiAgICAgICAgICBudW1BZGRyZXNzUGVyQ2FsbCxcbiAgICAgICAgICBmYWxzZSxcbiAgICAgICAgKTtcbiAgICAgICAgYWRkckxpc3QgPSBhZGRyTGlzdC5jb25jYXQobm9uQ2hhbmdlQWRkcmVzc2VzKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgdHJhbnNhY3Rpb25Db3VudHMgPSBhd2FpdCB0aGlzLmdldE1ldGhvZChcbiAgICAgICAgJ2dldEFkZHJlc3NUcmFuc2FjdGlvbkNvdW50cycsXG4gICAgICApKGFkZHJMaXN0KTtcblxuICAgICAgZm9yIChjb25zdCBhZGRyZXNzIG9mIGFkZHJMaXN0KSB7XG4gICAgICAgIGNvbnN0IGlzVXNlZCA9XG4gICAgICAgICAgdHJhbnNhY3Rpb25Db3VudHNbYWRkcmVzcy5hZGRyZXNzXSA+IDAgfHxcbiAgICAgICAgICB0aGlzLl91bnVzZWRBZGRyZXNzZXNCbGFja2xpc3RbYWRkcmVzcy5hZGRyZXNzXTtcbiAgICAgICAgY29uc3QgaXNDaGFuZ2VBZGRyZXNzID0gY2hhbmdlQWRkcmVzc2VzLmZpbmQoXG4gICAgICAgICAgKGEpID0+IGFkZHJlc3MuYWRkcmVzcyA9PT0gYS5hZGRyZXNzLFxuICAgICAgICApO1xuICAgICAgICBjb25zdCBrZXkgPSBpc0NoYW5nZUFkZHJlc3MgPyAnY2hhbmdlJyA6ICdub25DaGFuZ2UnO1xuXG4gICAgICAgIGlmIChpc1VzZWQpIHtcbiAgICAgICAgICB1c2VkQWRkcmVzc2VzLnB1c2goYWRkcmVzcyk7XG4gICAgICAgICAgYWRkcmVzc0NvdW50TWFwW2tleV0gPSAwO1xuICAgICAgICAgIHVudXNlZEFkZHJlc3NNYXBba2V5XSA9IG51bGw7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYWRkcmVzc0NvdW50TWFwW2tleV0rKztcblxuICAgICAgICAgIGlmICghdW51c2VkQWRkcmVzc01hcFtrZXldKSB7XG4gICAgICAgICAgICB1bnVzZWRBZGRyZXNzTWFwW2tleV0gPSBhZGRyZXNzO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBhZGRyZXNzSW5kZXggKz0gbnVtQWRkcmVzc1BlckNhbGw7XG4gICAgfVxuXG4gICAgbGV0IGZpcnN0VW51c2VkQWRkcmVzcztcbiAgICBjb25zdCBpbmRleE5vbkNoYW5nZSA9IHVudXNlZEFkZHJlc3NNYXAubm9uQ2hhbmdlXG4gICAgICA/IHVudXNlZEFkZHJlc3NNYXAubm9uQ2hhbmdlLmluZGV4XG4gICAgICA6IEluZmluaXR5O1xuICAgIGNvbnN0IGluZGV4Q2hhbmdlID0gdW51c2VkQWRkcmVzc01hcC5jaGFuZ2VcbiAgICAgID8gdW51c2VkQWRkcmVzc01hcC5jaGFuZ2UuaW5kZXhcbiAgICAgIDogSW5maW5pdHk7XG5cbiAgICBpZiAoaW5kZXhOb25DaGFuZ2UgPD0gaW5kZXhDaGFuZ2UpXG4gICAgICBmaXJzdFVudXNlZEFkZHJlc3MgPSB1bnVzZWRBZGRyZXNzTWFwLm5vbkNoYW5nZTtcbiAgICBlbHNlIGZpcnN0VW51c2VkQWRkcmVzcyA9IHVudXNlZEFkZHJlc3NNYXAuY2hhbmdlO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHVzZWRBZGRyZXNzZXMsXG4gICAgICB1bnVzZWRBZGRyZXNzOiB1bnVzZWRBZGRyZXNzTWFwLFxuICAgICAgZmlyc3RVbnVzZWRBZGRyZXNzLFxuICAgIH07XG4gIH1cblxuICBhc3luYyBzZW5kU3dlZXBUcmFuc2FjdGlvbldpdGhTZXRPdXRwdXRzKFxuICAgIGV4dGVybmFsQ2hhbmdlQWRkcmVzczogc3RyaW5nLFxuICAgIGZlZVBlckJ5dGU6IG51bWJlcixcbiAgICBfb3V0cHV0czogT3V0cHV0W10sXG4gICAgZml4ZWRJbnB1dHM6IElucHV0W10sXG4gICk6IFByb21pc2U8VHJhbnNhY3Rpb248YlQuVHJhbnNhY3Rpb24+PiB7XG4gICAgY29uc3QgeyBoZXgsIGZlZSB9ID0gYXdhaXQgdGhpcy5fYnVpbGRTd2VlcFRyYW5zYWN0aW9uKFxuICAgICAgZXh0ZXJuYWxDaGFuZ2VBZGRyZXNzLFxuICAgICAgZmVlUGVyQnl0ZSxcbiAgICAgIF9vdXRwdXRzLFxuICAgICAgZml4ZWRJbnB1dHMsXG4gICAgKTtcbiAgICBhd2FpdCB0aGlzLmdldE1ldGhvZCgnc2VuZFJhd1RyYW5zYWN0aW9uJykoaGV4KTtcbiAgICByZXR1cm4gbm9ybWFsaXplVHJhbnNhY3Rpb25PYmplY3QoXG4gICAgICBkZWNvZGVSYXdUcmFuc2FjdGlvbihoZXgsIHRoaXMuX25ldHdvcmspLFxuICAgICAgZmVlLFxuICAgICk7XG4gIH1cblxuICBhc3luYyBfYnVpbGRTd2VlcFRyYW5zYWN0aW9uKFxuICAgIGV4dGVybmFsQ2hhbmdlQWRkcmVzczogc3RyaW5nLFxuICAgIGZlZVBlckJ5dGU6IG51bWJlcixcbiAgICBfb3V0cHV0czogT3V0cHV0W10gPSBbXSxcbiAgICBmaXhlZElucHV0czogSW5wdXRbXSxcbiAgKSB7XG4gICAgY29uc3QgX2ZlZVBlckJ5dGUgPVxuICAgICAgZmVlUGVyQnl0ZSB8fFxuICAgICAgKGF3YWl0IHRoaXMuZ2V0TWV0aG9kKCdnZXRGZWVQZXJCeXRlJykoKSkgfHxcbiAgICAgIEZFRV9QRVJfQllURV9GQUxMQkFDSztcbiAgICBjb25zdCBpbnB1dHM6IElucHV0W10gPSBbXTtcbiAgICBjb25zdCBvdXRwdXRzOiBPdXRwdXRbXSA9IFtdO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBpbnB1dHNGb3JBbW91bnQgPSBhd2FpdCB0aGlzLmdldE1ldGhvZCgnZ2V0SW5wdXRzRm9yQW1vdW50JykoXG4gICAgICAgIF9vdXRwdXRzLFxuICAgICAgICBfZmVlUGVyQnl0ZSxcbiAgICAgICAgZml4ZWRJbnB1dHMsXG4gICAgICAgIDEwMCxcbiAgICAgICAgdHJ1ZSxcbiAgICAgICk7XG4gICAgICBpZiAoaW5wdXRzRm9yQW1vdW50LmNoYW5nZSkge1xuICAgICAgICB0aHJvdyBFcnJvcignVGhlcmUgc2hvdWxkIG5vdCBiZSBhbnkgY2hhbmdlIGZvciBzd2VlcGluZyB0cmFuc2FjdGlvbicpO1xuICAgICAgfVxuICAgICAgaW5wdXRzLnB1c2goLi4uKGlucHV0c0ZvckFtb3VudC5pbnB1dHMgfHwgW10pKTtcbiAgICAgIG91dHB1dHMucHVzaCguLi4oaW5wdXRzRm9yQW1vdW50Lm91dHB1dHMgfHwgW10pKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBpZiAoZml4ZWRJbnB1dHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHRocm93IEVycm9yKFxuICAgICAgICAgIGBJbnB1dHMgZm9yIGFtb3VudCBkb2Vzbid0IGV4aXN0IGFuZCBubyBmaXhlZElucHV0cyBwcm92aWRlZGAsXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGlucHV0c0ZvckFtb3VudCA9IGF3YWl0IHRoaXMuX2dldElucHV0Rm9yQW1vdW50V2l0aG91dFV0eG9DaGVjayhcbiAgICAgICAgX291dHB1dHMsXG4gICAgICAgIF9mZWVQZXJCeXRlLFxuICAgICAgICBmaXhlZElucHV0cyxcbiAgICAgICk7XG4gICAgICBpbnB1dHMucHVzaChcbiAgICAgICAgLi4uKGlucHV0c0ZvckFtb3VudC5pbnB1dHMubWFwKCh1dHhvKSA9PiBJbnB1dC5mcm9tVVRYTyh1dHhvKSkgfHwgW10pLFxuICAgICAgKTtcbiAgICAgIG91dHB1dHMucHVzaCguLi4oaW5wdXRzRm9yQW1vdW50Lm91dHB1dHMgfHwgW10pKTtcbiAgICB9XG4gICAgX291dHB1dHMuZm9yRWFjaCgob3V0cHV0KSA9PiB7XG4gICAgICBjb25zdCBzcGxpY2VJbmRleCA9IG91dHB1dHMuZmluZEluZGV4KFxuICAgICAgICAoc3dlZXBPdXRwdXQ6IE91dHB1dCkgPT4gb3V0cHV0LnZhbHVlID09PSBzd2VlcE91dHB1dC52YWx1ZSxcbiAgICAgICk7XG4gICAgICBvdXRwdXRzLnNwbGljZShzcGxpY2VJbmRleCwgMSk7XG4gICAgfSk7XG4gICAgX291dHB1dHMucHVzaCh7XG4gICAgICB0bzogZXh0ZXJuYWxDaGFuZ2VBZGRyZXNzLFxuICAgICAgdmFsdWU6IG91dHB1dHNbMF0udmFsdWUsXG4gICAgfSk7XG4gICAgcmV0dXJuIHRoaXMuX2J1aWxkVHJhbnNhY3Rpb25XaXRob3V0VXR4b0NoZWNrKFxuICAgICAgX291dHB1dHMsXG4gICAgICBfZmVlUGVyQnl0ZSxcbiAgICAgIGlucHV0cyxcbiAgICApO1xuICB9XG5cbiAgX2dldElucHV0Rm9yQW1vdW50V2l0aG91dFV0eG9DaGVjayhcbiAgICBfb3V0cHV0czogT3V0cHV0W10sXG4gICAgX2ZlZVBlckJ5dGU6IG51bWJlcixcbiAgICBmaXhlZElucHV0czogSW5wdXRbXSxcbiAgKToge1xuICAgIGlucHV0czogYlQuVVRYT1tdO1xuICAgIG91dHB1dHM6IHsgdmFsdWU6IG51bWJlcjsgaWQ/OiBzdHJpbmcgfVtdO1xuICAgIGZlZTogbnVtYmVyO1xuICAgIGNoYW5nZTogeyB2YWx1ZTogbnVtYmVyOyBpZD86IHN0cmluZyB9O1xuICB9IHtcbiAgICBjb25zdCB1dHhvQmFsYW5jZSA9IGZpeGVkSW5wdXRzLnJlZHVjZSgoYSwgYikgPT4gYSArIChiWyd2YWx1ZSddIHx8IDApLCAwKTtcbiAgICBjb25zdCBvdXRwdXRCYWxhbmNlID0gX291dHB1dHMucmVkdWNlKChhLCBiKSA9PiBhICsgKGJbJ3ZhbHVlJ10gfHwgMCksIDApO1xuICAgIGNvbnN0IGFtb3VudFRvU2VuZCA9XG4gICAgICB1dHhvQmFsYW5jZSAtXG4gICAgICBfZmVlUGVyQnl0ZSAqICgoX291dHB1dHMubGVuZ3RoICsgMSkgKiAzOSArIGZpeGVkSW5wdXRzLmxlbmd0aCAqIDE1Myk7IC8vIHRvZG8gYmV0dGVyIGNhbGN1bGF0aW9uXG5cbiAgICBjb25zdCB0YXJnZXRzID0gX291dHB1dHMubWFwKCh0YXJnZXQsIGkpID0+ICh7XG4gICAgICBpZDogJ21haW4nLFxuICAgICAgdmFsdWU6IHRhcmdldC52YWx1ZSxcbiAgICB9KSk7XG4gICAgaWYgKGFtb3VudFRvU2VuZCAtIG91dHB1dEJhbGFuY2UgPiAwKSB7XG4gICAgICB0YXJnZXRzLnB1c2goeyBpZDogJ21haW4nLCB2YWx1ZTogYW1vdW50VG9TZW5kIC0gb3V0cHV0QmFsYW5jZSB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gc2VsZWN0Q29pbnMoXG4gICAgICBmaXhlZElucHV0cyxcbiAgICAgIHRhcmdldHMsXG4gICAgICBNYXRoLmNlaWwoX2ZlZVBlckJ5dGUpLFxuICAgICAgZml4ZWRJbnB1dHMsXG4gICAgKTtcbiAgfVxuXG4gIGFzeW5jIF9idWlsZFRyYW5zYWN0aW9uV2l0aG91dFV0eG9DaGVjayhcbiAgICBvdXRwdXRzOiBPdXRwdXRbXSxcbiAgICBmZWVQZXJCeXRlOiBudW1iZXIsXG4gICAgZml4ZWRJbnB1dHM6IElucHV0W10sXG4gICkge1xuICAgIGNvbnN0IG5ldHdvcmsgPSB0aGlzLl9uZXR3b3JrO1xuXG4gICAgY29uc3QgeyBmZWUgfSA9IHRoaXMuX2dldElucHV0Rm9yQW1vdW50V2l0aG91dFV0eG9DaGVjayhcbiAgICAgIG91dHB1dHMsXG4gICAgICBmZWVQZXJCeXRlLFxuICAgICAgZml4ZWRJbnB1dHMsXG4gICAgKTtcbiAgICBjb25zdCBpbnB1dHMgPSBmaXhlZElucHV0cztcblxuICAgIGNvbnN0IHR4YiA9IG5ldyBiaXRjb2luLlRyYW5zYWN0aW9uQnVpbGRlcihuZXR3b3JrKTtcblxuICAgIGZvciAoY29uc3Qgb3V0cHV0IG9mIG91dHB1dHMpIHtcbiAgICAgIGNvbnN0IHRvID0gb3V0cHV0LnRvOyAvLyBBbGxvdyBmb3IgT1BfUkVUVVJOXG4gICAgICB0eGIuYWRkT3V0cHV0KHRvLCBvdXRwdXQudmFsdWUpO1xuICAgIH1cblxuICAgIGNvbnN0IHByZXZPdXRTY3JpcHRUeXBlID0gJ3Ayd3BraCc7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGlucHV0cy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3Qgd2FsbGV0ID0gYXdhaXQgdGhpcy5nZXRNZXRob2QoJ2dldFdhbGxldEFkZHJlc3MnKShcbiAgICAgICAgaW5wdXRzW2ldLmFkZHJlc3MsXG4gICAgICApO1xuICAgICAgY29uc3Qga2V5UGFpciA9IGF3YWl0IHRoaXMuZ2V0TWV0aG9kKCdrZXlQYWlyJykod2FsbGV0LmRlcml2YXRpb25QYXRoKTtcbiAgICAgIGNvbnN0IHBheW1lbnRWYXJpYW50ID0gdGhpcy5nZXRNZXRob2QoJ2dldFBheW1lbnRWYXJpYW50RnJvbVB1YmxpY0tleScpKFxuICAgICAgICBrZXlQYWlyLnB1YmxpY0tleSxcbiAgICAgICk7XG5cbiAgICAgIHR4Yi5hZGRJbnB1dChpbnB1dHNbaV0udHhpZCwgaW5wdXRzW2ldLnZvdXQsIDAsIHBheW1lbnRWYXJpYW50Lm91dHB1dCk7XG4gICAgfVxuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBpbnB1dHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHdhbGxldCA9IGF3YWl0IHRoaXMuZ2V0TWV0aG9kKCdnZXRXYWxsZXRBZGRyZXNzJykoXG4gICAgICAgIGlucHV0c1tpXS5hZGRyZXNzLFxuICAgICAgKTtcbiAgICAgIGNvbnN0IGtleVBhaXIgPSBhd2FpdCB0aGlzLmdldE1ldGhvZCgna2V5UGFpcicpKHdhbGxldC5kZXJpdmF0aW9uUGF0aCk7XG4gICAgICBjb25zdCBwYXltZW50VmFyaWFudCA9IHRoaXMuZ2V0TWV0aG9kKCdnZXRQYXltZW50VmFyaWFudEZyb21QdWJsaWNLZXknKShcbiAgICAgICAga2V5UGFpci5wdWJsaWNLZXksXG4gICAgICApO1xuICAgICAgY29uc3QgbmVlZHNXaXRuZXNzID0gdHJ1ZTtcblxuICAgICAgY29uc3Qgc2lnblBhcmFtcyA9IHtcbiAgICAgICAgcHJldk91dFNjcmlwdFR5cGUsXG4gICAgICAgIHZpbjogaSxcbiAgICAgICAga2V5UGFpcixcbiAgICAgICAgd2l0bmVzc1ZhbHVlOiAwLFxuICAgICAgfTtcblxuICAgICAgaWYgKG5lZWRzV2l0bmVzcykge1xuICAgICAgICBzaWduUGFyYW1zLndpdG5lc3NWYWx1ZSA9IGlucHV0c1tpXS52YWx1ZTtcbiAgICAgIH1cblxuICAgICAgdHhiLnNpZ24oc2lnblBhcmFtcyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHsgaGV4OiB0eGIuYnVpbGQoKS50b0hleCgpLCBmZWUgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBxdWlja0dldEFkZHJlc3NlcyBpcyBhbiBvcHRpbWl6ZWQgdmVyc2lvbiBvZiBnZXRBZGRyZXNzZXMuXG4gICAqIEl0IHJlbW92ZXMgdGhlIGNhbGwgdG8gYGFzeW5jU2V0SW1tZWRpYXRlKClgLCBzcGVlZGluZyB1cCB0aGUgZnVuY3Rpb24gYnkgYSBmYWN0b3Igb2YgNnguXG4gICAqXG4gICAqIEBwYXJhbSBzdGFydGluZ0luZGV4XG4gICAqIEBwYXJhbSBudW1BZGRyZXNzZXNcbiAgICogQHBhcmFtIGNoYW5nZVxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxBZGRyZXNzW10+fVxuICAgKi9cbiAgYXN5bmMgcXVpY2tHZXRBZGRyZXNzZXMoXG4gICAgc3RhcnRpbmdJbmRleCA9IDAsXG4gICAgbnVtQWRkcmVzc2VzID0gMSxcbiAgICBjaGFuZ2UgPSBmYWxzZSxcbiAgKTogUHJvbWlzZTxBZGRyZXNzW10+IHtcbiAgICBpZiAobnVtQWRkcmVzc2VzIDwgMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdZb3UgbXVzdCByZXR1cm4gYXQgbGVhc3Qgb25lIGFkZHJlc3MnKTtcbiAgICB9XG5cbiAgICBjb25zdCBhZGRyZXNzZXMgPSBbXTtcbiAgICBjb25zdCBsYXN0SW5kZXggPSBzdGFydGluZ0luZGV4ICsgbnVtQWRkcmVzc2VzO1xuICAgIGNvbnN0IGNoYW5nZVZhbCA9IGNoYW5nZSA/ICcxJyA6ICcwJztcblxuICAgIC8vIE9yaWdpbmFsIHdhbGxldCBwcm92aWRlciBpcyBmZXRjaGVkIHRvIGdldCB0aGUgYmFzZSBkZXJpdmF0aW9uIHBhdGhcbiAgICBjb25zdCBvcmlnaW5hbFByb3ZpZGVyID0gdGhpcy5jbGllbnQuZ2V0UHJvdmlkZXJGb3JNZXRob2QoJ2dldEFkZHJlc3NlcycpO1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG4gICAgY29uc3QgYmFzZURlcml2YXRpb25QYXRoID0gKG9yaWdpbmFsUHJvdmlkZXIgYXMgYW55KVxuICAgICAgLl9iYXNlRGVyaXZhdGlvblBhdGggYXMgc3RyaW5nO1xuXG4gICAgY29uc3QgZ2V0RGVyaXZhdGlvblBhdGhBZGRyZXNzRm46IChcbiAgICAgIHBhdGg6IHN0cmluZyxcbiAgICApID0+IFByb21pc2U8QWRkcmVzcz4gPSB0aGlzLmNsaWVudC5nZXRNZXRob2QoJ2dldERlcml2YXRpb25QYXRoQWRkcmVzcycpO1xuXG4gICAgZm9yIChcbiAgICAgIGxldCBjdXJyZW50SW5kZXggPSBzdGFydGluZ0luZGV4O1xuICAgICAgY3VycmVudEluZGV4IDwgbGFzdEluZGV4O1xuICAgICAgY3VycmVudEluZGV4KytcbiAgICApIHtcbiAgICAgIGNvbnN0IHN1YlBhdGggPSBjaGFuZ2VWYWwgKyAnLycgKyBjdXJyZW50SW5kZXg7XG4gICAgICBjb25zdCBwYXRoID0gYmFzZURlcml2YXRpb25QYXRoICsgJy8nICsgc3ViUGF0aDtcbiAgICAgIGNvbnN0IGFkZHJlc3NPYmplY3QgPSBhd2FpdCBnZXREZXJpdmF0aW9uUGF0aEFkZHJlc3NGbihwYXRoKTtcbiAgICAgIGFkZHJlc3Nlcy5wdXNoKGFkZHJlc3NPYmplY3QpO1xuICAgIH1cblxuICAgIHJldHVybiBhZGRyZXNzZXM7XG4gIH1cblxuICAvKipcbiAgICogcXVpY2tGaW5kQWRkcmVzcyBpcyBhbiBvcHRpbWl6ZWQgdmVyc2lvbiBvZiBmaW5kQWRkcmVzcy5cbiAgICpcbiAgICogSXQgc2VhcmNoZXMgdGhyb3VnaCBib3RoIGNoYW5nZSBhbmQgbm9uLWNoYW5nZSBhZGRyZXNzZXMgZWFjaCBpdGVyYXRpb24uXG4gICAqXG4gICAqIFRoaXMgaXMgaW4gY29udHJhc3QgdG8gdGhlIG9yaWdpbmFsIGZpbmRBZGRyZXNzIGZ1bmN0aW9uIHdoaWNoIHNlYXJjaGVzXG4gICAqIHRocm91Z2ggYWxsIG5vbi1jaGFuZ2UgYWRkcmVzc2VzIGJlZm9yZSBtb3Zpbmcgb24gdG8gY2hhbmdlIGFkZHJlc3Nlcy5cbiAgICpcbiAgICogQHBhcmFtIGFkZHJlc3Nlc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxBZGRyZXNzW10+fVxuICAgKi9cbiAgYXN5bmMgcXVpY2tGaW5kQWRkcmVzcyhhZGRyZXNzZXM6IHN0cmluZ1tdKTogUHJvbWlzZTxBZGRyZXNzPiB7XG4gICAgY29uc3QgYWRkcmVzc2VzUGVyQ2FsbCA9IDIwO1xuICAgIGxldCBpbmRleCA9IDA7XG5cbiAgICB3aGlsZSAoaW5kZXggPCB0aGlzLl9tYXhBZGRyZXNzZXNUb0Rlcml2ZSkge1xuICAgICAgY29uc3Qgd2FsbGV0Tm9uQ2hhbmdlQWRkcmVzc2VzID0gYXdhaXQgdGhpcy5xdWlja0dldEFkZHJlc3NlcyhcbiAgICAgICAgaW5kZXgsXG4gICAgICAgIGFkZHJlc3Nlc1BlckNhbGwsXG4gICAgICAgIHRydWUsXG4gICAgICApO1xuICAgICAgY29uc3Qgd2FsbGV0Q2hhbmdlQWRkcmVzc2VzID0gYXdhaXQgdGhpcy5xdWlja0dldEFkZHJlc3NlcyhcbiAgICAgICAgaW5kZXgsXG4gICAgICAgIGFkZHJlc3Nlc1BlckNhbGwsXG4gICAgICAgIGZhbHNlLFxuICAgICAgKTtcbiAgICAgIGNvbnN0IHdhbGxldEFkZHJlc3NlcyA9IFtcbiAgICAgICAgLi4ud2FsbGV0Tm9uQ2hhbmdlQWRkcmVzc2VzLFxuICAgICAgICAuLi53YWxsZXRDaGFuZ2VBZGRyZXNzZXMsXG4gICAgICBdO1xuICAgICAgY29uc3Qgd2FsbGV0QWRkcmVzcyA9IHdhbGxldEFkZHJlc3Nlcy5maW5kKCh3YWxsZXRBZGRyKSA9PlxuICAgICAgICBhZGRyZXNzZXMuZmluZCgoYWRkcikgPT4gd2FsbGV0QWRkci5hZGRyZXNzID09PSBhZGRyKSxcbiAgICAgICk7XG5cbiAgICAgIGlmICh3YWxsZXRBZGRyZXNzKSB7XG4gICAgICAgIC8vIEluY3JlbWVudCBtYXggYWRkcmVzc2VzIHRvIGRlcml2ZSBieSAxMDAgaWYgZm91bmQgd2l0aGluIDEwMCBhZGRyZXNzZXMgb2YgbWF4QWRkcmVzc2VzVG9EZXJpdmVcbiAgICAgICAgdGhpcy5fbWF4QWRkcmVzc2VzVG9EZXJpdmUgPSBNYXRoLm1heChcbiAgICAgICAgICB0aGlzLl9tYXhBZGRyZXNzZXNUb0Rlcml2ZSxcbiAgICAgICAgICBpbmRleCArIDEwMCxcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIHdhbGxldEFkZHJlc3M7XG4gICAgICB9XG4gICAgICBpbmRleCArPSBhZGRyZXNzZXNQZXJDYWxsO1xuICAgIH1cbiAgfVxufVxuIl0sIm5hbWVzIjpbImJpdGNvaW4iLCJGRUVfUEVSX0JZVEVfRkFMTEJBQ0siLCJBRERSRVNTX0dBUCIsIk5PTkNIQU5HRV9BRERSRVNTIiwiQ0hBTkdFX0FERFJFU1MiLCJOT05DSEFOR0VfT1JfQ0hBTkdFX0FERFJFU1MiLCJCaXRjb2luV2FsbGV0UHJvdmlkZXIiLCJQcm92aWRlciIsImJ1aWxkU3dlZXBUcmFuc2FjdGlvbldpdGhTZXRPdXRwdXRzIiwiZXh0ZXJuYWxDaGFuZ2VBZGRyZXNzIiwiZmVlUGVyQnl0ZSIsIl9vdXRwdXRzIiwiZml4ZWRJbnB1dHMiLCJfYnVpbGRTd2VlcFRyYW5zYWN0aW9uIiwiZ2V0VW51c2VkQWRkcmVzc2VzQmxhY2tsaXN0IiwiX3VudXNlZEFkZHJlc3Nlc0JsYWNrbGlzdCIsInNldFVudXNlZEFkZHJlc3Nlc0JsYWNrbGlzdCIsInVudXNlZEFkZHJlc3Nlc0JsYWNrbGlzdCIsInNldE1heEFkZHJlc3Nlc1RvRGVyaXZlIiwibWF4QWRkcmVzc2VzVG9EZXJpdmUiLCJfbWF4QWRkcmVzc2VzVG9EZXJpdmUiLCJnZXRNYXhBZGRyZXNzZXNUb0Rlcml2ZSIsIl9jcmVhdGVNdWx0aXNpZ1BheW1lbnQiLCJtIiwicHVia2V5cyIsImxlbmd0aCIsIkVycm9yIiwicDJtcyIsInBheW1lbnRzIiwibWFwIiwia2V5IiwiQnVmZmVyIiwiZnJvbSIsIm5ldHdvcmsiLCJfbmV0d29yayIsInAyd3NoIiwicmVkZWVtIiwiY3JlYXRlTXVsdGlzaWciLCJhZGRyZXNzIiwicmVkZWVtU2NyaXB0Iiwib3V0cHV0IiwidG9TdHJpbmciLCJidWlsZE11bHRpc2lnUFNCVCIsImlucHV0cyIsIm91dHB1dHMiLCJhc3NlcnQiLCJldmVyeSIsImlucHV0Iiwic2NyaXB0UHViS2V5IiwicHNidCIsIlBzYnQiLCJmb3JFYWNoIiwiYWRkSW5wdXQiLCJoYXNoIiwidHhpZCIsImluZGV4Iiwidm91dCIsIndpdG5lc3NVdHhvIiwic2NyaXB0IiwidmFsdWUiLCJ3aXRuZXNzU2NyaXB0IiwiYWRkT3V0cHV0IiwidG8iLCJ0b0Jhc2U2NCIsIndhbGxldFByb2Nlc3NQU0JUIiwicHNidFN0cmluZyIsImZyb21CYXNlNjQiLCJQcm9taXNlIiwiYWxsIiwiZGF0YSIsImkiLCJnZXRJbnB1dFR5cGUiLCJzbGljZSIsInNjcmlwdFN0YWNrIiwiZGVjb21waWxlIiwiZmlsdGVyIiwiaXNCdWZmZXIiLCJzZWNwMjU2azEiLCJwdWJsaWNLZXlWZXJpZnkiLCJhZGRyZXNzU3RyaW5nIiwicDJ3cGtoIiwicHVia2V5IiwicXVpY2tGaW5kQWRkcmVzcyIsImtleVBhaXIiLCJnZXRNZXRob2QiLCJkZXJpdmF0aW9uUGF0aCIsInNpZ25JbnB1dCIsInZhbGlkYXRlU2lnbmF0dXJlc09mQWxsSW5wdXRzIiwiZmluYWxpemVQU0JUIiwiZmluYWxpemVBbGxJbnB1dHMiLCJlcnJvciIsImNvbXBsZXRlIiwiaGV4IiwiZXh0cmFjdFRyYW5zYWN0aW9uIiwidG9IZXgiLCJnZXRVbnVzZWRBZGRyZXNzIiwiY2hhbmdlIiwibnVtQWRkcmVzc1BlckNhbGwiLCJhZGRyZXNzVHlwZSIsIl9nZXRVc2VkVW51c2VkQWRkcmVzc2VzIiwidGhlbiIsInVudXNlZEFkZHJlc3MiLCJ1c2VkQWRkcmVzc2VzIiwiYWRkcmVzc0NvdW50TWFwIiwibm9uQ2hhbmdlIiwidW51c2VkQWRkcmVzc01hcCIsImFkZHJMaXN0IiwiYWRkcmVzc0luZGV4IiwiY2hhbmdlQWRkcmVzc2VzIiwibm9uQ2hhbmdlQWRkcmVzc2VzIiwiY2xpZW50Iiwid2FsbGV0IiwiZ2V0QWRkcmVzc2VzIiwiY29uY2F0IiwicXVpY2tHZXRBZGRyZXNzZXMiLCJ0cmFuc2FjdGlvbkNvdW50cyIsImlzVXNlZCIsImlzQ2hhbmdlQWRkcmVzcyIsImZpbmQiLCJhIiwicHVzaCIsImZpcnN0VW51c2VkQWRkcmVzcyIsImluZGV4Tm9uQ2hhbmdlIiwiSW5maW5pdHkiLCJpbmRleENoYW5nZSIsInNlbmRTd2VlcFRyYW5zYWN0aW9uV2l0aFNldE91dHB1dHMiLCJmZWUiLCJub3JtYWxpemVUcmFuc2FjdGlvbk9iamVjdCIsImRlY29kZVJhd1RyYW5zYWN0aW9uIiwiX2ZlZVBlckJ5dGUiLCJpbnB1dHNGb3JBbW91bnQiLCJlIiwiX2dldElucHV0Rm9yQW1vdW50V2l0aG91dFV0eG9DaGVjayIsInV0eG8iLCJJbnB1dCIsImZyb21VVFhPIiwic3BsaWNlSW5kZXgiLCJmaW5kSW5kZXgiLCJzd2VlcE91dHB1dCIsInNwbGljZSIsIl9idWlsZFRyYW5zYWN0aW9uV2l0aG91dFV0eG9DaGVjayIsInV0eG9CYWxhbmNlIiwicmVkdWNlIiwiYiIsIm91dHB1dEJhbGFuY2UiLCJhbW91bnRUb1NlbmQiLCJ0YXJnZXRzIiwidGFyZ2V0IiwiaWQiLCJzZWxlY3RDb2lucyIsIk1hdGgiLCJjZWlsIiwidHhiIiwiVHJhbnNhY3Rpb25CdWlsZGVyIiwicHJldk91dFNjcmlwdFR5cGUiLCJwYXltZW50VmFyaWFudCIsInB1YmxpY0tleSIsIm5lZWRzV2l0bmVzcyIsInNpZ25QYXJhbXMiLCJ2aW4iLCJ3aXRuZXNzVmFsdWUiLCJzaWduIiwiYnVpbGQiLCJzdGFydGluZ0luZGV4IiwibnVtQWRkcmVzc2VzIiwiYWRkcmVzc2VzIiwibGFzdEluZGV4IiwiY2hhbmdlVmFsIiwib3JpZ2luYWxQcm92aWRlciIsImdldFByb3ZpZGVyRm9yTWV0aG9kIiwiYmFzZURlcml2YXRpb25QYXRoIiwiX2Jhc2VEZXJpdmF0aW9uUGF0aCIsImdldERlcml2YXRpb25QYXRoQWRkcmVzc0ZuIiwiY3VycmVudEluZGV4Iiwic3ViUGF0aCIsInBhdGgiLCJhZGRyZXNzT2JqZWN0IiwiYWRkcmVzc2VzUGVyQ2FsbCIsIndhbGxldE5vbkNoYW5nZUFkZHJlc3NlcyIsIndhbGxldENoYW5nZUFkZHJlc3NlcyIsIndhbGxldEFkZHJlc3NlcyIsIndhbGxldEFkZHJlc3MiLCJ3YWxs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
500
+ //# sourceMappingURL=BitcoinWalletProvider.js.map