eulith-web3js-core
Version:
Eulith core web3js SDK (code to access Eulith services via web3js)
401 lines • 54.1 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AtomicTx = void 0;
const web3_1 = __importDefault(require("web3"));
const crypto = require("crypto"); // import crypto doesn't work from (at least some) reactjs browser apps
const Eulith = __importStar(require("../src/index"));
/**
* APIS relating to Atomic Transactions
*/
var AtomicTx;
(function (AtomicTx) {
/**
* @typedef Eulith.AtomicTx.Transaction - a root transaction
*
* \brief An atomic transaction is a bundle of transactions that all execute at the same time.
*
* Key properties of atomicity:
* o Everything fails or everything succeeds --> If at any point in your series of transactions something goes wrong, everything reverts. This is especially useful if you're making a trade that has upstream dependencies.
* o Everything is executed as a single unit --> It's not possible for someone else to take action in the middle of your execution. The state of the blockchain is frozen at the start of your transaction and is only mutable by your own actions. As far as the blockchain is concerned, your atomic unit is a single transaction.
* o More efficient usage of EVM storage --> Could result in cheaper transactions.
*
* \see <https://docs.eulith.com/v/srG7S9J4U0bx5OMNR41S/implementation-details/atomic-transactions>
*
* Constructing the transaction object starts the transaction.
* Calling commit() or rollback() renders this object unusable again, so
* the caller should create a new one for a second transaction.
*
* Should the caller need to perform additional operations 'in the context' of the
* transaction (such as calls to eulith_swap_quote), use the Eulith.Provider from
* this.provider()
*
* Example Code
* ~~~
* const atomicTransaction = new Eulith.AtomicTx.Transaction({web3: ew3, signer: acct});
* atomicTransaction.addTransaction({ 'from': acct.address, 'to': await one.address, 'value': 12131415 });
* const combinedTransactionAsTxParams = await atomicTransaction.commit()
* const txHash: string = await provider.signAndSendTransaction(combinedTransactionAsTxParams, acct);
* const txReceipt: TransactionReceipt = await ew3.eth.getTransactionReceipt(txHash);
* ~~~
*
* Example Code
* ~~~
* const atomicTransaction = new Eulith.AtomicTx.Transaction({web3: ew3, signer: acct});
* atomicTransaction.addTransaction({ 'from': acct.address, 'to': one.address, 'value': 12131415 });
* const txReceipt = await atomicTx.commitAndSendAndWait({timeoutMS: 10*1000});
* ~~~
*
* An AtomicTx - internally - uses a 'proxy' contract to implement the actual steps of an atomic transaction.
* Since this contract sometimes needs to be setup before the start of any AtomicTx using it, the caller
* may preconstruct it (for use in approves, for example). They then may optionally pass it to the
* AtomicTx constructor to avoid having it recomputed.
*
* if signer provided, its address must match that given by the (then optional) accountAddress
*/
class Transaction {
/**
* Begin an atomic transaction.
*
* Atomic transactions, are implemented via a proxy contract instance which typically must
* be 'approved' for operations.
*
* This class automatically manages construction of that proxy object, if needed, though it can be specifically
* passed into the constructor, for performance reasons.
*
* Example Usage:
* ~~~
* const atomicTx = new Transaction({web3: ew3, accountAddress: acct.address});
* ~~~
*
* Example Usage:
* ~~~
* const agentContractAddress = await Eulith.OnChainAgents.contractAddress({ provider, authorizedSigner: acct });
* await tokenContract
* .approve(agentContractAddress, tokenContract.asTokenValue(borrowAmountA * 1.2), { from: acct.address })
* .signAndSendAndWait(acct, provider);
* const atomicTx = await Eulith.AtomicTx.Transaction({ provider, signer: acct, agentContractAddress });
* ~~~
*
* @param provider - typically Eulith.Provider object OR Eulith.Web3
* @param accountAddress - required - transactions must be signed, and this tells the address of the signer
* @param agentContractAddress - optional - generally omitted, and computed automatically - refers to the contract address of the on-chain agent implementing this atomic transaction
* @param gnosis - tbd ;-)
*/
constructor({ provider, signer, accountAddress, agentContractAddress, gnosis }) {
this.ongoingTransactions_ = [];
let useProvider = Eulith.Provider.ProviderOrWeb3(provider);
// @todo cleanup addURLAdditionsIf code = too complex setting addr
useProvider.addURLAdditionsIf({
auth_address: accountAddress !== null && accountAddress !== void 0 ? accountAddress : Eulith.Signing.SigningService.assure(signer, useProvider).address
});
const useSigner = signer == null ? null : Eulith.Signing.SigningService.assure(signer, useProvider);
this.inTransaction_ = true;
if (accountAddress === undefined) {
if (signer === undefined) {
throw new Error("AtomicTx.Transaction constructor needs either accountAddress or signer");
}
else {
accountAddress = useSigner.address;
}
}
this.TXID_ = crypto.randomUUID();
const wireFormatParams = {
auth_address: accountAddress,
atomic_tx_id: this.TXID_
};
if (gnosis) {
wireFormatParams["gnosis_address"] = gnosis;
}
if (useProvider) {
useProvider = useProvider.cloneWithURLAdditions(wireFormatParams);
}
else {
throw new Error("Need provider or web3");
}
this.web3_ = new Eulith.Web3({
provider: useProvider,
signer: useSigner
});
if (agentContractAddress === undefined) {
this.agentContractAddress_ = Eulith.OnChainAgents.contractAddress({
provider: this.web3_.provider,
authorizedAddress: accountAddress
});
}
else {
function f() {
return __awaiter(this, void 0, void 0, function* () {
return agentContractAddress;
});
}
this.agentContractAddress_ = f();
}
this.logger_ = provider === null || provider === void 0 ? void 0 : provider.logger;
}
/**
* Since atomic transactions are implemented by having an onChain Agent contract do all the operations,
* it often must be 'approved' to do those operations, so the caller will need to know its address.
*/
get agentContractAddress() {
return this.agentContractAddress_;
}
/**
* Accumulate the argument transactionConfig into a server-side object which,
* when this.commit() is called, will produce a single transaction which combines
* the added sub-transactions.
*
* @todo SINCE we don't implement any (much) server side logic to react to partially
* completed transactions (like in a database) - I wonder why this is useful todo
* server side. That's probably worth DOCUMENTING. Otherwise, we could just accumulate
* an array client side and send that.
*
* @param transactionConfig
* @returns
*/
addTransaction(transactionConfig) {
return __awaiter(this, void 0, void 0, function* () {
yield this.agentContractAddress_; // so server knows about the account
const txR = this.web3_.eulith_send_unsigned_transaction(transactionConfig);
this.ongoingTransactions_.push(txR);
return yield txR;
});
}
/**
* Adds multiple transactions, and returns all their hashed txHash results.
* Equivilent to a series sof calls to addTransaction, but waiting for each in turn.
*
* \note - this means that order is preserved between the transactions added, even
* if the caller fails to wait on this promise.
*/
addTransactions(txs) {
return __awaiter(this, void 0, void 0, function* () {
let results = [];
for (const tx of txs) {
const txHash = yield this.addTransaction(tx);
results.push(txHash);
}
return results;
});
}
/**
* Not needed, but for debugging purposes maybe handy, and certainly used internally.
*/
get provider() {
return this.web3_.provider;
}
/**
* Not needed, but for debugging purposes maybe handy
*/
get atomicTxID() {
return this.TXID_;
}
/**
* \note This replaces the python ew3.v0.commit_atomic_transaction()
*
* \see also commitAndSendAndWait - often a better choice
*
* Example Usage:
* ~~~
* const atomicTx = new AtomicTx.Transaction({web3: ew3, accountAddress: acct.address});
* // do something with atmoictx - adding transactions or .provider and hand to something else
* const txReceipt: TransactionReceipt = await atomicTx.commitAndSendAndWait({ timeoutMS: 10 * 1000, extraTXParams2Merge: { gas: 1000000 } })
* expect(txReceipt.status).toBeTruthy();
* ~~~
*
* This (automatically) waits for any transactions added (so the caller doesnt need to
* wait on the async results)
*
* Then, combines all the transactions attempted, and produces a new TransactionConfig
* which, if signed and sent to the server, will complete the (combined atomic) transaction.
*/
commit() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.inTransaction_) {
throw new Error("Cannot call commit on transaction that is already comitted");
}
this.inTransaction_ = false;
yield Promise.all(this.ongoingTransactions_);
return yield this.provider.request({ method: "eulith_commit", params: [] });
});
}
/**
* \note This replaces the python ew3.v0.commit_atomic_transaction() followed by ew3.eth.send_transaction(txparams) and ew3.eth.wait_for_transaction_receipt(tx)
*
* \brief shorthand for commit(), and signing and sending that tx, and waiting for its txReceipt all in one
*
* \note - requires a signer attached to the provider object used to create the transaction.
*
* \note - this takes a timeoutMS, and repeatedly calls getTransactionReceipt, until the timeout
* expires (or receipt received). This differs from the default behavior of getTransactionReceipt
*
* \todo Discuss if this should throw on failure? or provide overload/variant that does
*/
commitAndSendAndWait(args) {
return __awaiter(this, void 0, void 0, function* () {
const timeoutMS = args === null || args === void 0 ? void 0 : args.timeoutMS;
const extraTXParams2Merge = args === null || args === void 0 ? void 0 : args.extraTXParams2Merge;
if (this.web3_.signer == null) {
Eulith.Exceptions.API.Throw(this.logger_, {
message: "Cannot AtomicTx.commitAndSendAndWait without an associated signer"
});
}
let combinedTransactionAsTxParams = yield this.commit();
if (extraTXParams2Merge) {
combinedTransactionAsTxParams = Object.assign(Object.assign({}, combinedTransactionAsTxParams), extraTXParams2Merge);
}
const txHash = yield this.provider.signAndSendTransaction(combinedTransactionAsTxParams, this.web3_.signer);
return yield Eulith.Utils.waitForTxReceipt({
logger: this.logger_,
provider: this.provider,
txHash: txHash,
timeoutInMS: timeoutMS
});
});
}
/**
*/
rollback() {
return __awaiter(this, void 0, void 0, function* () {
/*
* The blockchain and our server are two different layers of services. When you call commit on our server,
* it flushes the accumulated tx and serializes it. This output then has to be signed and
* sent to the chain in order to be confirmed. If you call commit and do nothing with the serialized output,
* it's effectively flushing the tx and throwing it in the garbage, hence the rollback functionality.
*
* since rollback doesnt return the transaction, it has nothing to forward with sendTransaction
*/
yield this.commit();
});
}
}
AtomicTx.Transaction = Transaction;
/**
* @typedef Eulith.AtomicTx.NestedTransaction
*
* \brief This acts quite similarly to a AtomicTx (and someday maybe merged with that)
*
* It acts as a nested transaction.
*
* Example Usage:
* ~~~
* const atomicTx = new Eulith.AtomicTx.Transaction({ provider, signer: acct });
*
* const swapAtomicTx: Eulith.AtomicTx.NestedTransaction = await Eulith.Uniswap.startSwap({
* request: quote.swapRequest,
* parentTx: atomicTx
* });
* await swapAtomicTx.commit(); // NOTE: critical to await here, before commiting the parent transaction!
*
* // Commit, sign, and complete the operation - this will only return if/when the entire operation succeeeds (and throws on any part failing)
* await atomicTx.commitAndSendAndWait();
* ~~~
*/
class NestedTransaction {
/**
* NestedTransaction can be nested inside AtomicTx.Transaction or another NestedTransaction
* (so really should consider merging this with AtomicTx)
*/
constructor({ parentTx }) {
this.ongoingTransactions_ = [];
this.web3_ = new Eulith.Web3({ provider: parentTx.provider });
}
/**
* \see IAtomicTx.provider
*/
get provider() {
return this.web3_.provider;
}
/**
* Accumulate the argument transactionConfig into a server-side object which,
* when this.commit() is called, will produce a single transaction which combines
* the added sub-transactions.
*
* @todo SINCE we don't implement any (much) server side logic to react to partially
* completed transactions (like in a database) - I wonder why this is useful todo
* server side. That's probably worth DOCUMENTING. Otherwise, we could just accumulate
* an array client side and send that.
*
* @param transactionConfig
* @returns
*/
addTransaction(transactionConfig) {
return __awaiter(this, void 0, void 0, function* () {
const txR = this.web3_.eulith_send_unsigned_transaction(transactionConfig);
this.ongoingTransactions_.push(txR);
return yield txR;
});
}
/**
* Adds multiple transactions, and returns all their hashed txHash results.
* Equivilent to a series sof calls to addTransaction, but waiting for each in turn.
*
* \note - this means that order is preserved between the transactions added, even
* if the caller fails to wait on this promise.
*/
addTransactions(txs) {
return __awaiter(this, void 0, void 0, function* () {
let results = [];
for (const tx of txs) {
const txHash = yield this.addTransaction(tx);
results.push(txHash);
}
return results;
});
}
/**
* Complete (atomically) this sub-step of the parent atomic transaction.
*
* This doesn't actually do the step, but marks as completed the sequence of operations
* and returns the (1-based) index of this step in the parent atomic transaction.
*
* Note: that returned index is not generally useful, but can be used to help diagnose problems
* in advanced applications.
*/
commit() {
return __awaiter(this, void 0, void 0, function* () {
const res = yield this.provider.request({
method: "eulith_finish_inner",
params: []
});
return web3_1.default.utils.toNumber(res);
});
}
}
AtomicTx.NestedTransaction = NestedTransaction;
})(AtomicTx = exports.AtomicTx || (exports.AtomicTx = {}));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXRvbWljX3R4LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2F0b21pY190eC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLGdEQUF3QjtBQUV4QixNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyx1RUFBdUU7QUFFekcscURBQXVDO0FBT3ZDOztHQUVHO0FBQ0gsSUFBYyxRQUFRLENBbWFyQjtBQW5hRCxXQUFjLFFBQVE7SUE4Q2xCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0EwQ0c7SUFDSCxNQUFhLFdBQVc7UUFDcEI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztXQTJCRztRQUNILFlBQW1CLEVBQ2YsUUFBUSxFQUNSLE1BQU0sRUFDTixjQUFjLEVBQ2Qsb0JBQW9CLEVBQ3BCLE1BQU0sRUFPVDtZQWlNTyx5QkFBb0IsR0FBc0IsRUFBRSxDQUFDO1lBaE1qRCxJQUFJLFdBQVcsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUMzRCxrRUFBa0U7WUFDbEUsV0FBVyxDQUFDLGlCQUFpQixDQUFDO2dCQUMxQixZQUFZLEVBQUUsY0FBYyxhQUFkLGNBQWMsY0FBZCxjQUFjLEdBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxXQUFXLENBQUMsQ0FBQyxPQUFPO2FBQ3BHLENBQUMsQ0FBQztZQUNILE1BQU0sU0FBUyxHQUFHLE1BQU0sSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxXQUFXLENBQUMsQ0FBQztZQUNwRyxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQztZ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LElBQUksQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDLENBQUM7b0JBQ3JELE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7aUJBQ3hCO2dCQUNELE9BQU8sT0FBTyxDQUFDO1lBQ25CLENBQUM7U0FBQTtRQUVEOzs7Ozs7OztXQVFHO1FBQ1UsTUFBTTs7Z0JBQ2YsTUFBTSxHQUFHLEdBQUcsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQztvQkFDcEMsTUFBTSxFQUFFLHFCQUFxQjtvQkFDN0IsTUFBTSxFQUFFLEVBQUU7aUJBQ2IsQ0FBQyxDQUFDO2dCQUNILE9BQU8sY0FBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFXLENBQUM7WUFDOUMsQ0FBQztTQUFBO0tBSUo7SUF0RVksMEJBQWlCLG9CQXNFN0IsQ0FBQTtBQUNMLENBQUMsRUFuYWEsUUFBUSxHQUFSLGdCQUFRLEtBQVIsZ0JBQVEsUUFtYXJCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFdlYjMgZnJvbSBcIndlYjNcIjtcbmltcG9ydCB7IFRyYW5zYWN0aW9uQ29uZmlnLCBUcmFuc2FjdGlvblJlY2VpcHQgfSBmcm9tIFwid2ViMy1ldGhcIjtcbmNvbnN0IGNyeXB0byA9IHJlcXVpcmUoXCJjcnlwdG9cIik7IC8vIGltcG9ydCBjcnlwdG8gZG9lc24ndCB3b3JrIGZyb20gKGF0IGxlYXN0IHNvbWUpIHJlYWN0anMgYnJvd3NlciBhcHBzXG5cbmltcG9ydCAqIGFzIEV1bGl0aCBmcm9tIFwiLi4vc3JjL2luZGV4XCI7XG5cbmludGVyZmFjZSBjb21taXRBbmRTZW5kQW5kV2FpdEFyZ3Mge1xuICAgIHRpbWVvdXRNUz86IG51bWJlcjtcbiAgICBleHRyYVRYUGFyYW1zMk1lcmdlPzogVHJhbnNhY3Rpb25Db25maWc7XG59XG5cbi8qKlxuICogIEFQSVMgcmVsYXRpbmcgdG8gQXRvbWljIFRyYW5zYWN0aW9uc1xuICovXG5leHBvcnQgbW9kdWxlIEF0b21pY1R4IHtcbiAgICAvKipcbiAgICAgKiAgQHRvZG8gQ09OU0lERVIgaWYgSUF0b21pY1R4IHNob3VsZCBoYXZlIGEgY29tbWl0IG1ldGhvZCwgYW5kIHdoYXQgaXQgc2hvdWxkIHJldHVyblxuICAgICAqICAgICAgUmVhc29uYWJsZSBhbnN3ZXJzOlxuICAgICAqICAgICAgICAgIG8gICBubyAtIGp1c3QgZG8gaW4gc3ViY2xhc3Nlc1xuICAgICAqICAgICAgICAgIG8gICBZZXMsIHJldHVybiB2b2lkfFRyYW5zYWN0aW9uQ29uZmlnLCBkZXBlbmRpbmcgb24gaWYgdHggaXMgb3V0ZXJtb3N0IG9yIG5vdC5cbiAgICAgKiAgICAgICAgICBvICAgbWF5YmUgcmV0dXJuICMgb3IgfCBUcmFuc2FjdGlvbkNvbmZpZyAtIG9uY2UgSSB1bmRlcnN0YW5kIHBvaW50IG9mIGV2ZXIgcmV0dXJuaW5nIG51bWJlclxuICAgICAqXG4gICAgICogIEB0b2RvIGNvbnNpZGVyIGFkZGluZyBMRVZFTCBwcm9wZXJ0eSB0byBiZSByZXR1cm5lZC5cbiAgICAgKi9cbiAgICBleHBvcnQgaW50ZXJmYWNlIElBdG9taWNUeCB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiAgQWNjdW11bGF0ZSB0aGUgYXJndW1lbnQgdHJhbnNhY3Rpb25Db25maWcgaW50byBhIHNlcnZlci1zaWRlIG9iamVjdCB3aGljaCxcbiAgICAgICAgICogIHdoZW4gdGhpcy5jb21taXQoKSBpcyBjYWxsZWQsIHdpbGwgcHJvZHVjZSBhIHNpbmdsZSB0cmFuc2FjdGlvbiB3aGljaCBjb21iaW5lc1xuICAgICAgICAgKiAgdGhlIGFkZGVkIHN1Yi10cmFuc2FjdGlvbnMuXG4gICAgICAgICAqXG4gICAgICAgICAqICBAdG9kbyBTSU5DRSB3ZSBkb24ndCBpbXBsZW1lbnQgYW55IChtdWNoKSBzZXJ2ZXIgc2lkZSBsb2dpYyB0byByZWFjdCB0byBwYXJ0aWFsbHlcbiAgICAgICAgICogIGNvbXBsZXRlZCB0cmFuc2FjdGlvbnMgKGxpa2UgaW4gYSBkYXRhYmFzZSkgLSBJIHdvbmRlciB3aHkgdGhpcyBpcyB1c2VmdWwgdG9kb1xuICAgICAgICAgKiAgc2VydmVyIHNpZGUuIFRoYXQncyBwcm9iYWJseSB3b3J0aCBET0NVTUVOVElORy4gT3RoZXJ3aXNlLCB3ZSBjb3VsZCBqdXN0IGFjY3VtdWxhdGVcbiAgICAgICAgICogIGFuIGFycmF5IGNsaWVudCBzaWRlIGFuZCBzZW5kIHRoYXQuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSB0cmFuc2FjdGlvbkNvbmZpZ1xuICAgICAgICAgKiBAcmV0dXJuc1xuICAgICAgICAgKi9cbiAgICAgICAgYWRkVHJhbnNhY3Rpb24odHJhbnNhY3Rpb25Db25maWc6IFRyYW5zYWN0aW9uQ29uZmlnKTogUHJvbWlzZTxzdHJpbmc+O1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiAgQWRkcyBtdWx0aXBsZSB0cmFuc2FjdGlvbnMsIGFuZCByZXR1cm5zIGFsbCB0aGVpciBoYXNoZWQgdHhIYXNoIHJlc3VsdHMuXG4gICAgICAgICAqICBFcXVpdmlsZW50IHRvIGEgc2VyaWVzIHNvZiBjYWxscyB0byBhZGRUcmFuc2FjdGlvbiwgYnV0IHdhaXRpbmcgZm9yIGVhY2ggaW4gdHVybi5cbiAgICAgICAgICpcbiAgICAgICAgICogIFxcbm90ZSAtIHRoaXMgbWVhbnMgdGhhdCBvcmRlciBpcyBwcmVzZXJ2ZWQgYmV0d2VlbiB0aGUgdHJhbnNhY3Rpb25zIGFkZGVkLCBldmVuXG4gICAgICAgICAqICBpZiB0aGUgY2FsbGVyIGZhaWxzIHRvIHdhaXQgb24gdGhpcyBwcm9taXNlLlxuICAgICAgICAgKi9cbiAgICAgICAgYWRkVHJhbnNhY3Rpb25zKHR4czogVHJhbnNhY3Rpb25Db25maWdbXSk6IFByb21pc2U8c3RyaW5nW10+O1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKlxuICAgICAgICAgKi9cbiAgICAgICAgcmVhZG9ubHkgcHJvdmlkZXI6IEV1bGl0aC5Qcm92aWRlcjtcblxuICAgICAgICAvKipcbiAgICAgICAgICogIE5vdCBuZWVkZWQsIGJ1dCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzIG1heWJlIGhhbmR5XG4gICAgICAgICAqL1xuICAgICAgICByZWFkb25seSBhdG9taWNUeElEOiBzdHJpbmc7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogIEB0eXBlZGVmIEV1bGl0aC5BdG9taWNUeC5UcmFuc2FjdGlvbiAtIGEgcm9vdCB0cmFuc2FjdGlvblxuICAgICAqXG4gICAgICogXFxicmllZiBBbiBhdG9taWMgdHJhbnNhY3Rpb24gaXMgYSBidW5kbGUgb2YgdHJhbnNhY3Rpb25zIHRoYXQgYWxsIGV4ZWN1dGUgYXQgdGhlIHNhbWUgdGltZS5cbiAgICAgKlxuICAgICAqIEtleSBwcm9wZXJ0aWVzIG9mIGF0b21pY2l0eTpcbiAgICAgKiAgICAgbyAgICBFdmVyeXRoaW5nIGZhaWxzIG9yIGV2ZXJ5dGhpbmcgc3VjY2VlZHMgLS0+IElmIGF0IGFueSBwb2ludCBpbiB5b3VyIHNlcmllcyBvZiB0cmFuc2FjdGlvbnMgc29tZXRoaW5nIGdvZXMgd3JvbmcsIGV2ZXJ5dGhpbmcgcmV2ZXJ0cy4gVGhpcyBpcyBlc3BlY2lhbGx5IHVzZWZ1bCBpZiB5b3UncmUgbWFraW5nIGEgdHJhZGUgdGhhdCBoYXMgdXBzdHJlYW0gZGVwZW5kZW5jaWVzLlxuICAgICAqICAgICBvICAgIEV2ZXJ5dGhpbmcgaXMgZXhlY3V0ZWQgYXMgYSBzaW5nbGUgdW5pdCAtLT4gSXQncyBub3QgcG9zc2libGUgZm9yIHNvbWVvbmUgZWxzZSB0byB0YWtlIGFjdGlvbiBpbiB0aGUgbWlkZGxlIG9mIHlvdXIgZXhlY3V0aW9uLiBUaGUgc3RhdGUgb2YgdGhlIGJsb2NrY2hhaW4gaXMgZnJvemVuIGF0IHRoZSBzdGFydCBvZiB5b3VyIHRyYW5zYWN0aW9uIGFuZCBpcyBvbmx5IG11dGFibGUgYnkgeW91ciBvd24gYWN0aW9ucy4gQXMgZmFyIGFzIHRoZSBibG9ja2NoYWluIGlzIGNvbmNlcm5lZCwgeW91ciBhdG9taWMgdW5pdCBpcyBhIHNpbmdsZSB0cmFuc2FjdGlvbi5cbiAgICAgKiAgICAgbyAgICBNb3JlIGVmZmljaWVudCB1c2FnZSBvZiBFVk0gc3RvcmFnZSAtLT4gQ291bGQgcmVzdWx0IGluIGNoZWFwZXIgdHJhbnNhY3Rpb25zLlxuICAgICAqXG4gICAgICogIFxcc2VlIDxodHRwczovL2RvY3MuZXVsaXRoLmNvbS92L3NyRzdTOUo0VTBieDVPTU5SNDFTL2ltcGxlbWVudGF0aW9uLWRldGFpbHMvYXRvbWljLXRyYW5zYWN0aW9ucz5cbiAgICAgKlxuICAgICAqICBDb25zdHJ1Y3RpbmcgdGhlIHRyYW5zYWN0aW9uIG9iamVjdCBzdGFydHMgdGhlIHRyYW5zYWN0aW9uLlxuICAgICAqICBDYWxsaW5nIGNvbW1pdCgpIG9yIHJvbGxiYWNrKCkgcmVuZGVycyB0aGlzIG9iamVjdCB1bnVzYWJsZSBhZ2Fpbiwgc29cbiAgICAgKiAgdGhlIGNhbGxlciBzaG91bGQgY3JlYXRlIGEgbmV3IG9uZSBmb3IgYSBzZWNvbmQgdHJhbnNhY3Rpb24uXG4gICAgICpcbiAgICAgKiAgU2hvdWxkIHRoZSBjYWxsZXIgbmVlZCB0byBwZXJmb3JtIGFkZGl0aW9uYWwgb3BlcmF0aW9ucyAnaW4gdGhlIGNvbnRleHQnIG9mIHRoZVxuICAgICAqICB0cmFuc2FjdGlvbiAoc3VjaCBhcyBjYWxscyB0byBldWxpdGhfc3dhcF9xdW90ZSksIHVzZSB0aGUgRXVsaXRoLlByb3ZpZGVyIGZyb21cbiAgICAgKiAgdGhpcy5wcm92aWRlcigpXG4gICAgICpcbiAgICAgKiAgRXhhbXBsZSBDb2RlXG4gICAgICogICAgICB+fn5cbiAgICAgKiAgICAgICAgICBjb25zdCBhdG9taWNUcmFuc2FjdGlvbiA9IG5ldyBFdWxpdGguQXRvbWljVHguVHJhbnNhY3Rpb24oe3dlYjM6IGV3Mywgc2lnbmVyOiBhY2N0fSk7XG4gICAgICogICAgICAgICAgYXRvbWljVHJhbnNhY3Rpb24uYWRkVHJhbnNhY3Rpb24oeyAnZnJvbSc6ICBhY2N0LmFkZHJlc3MsICd0byc6IGF3YWl0IG9uZS5hZGRyZXNzLCAndmFsdWUnOiAxMjEzMTQxNSB9KTtcbiAgICAgKiAgICAgICAgICBjb25zdCBjb21iaW5lZFRyYW5zYWN0aW9uQXNUeFBhcmFtcyA9IGF3YWl0IGF0b21pY1RyYW5zYWN0aW9uLmNvbW1pdCgpXG4gICAgICogICAgICAgICAgY29uc3QgdHhIYXNoOiBzdHJpbmcgPSBhd2FpdCBwcm92aWRlci5zaWduQW5kU2VuZFRyYW5zYWN0aW9uKGNvbWJpbmVkVHJhbnNhY3Rpb25Bc1R4UGFyYW1zLCBhY2N0KTtcbiAgICAgKiAgICAgICAgICBjb25zdCB0eFJlY2VpcHQ6IFRyYW5zYWN0aW9uUmVjZWlwdCA9IGF3YWl0IGV3My5ldGguZ2V0VHJhbnNhY3Rpb25SZWNlaXB0KHR4SGFzaCk7XG4gICAgICogICAgICB+fn5cbiAgICAgKlxuICAgICAqICBFeGFtcGxlIENvZGVcbiAgICAgKiAgICAgIH5+flxuICAgICAqICAgICAgICAgIGNvbnN0IGF0b21pY1RyYW5zYWN0aW9uID0gbmV3IEV1bGl0aC5BdG9taWNUeC5UcmFuc2FjdGlvbih7d2ViMzogZXczLCBzaWduZXI6IGFjY3R9KTtcbiAgICAgKiAgICAgICAgICBhdG9taWNUcmFuc2FjdGlvbi5hZGRUcmFuc2FjdGlvbih7ICdmcm9tJzogIGFjY3QuYWRkcmVzcywgJ3RvJzogIG9uZS5hZGRyZXNzLCAndmFsdWUnOiAxMjEzMTQxNSB9KTtcbiAgICAgKiAgICAgICAgICBjb25zdCB0eFJlY2VpcHQgPSBhd2FpdCBhdG9taWNUeC5jb21taXRBbmRTZW5kQW5kV2FpdCh7dGltZW91dE1TOiAxMCoxMDAwfSk7XG4gICAgICogICAgICB+fn5cbiAgICAgKlxuICAgICAqICBBbiBBdG9taWNUeCAtIGludGVybmFsbHkgLSB1c2VzIGEgJ3Byb3h5JyBjb250cmFjdCB0byBpbXBsZW1lbnQgdGhlIGFjdHVhbCBzdGVwcyBvZiBhbiBhdG9taWMgdHJhbnNhY3Rpb24uXG4gICAgICogIFNpbmNlIHRoaXMgY29udHJhY3Qgc29tZXRpbWVzIG5lZWRzIHRvIGJlIHNldHVwIGJlZm9yZSB0aGUgc3RhcnQgb2YgYW55IEF0b21pY1R4IHVzaW5nIGl0LCB0aGUgY2FsbGVyXG4gICAgICogIG1heSBwcmVjb25zdHJ1Y3QgaXQgKGZvciB1c2UgaW4gYXBwcm92ZXMsIGZvciBleGFtcGxlKS4gVGhleSB0aGVuIG1heSBvcHRpb25hbGx5IHBhc3MgaXQgdG8gdGhlXG4gICAgICogIEF0b21pY1R4IGNvbnN0cnVjdG9yIHRvIGF2b2lkIGhhdmluZyBpdCByZWNvbXB1dGVkLlxuICAgICAqXG4gICAgICogIGlmIHNpZ25lciBwcm92aWRlZCwgaXRzIGFkZHJlc3MgbXVzdCBtYXRjaCB0aGF0IGdpdmVuIGJ5IHRoZSAodGhlbiBvcHRpb25hbCkgYWNjb3VudEFkZHJlc3NcbiAgICAgKi9cbiAgICBleHBvcnQgY2xhc3MgVHJhbnNhY3Rpb24gaW1wbGVtZW50cyBJQXRvbWljVHgge1xuICAgICAgICAvKipcbiAgICAgICAgICogIEJlZ2luIGFuIGF0b21pYyB0cmFuc2FjdGlvbi5cbiAgICAgICAgICpcbiAgICAgICAgICogIEF0b21pYyB0cmFuc2FjdGlvbnMsIGFyZSBpbXBsZW1lbnRlZCB2aWEgYSBwcm94eSBjb250cmFjdCBpbnN0YW5jZSB3aGljaCB0eXBpY2FsbHkgbXVzdFxuICAgICAgICAgKiAgYmUgJ2FwcHJvdmVkJyBmb3Igb3BlcmF0aW9ucy5cbiAgICAgICAgICpcbiAgICAgICAgICogIFRoaXMgY2xhc3MgYXV0b21hdGljYWxseSBtYW5hZ2VzIGNvbnN0cnVjdGlvbiBvZiB0aGF0IHByb3h5IG9iamVjdCwgaWYgbmVlZGVkLCB0aG91Z2ggaXQgY2FuIGJlIHNwZWNpZmljYWxseVxuICAgICAgICAgKiAgcGFzc2VkIGludG8gdGhlIGNvbnN0cnVjdG9yLCBmb3IgcGVyZm9ybWFuY2UgcmVhc29ucy5cbiAgICAgICAgICpcbiAgICAgICAgICogIEV4YW1wbGUgVXNhZ2U6XG4gICAgICAgICAqICAgICAgfn5+XG4gICAgICAgICAqICAgICAgICAgIGNvbnN0IGF0b21pY1R4ID0gbmV3IFRyYW5zYWN0aW9uKHt3ZWIzOiBldzMsIGFjY291bnRBZGRyZXNzOiAgYWNjdC5hZGRyZXNzfSk7XG4gICAgICAgICAqICAgICAgfn5+XG4gICAgICAgICAqXG4gICAgICAgICAqICBFeGFtcGxlIFVzYWdlOlxuICAgICAgICAgKiAgICAgIH5+flxuICAgICAgICAgKiAgICAgICAgICBjb25zdCBhZ2VudENvbnRyYWN0QWRkcmVzcyA9IGF3YWl0IEV1bGl0aC5PbkNoYWluQWdlbnRzLmNvbnRyYWN0QWRkcmVzcyh7IHByb3ZpZGVyLCBhdXRob3JpemVkU2lnbmVyOiBhY2N0IH0pO1xuICAgICAgICAgKiAgICAgICAgICBhd2FpdCB0b2tlbkNvbnRyYWN0XG4gICAgICAgICAqICAgICAgICAgICAgICAgICAgLmFwcHJvdmUoYWdlbnRDb250cmFjdEFkZHJlc3MsIHRva2VuQ29udHJhY3QuYXNUb2tlblZhbHVlKGJvcnJvd0Ftb3VudEEgKiAxLjIpLCB7IGZyb206IGFjY3QuYWRkcmVzcyB9KVxuICAgICAgICAgKiAgICAgICAgICAgICAgICAgIC5zaWduQW5kU2VuZEFuZFdhaXQoYWNjdCwgcHJvdmlkZXIpO1xuICAgICAgICAgKiAgICAgICAgICBjb25zdCBhdG9taWNUeCA9IGF3YWl0IEV1bGl0aC5BdG9taWNUeC5UcmFuc2FjdGlvbih7IHByb3ZpZGVyLCBzaWduZXI6IGFjY3QsIGFnZW50Q29udHJhY3RBZGRyZXNzIH0pO1xuICAgICAgICAgKiAgICAgIH5+flxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0gcHJvdmlkZXIgLSB0eXBpY2FsbHkgRXVsaXRoLlByb3ZpZGVyIG9iamVjdCBPUiBFdWxpdGguV2ViM1xuICAgICAgICAgKiBAcGFyYW0gYWNjb3VudEFkZHJlc3MgLSByZXF1aXJlZCAtIHRyYW5zYWN0aW9ucyBtdXN0IGJlIHNpZ25lZCwgYW5kIHRoaXMgdGVsbHMgdGhlIGFkZHJlc3Mgb2YgdGhlIHNpZ25lclxuICAgICAgICAgKiBAcGFyYW0gYWdlbnRDb250cmFjdEFkZHJlc3MgLSBvcHRpb25hbCAtIGdlbmVyYWxseSBvbWl0dGVkLCBhbmQgY29tcHV0ZWQgYXV0b21hdGljYWxseSAtIHJlZmVycyB0byB0aGUgY29udHJhY3QgYWRkcmVzcyBvZiB0aGUgb24tY2hhaW4gYWdlbnQgaW1wbGVtZW50aW5nIHRoaXMgYXRvbWljIHRyYW5zYWN0aW9uXG4gICAgICAgICAqIEBwYXJhbSBnbm9zaXMgLSB0YmQgOy0pXG4gICAgICAgICAqL1xuICAgICAgICBwdWJsaWMgY29uc3RydWN0b3Ioe1xuICAgICAgICAgICAgcHJvdmlkZXIsXG4gICAgICAgICAgICBzaWduZXIsXG4gICAgICAgICAgICBhY2NvdW50QWRkcmVzcyxcbiAgICAgICAgICAgIGFnZW50Q29udHJhY3RBZGRyZXNzLFxuICAgICAgICAgICAgZ25vc2lzXG4gICAgICAgIH06IHtcbiAgICAgICAgICAgIHByb3ZpZGVyPzogRXVsaXRoLlByb3ZpZGVyIHwgRXVsaXRoLldlYjM7XG4gICAgICAgICAgICBhY2NvdW50QWRkcmVzcz86IHN0cmluZztcbiAgICAgICAgICAgIGdub3Npcz86IHN0cmluZztcbiAgICAgICAgICAgIHNpZ25lcj86IEV1bGl0aC5TaWduaW5nLklDcnlwdG9ncmFwaGljU2lnbmVyIHwgRXVsaXRoLlNpZ25pbmcuU2lnbmluZ1NlcnZpY2UgfCBFdWxpdGguV2ViMztcbiAgICAgICAgICAgIGFnZW50Q29udHJhY3RBZGRyZXNzPzogc3RyaW5nO1xuICAgICAgICB9KSB7XG4gICAgICAgICAgICBsZXQgdXNlUHJvdmlkZXIgPSBFdWxpdGguUHJvdmlkZXIuUHJvdmlkZXJPcldlYjMocHJvdmlkZXIpO1xuICAgICAgICAgICAgLy8gQHRvZG8gY2xlYW51cCBhZGRVUkxBZGRpdGlvbnNJZiBjb2RlID0gdG9vIGNvbXBsZXggc2V0dGluZyBhZGRyXG4gICAgICAgICAgICB1c2VQcm92aWRlci5hZGRVUkxBZGRpdGlvbnNJZih7XG4gICAgICAgICAgICAgICAgYXV0aF9hZGRyZXNzOiBhY2NvdW50QWRkcmVzcyA/PyBFdWxpdGguU2lnbmluZy5TaWduaW5nU2VydmljZS5hc3N1cmUoc2lnbmVyLCB1c2VQcm92aWRlcikuYWRkcmVzc1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBjb25zdCB1c2VTaWduZXIgPSBzaWduZXIgPT0gbnVsbCA/IG51bGwgOiBFdWxpdGguU2lnbmluZy5TaWduaW5nU2VydmljZS5hc3N1cmUoc2lnbmVyLCB1c2VQcm92aWRlcik7XG4gICAgICAgICAgICB0aGlzLmluVHJhbnNhY3Rpb25fID0gdHJ1ZTtcbiAgICAgICAgICAgIGlmIChhY2NvdW50QWRkcmVzcyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgaWYgKHNpZ25lciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIkF0b21pY1R4LlRyYW5zYWN0aW9uIGNvbnN0cnVjdG9yIG5lZWRzIGVpdGhlciBhY2NvdW50QWRkcmVzcyBvciBzaWduZXJcIik7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgYWNjb3VudEFkZHJlc3MgPSB1c2VTaWduZXIuYWRkcmVzcztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLlRYSURfID0gY3J5cHRvLnJhbmRvbVVVSUQoKTtcblxuICAgICAgICAgICAgY29uc3Qgd2lyZUZvcm1hdFBhcmFtczogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfSA9IHtcbiAgICAgICAgICAgICAgICBhdXRoX2FkZHJlc3M6IGFjY291bnRBZGRyZXNzLFxuICAgICAgICAgICAgICAgIGF0b21pY190eF9pZDogdGhpcy5UWElEX1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGlmIChnbm9zaXMpIHtcbiAgICAgICAgICAgICAgICB3aXJlRm9ybWF0UGFyYW1zW1wiZ25vc2lzX2FkZHJlc3NcIl0gPSBnbm9zaXM7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh1c2VQcm92aWRlcikge1xuICAgICAgICAgICAgICAgIHVzZVByb3ZpZGVyID0gdXNlUHJvdmlkZXIuY2xvbmVXaXRoVVJMQWRkaXRpb25zKHdpcmVGb3JtYXRQYXJhbXMpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJOZWVkIHByb3ZpZGVyIG9yIHdlYjNcIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLndlYjNfID0gbmV3IEV1bGl0aC5XZWIzKHtcbiAgICAgICAgICAgICAgICBwcm92aWRlcjogdXNlUHJvdmlkZXIsXG4gICAgICAgICAgICAgICAgc2lnbmVyOiB1c2VTaWduZXJcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICBpZiAoYWdlbnRDb250cmFjdEFkZHJlc3MgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHRoaXMuYWdlbnRDb250cmFjdEFkZHJlc3NfID0gRXVsaXRoLk9uQ2hhaW5BZ2VudHMuY29udHJhY3RBZGRyZXNzKHtcbiAgICAgICAgICAgICAgICAgICAgcHJvdmlkZXI6IHRoaXMud2ViM18ucHJvdmlkZXIsXG4gICAgICAgICAgICAgICAgICAgIGF1dGhvcml6ZWRBZGRyZXNzOiBhY2NvdW50QWRkcmVzc1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBhc3luYyBmdW5jdGlvbiBmKCk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBhZ2VudENvbnRyYWN0QWRkcmVzcyBhcyBzdHJpbmc7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRoaXMuYWdlbnRDb250cmFjdEFkZHJlc3NfID0gZigpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0aGlzLmxvZ2dlcl8gPSBwcm92aWRlcj8ubG9nZ2VyO1xuICAgICAgICB9XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqICBTaW5jZSBhdG9taWMgdHJhbnNhY3Rpb25zIGFyZSBpbXBsZW1lbnRlZCBieSBoYXZpbmcgYW4gb25DaGFpbiBBZ2VudCBjb250cmFjdCBkbyBhbGwgdGhlIG9wZXJhdGlvbnMsXG4gICAgICAgICAqICBpdCBvZnRlbiBtdXN0IGJlICdhcHByb3ZlZCcgdG8gZG8gdGhvc2Ugb3BlcmF0aW9ucywgc28gdGhlIGNhbGxlciB3aWxsIG5lZWQgdG8ga25vdyBpdHMgYWRkcmVzcy5cbiAgICAgICAgICovXG4gICAgICAgIHB1YmxpYyBnZXQgYWdlbnRDb250cmFjdEFkZHJlc3MoKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmFnZW50Q29udHJhY3RBZGRyZXNzXztcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiAgQWNjdW11bGF0ZSB0aGUgYXJndW1lbnQgdHJhbnNhY3Rpb25Db25maWcgaW50byBhIHNlcnZlci1zaWRlIG9iamVjdCB3aGljaCxcbiAgICAgICAgICogIHdoZW4gdGhpcy5jb21taXQoKSBpcyBjYWxsZWQsIHdpbGwgcHJvZHVjZSBhIHNpbmdsZSB0cmFuc2FjdGlvbiB3aGljaCBjb21iaW5lc1xuICAgICAgICAgKiAgdGhlIGFkZGVkIHN1Yi10cmFuc2FjdGlvbnMuXG4gICAgICAgICAqXG4gICAgICAgICAqICBAdG9kbyBTSU5DRSB3ZSBkb24ndCBpbXBsZW1lbnQgYW55IChtdWNoKSBzZXJ2ZXIgc2lkZSBsb2dpYyB0byByZWFjdCB0byBwYXJ0aWFsbHlcbiAgICAgICAgICogIGNvbXBsZXRlZCB0cmFuc2FjdGlvbnMgKGxpa2UgaW4gYSBkYXRhYmFzZSkgLSBJIHdvbmRlciB3aHkgdGhpcyBpcyB1c2VmdWwgdG9kb1xuICAgICAgICAgKiAgc2VydmVyIHNpZGUuIFRoYXQncyBwcm9iYWJseSB3b3J0aCBET0NVTUVOVElORy4gT3RoZXJ3aXNlLCB3ZSBjb3VsZCBqdXN0IGFjY3VtdWxhdGVcbiAgICAgICAgICogIGFuIGFycmF5IGNsaWVudCBzaWRlIGFuZCBzZW5kIHRoYXQuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSB0cmFuc2FjdGlvbkNvbmZpZ1xuICAgICAgICAgKiBAcmV0dXJuc1xuICAgICAgICAgKi9cbiAgICAgICAgcHVibGljIGFzeW5jIGFkZFRyYW5zYWN0aW9uKHRyYW5zYWN0aW9uQ29uZmlnOiBUcmFuc2FjdGlvbkNvbmZpZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmFnZW50Q29udHJhY3RBZGRyZXNzXzsgLy8gc28gc2VydmVyIGtub3dzIGFib3V0IHRoZSBhY2NvdW50XG4gICAgICAgICAgICBjb25zdCB0eFI6IFByb21pc2U8c3RyaW5nPiA9IHRoaXMud2ViM18uZXVsaXRoX3NlbmRfdW5zaWduZWRfdHJhbnNhY3Rpb24odHJhbnNhY3Rpb25Db25maWcpO1xuICAgICAgICAgICAgdGhpcy5vbmdvaW5nVHJhbnNhY3Rpb25zXy5wdXNoKHR4Uik7XG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgdHhSO1xuICAgICAgICB9XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqICBBZGRzIG11bHRpcGxlIHRyYW5zYWN0aW9ucywgYW5kIHJldHVybnMgYWxsIHRoZWlyIGhhc2hlZCB0eEhhc2ggcmVzdWx0cy5cbiAgICAgICAgICogIEVxdWl2aWxlbnQgdG8gYSBzZXJpZXMgc29mIGNhbGxzIHRvIGFkZFRyYW5zYWN0aW9uLCBidXQgd2FpdGluZyBmb3IgZWFjaCBpbiB0dXJuLlxuICAgICAgICAgKlxuICAgICAgICAgKiAgXFxub3RlIC0gdGhpcyBtZWFucyB0aGF0IG9yZGVyIGlzIHByZXNlcnZlZCBiZXR3ZWVuIHRoZSB0cmFuc2FjdGlvbnMgYWRkZWQsIGV2ZW5cbiAgICAgICAgICogIGlmIHRoZSBjYWxsZXIgZmFpbHMgdG8gd2FpdCBvbiB0aGlzIHByb21pc2UuXG4gICAgICAgICAqL1xuICAgICAgICBwdWJsaWMgYXN5bmMgYWRkVHJhbnNhY3Rpb25zKHR4czogVHJhbnNhY3Rpb25Db25maWdbXSk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICAgICAgICAgIGxldCByZXN1bHRzOiBzdHJpbmdbXSA9IFtdO1xuICAgICAgICAgICAgZm9yIChjb25zdCB0eCBvZiB0eHMpIHtcbiAgICAgICAgICAgICAgICBjb25zdCB0eEhhc2g6IHN0cmluZyA9IGF3YWl0IHRoaXMuYWRkVHJhbnNhY3Rpb24odHgpO1xuICAgICAgICAgICAgICAgIHJlc3VsdHMucHVzaCh0eEhhc2gpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdHM7XG4gICAgICAgIH1cblxuICAgICAgICAvKipcbiAgICAgICAgICogIE5vdCBuZWVkZWQsIGJ1dCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzIG1heWJlIGhhbmR5LCBhbmQgY2VydGFpbmx5IHVzZWQgaW50ZXJuYWxseS5cbiAgICAgICAgICovXG4gICAgICAgIHB1YmxpYyBnZXQgcHJvdmlkZXIoKTogRXVsaXRoLlByb3ZpZGVyIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLndlYjNfLnByb3ZpZGVyO1xuICAgICAgICB9XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqICBOb3QgbmVlZGVkLCBidXQgZm9yIGRlYnVnZ2luZyBwdXJwb3NlcyBtYXliZSBoYW5keVxuICAgICAgICAgKi9cbiAgICAgICAgcHVibGljIGdldCBhdG9taWNUeElEKCk6IHN0cmluZyB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5UWElEXztcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiAgXFxub3RlIFRoaXMgcmVwbGFjZXMgdGhlIHB5dGhvbiBldzMudjAuY29tbWl0X2F0b21pY190cmFuc2FjdGlvbigpXG4gICAgICAgICAqXG4gICAgICAgICAqICBcXHNlZSBhbHNvIGNvbW1pdEFuZFNlbmRBbmRXYWl0IC0gb2Z0ZW4gYSBiZXR0ZXIgY2hvaWNlXG4gICAgICAgICAqXG4gICAgICAgICAqICBFeGFtcGxlIFVzYWdlOlxuICAgICAgICAgKiAgICAgIH5+flxuICAgICAgICAgKiAgICAgICAgICBjb25zdCBhdG9taWNUeCA9IG5ldyBBdG9taWNUeC5UcmFuc2FjdGlvbih7d2ViMzogZXczLCBhY2NvdW50QWRkcmVzczogIGFjY3QuYWRkcmVzc30pO1xuICAgICAgICAgKiAgICAgICAgICAvLyBkbyBzb21ldGhpbmcgd2l0aCBhdG1vaWN0eCAtIGFkZGluZyB0cmFuc2FjdGlvbnMgb3IgLnByb3ZpZGVyIGFuZCBoYW5kIHRvIHNvbWV0aGluZyBlbHNlXG4gICAgICAgICAqICAgICAgICAgIGNvbnN0IHR4UmVjZWlwdDogVHJhbnNhY3Rpb25SZWNlaXB0ID0gYXdhaXQgYXRvbWljVHguY29tbWl0QW5kU2VuZEFuZFdhaXQoeyB0aW1lb3V0TVM6IDEwICogMTAwMCwgZXh0cmFUWFBhcmFtczJNZXJnZTogeyBnYXM6IDEwMDAwMDAgfSB9KVxuICAgICAgICAgKiAgICAgICAgICBleHBlY3QodHhSZWNlaXB0LnN0YXR1cykudG9CZVRydXRoeSgpO1xuICAgICAgICAgKiAgICAgIH5+flxuICAgICAgICAgKlxuICAgICAgICAgKiAgVGhpcyAoYXV0b21hdGljYWxseSkgd2FpdHMgZm9yIGFueSB0cmFuc2FjdGlvbnMgYWRkZWQgKHNvIHRoZSBjYWxsZXIgZG9lc250IG5lZWQgdG9cbiAgICAgICAgICogIHdhaXQgb24gdGhlIGFzeW5jIHJlc3VsdHMpXG4gICAgICAgICAqXG4gICAgICAgICAqICBUaGVuLCBjb21iaW5lcyBhbGwgdGhlIHRyYW5zYWN0aW9ucyBhdHRlbXB0ZWQsIGFuZCBwcm9kdWNlcyBhIG5ldyBUcmFuc2FjdGlvbkNvbmZpZ1xuICAgICAgICAgKiAgd2hpY2gsIGlmIHNpZ25lZCBhbmQgc2VudCB0byB0aGUgc2VydmVyLCB3aWxsIGNvbXBsZXRlIHRoZSAoY29tYmluZWQgYXRvbWljKSB0cmFuc2FjdGlvbi5cbiAgICAgICAgICovXG4gICAgICAgIHB1YmxpYyBhc3luYyBjb21taXQoKTogUHJvbWlzZTxUcmFuc2FjdGlvbkNvbmZpZz4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLmluVHJhbnNhY3Rpb25fKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiQ2Fubm90IGNhbGwgY29tbWl0IG9uIHRyYW5zYWN0aW9uIHRoYXQgaXMgYWxyZWFkeSBjb21pdHRlZFwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuaW5UcmFuc2FjdGlvbl8gPSBmYWxzZTtcbiAgICAgICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHRoaXMub25nb2luZ1RyYW5zYWN0aW9uc18pO1xuICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMucHJvdmlkZXIucmVxdWVzdCh7IG1ldGhvZDogXCJldWxpdGhfY29tbWl0XCIsIHBhcmFtczogW10gfSk7XG4gICAgICAgIH1cblxuICAgICAgICAvKipcbiAgICAgICAgICogIFxcbm90ZSBUaGlzIHJlcGxhY2VzIHRoZSBweXRob24gZXczLnYwLmNvbW1pdF9hdG9taWNfdHJhbnNhY3Rpb24oKSBmb2xsb3dlZCBieSBldzMuZXRoLnNlbmRfdHJhbnNhY3Rpb24odHhwYXJhbXMpIGFuZCBldzMuZXRoLndhaXRfZm9yX3RyYW5zYWN0aW9uX3JlY2VpcHQodHgpXG4gICAgICAgICAqXG4gICAgICAgICAqICBcXGJyaWVmIHNob3J0aGFuZCBmb3IgY29tbWl0KCksIGFuZCBzaWduaW5nIGFuZCBzZW5kaW5nIHRoYXQgdHgsIGFuZCB3YWl0aW5nIGZvciBpdHMgdHhSZWNlaXB0IGFsbCBpbiBvbmVcbiAgICAgICAgICpcbiAgICAgICAgICogIFxcbm90ZSAtIHJlcXVpcmVzIGEgc2lnbmVyIGF0dGFjaGVkIHRvIHRoZSBwcm92aWRlciBvYmplY3QgdXNlZCB0byBjcmVhdGUgdGhlIHRyYW5zYWN0aW9uLlxuICAgICAgICAgKlxuICAgICAgICAgKiAgXFxub3RlIC0gdGhpcyB0YWtlcyBhIHRpbWVvdXRNUywgYW5kIHJlcGVhdGVkbHkgY2FsbHMgZ2V0VHJhbnNhY3Rpb25SZWNlaXB0LCB1bnRpbCB0aGUgdGltZW91dFxuICAgICAgICAgKiAgICAgICAgZXhwaXJlcyAob3IgcmVjZWlwdCByZWNlaXZlZCkuIFRoaXMgZGlmZmVycyBmcm9tIHRoZSBkZWZhdWx0IGJlaGF2aW9yIG9mIGdldFRyYW5zYWN0aW9uUmVjZWlwdFxuICAgICAgICAgKlxuICAgICAgICAgKiAgXFx0b2RvIERpc2N1c3MgaWYgdGhpcyBzaG91bGQgdGhyb3cgb24gZmFpbHVyZT8gb3IgcHJvdmlkZSBvdmVybG9hZC92YXJpYW50IHRoYXQgZG9lc1xuICAgICAgICAgKi9cbiAgICAgICAgcHVibGljIGFzeW5jIGNvbW1pdEFuZFNlbmRBbmRXYWl0KGFyZ3M/OiBjb21taXRBbmRTZW5kQW5kV2FpdEFyZ3MpOiBQcm9taXNlPFRyYW5zYWN0aW9uUmVjZWlwdD4ge1xuICAgICAgICAgICAgY29uc3QgdGltZW91dE1TID0gYXJncz8udGltZW91dE1TO1xuICAgICAgICAgICAgY29uc3QgZXh0cmFUWFBhcmFtczJNZXJnZSA9IGFyZ3M/LmV4dHJhVFhQYXJhbXMyTWVyZ2U7XG4gICAgICAgICAgICBpZiAodGhpcy53ZWIzXy5zaWduZXIgPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIEV1bGl0aC5FeGNlcHRpb25zLkFQSS5UaHJvdyh0aGlzLmxvZ2dlcl8sIHtcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogXCJDYW5ub3QgQXRvbWljVHguY29tbWl0QW5kU2VuZEFuZFdhaXQgd2l0aG91dCBhbiBhc3NvY2lhdGVkIHNpZ25lclwiXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBsZXQgY29tYmluZWRUcmFuc2FjdGlvbkFzVHhQYXJhbXMgPSBhd2FpdCB0aGlzLmNvbW1pdCgpO1xuICAgICAgICAgICAgaWYgKGV4dHJhVFhQYXJhbXMyTWVyZ2UpIHtcbiAgICAgICAgICAgICAgICBjb21iaW5lZFRyYW5zYWN0aW9uQXNUeFBhcmFtcyA9IHtcbiAgICAgICAgICAgICAgICAgICAgLi4uY29tYmluZWRUcmFuc2FjdGlvbkFzVHhQYXJhbXMsXG4gICAgICAgICAgICAgICAgICAgIC4uLmV4dHJhVFhQYXJhbXMyTWVyZ2VcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgdHhIYXNoOiBzdHJpbmcgPSBhd2FpdCB0aGlzLnByb3ZpZGVyLnNpZ25BbmRTZW5kVHJhbnNhY3Rpb24oXG4gICAgICAgICAgICAgICAgY29tYmluZWRUcmFuc2FjdGlvbkFzVHhQYXJhbXMsXG4gICAgICAgICAgICAgICAgdGhpcy53ZWIzXy5zaWduZXJcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgRXVsaXRoLlV0aWxzLndhaXRGb3JUeFJlY2VpcHQoe1xuICAgICAgICAgICAgICAgIGxvZ2dlcjogdGhpcy5sb2dnZXJfLFxuICAgICAgICAgICAgICAgIHByb3ZpZGVyOiB0aGlzLnByb3ZpZGVyLFxuICAgICAgICAgICAgICAgIHR4SGFzaDogdHhIYXNoLFxuICAgICAgICAgICAgICAgIHRpbWVvdXRJbk1TOiB0aW1lb3V0TVNcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqL1xuICAgICAgICBwdWJsaWMgYXN5bmMgcm9sbGJhY2soKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgICAgICAvKlxuICAgICAgICAgICAgICogIFRoZSBibG9ja2NoYWluIGFuZCBvdXIgc2VydmVyIGFyZSB0d28gZGlmZmVyZW50IGxheWVycyBvZiBzZXJ2aWNlcy4gV2hlbiB5b3UgY2FsbCBjb21taXQgb24gb3VyIHNlcnZlcixcbiAgICAgICAgICAgICAqICBpdCBmbHVzaGVzIHRoZSBhY2N1bXVsYXRlZCB0eCBhbmQgc2VyaWFsaXplcyBpdC4gVGhpcyBvdXRwdXQgdGhlbiBoYXMgdG8gYmUgc2lnbmVkIGFuZFxuICAgICAgICAgICAgICogIHNlbnQgdG8gdGhlIGNoYWluIGluIG9yZGVyIHRvIGJlIGNvbmZpcm1lZC4gSWYgeW91IGNhbGwgY29tbWl0IGFuZCBkbyBub3RoaW5nIHdpdGggdGhlIHNlcmlhbGl6ZWQgb3V0cHV0LFxuICAgICAgICAgICAgICogIGl0J3MgZWZmZWN0aXZlbHkgZmx1c2hpbmcgdGhlIHR4IGFuZCB0aHJvd2luZyBpdCBpbiB0aGUgZ2FyYmFnZSwgaGVuY2UgdGhlIHJvbGxiYWNrIGZ1bmN0aW9uYWxpdHkuXG4gICAgICAgICAgICAgKlxuICAgICAgICAgICAgICogc2luY2Ugcm9sbGJhY2sgZG9lc250IHJldHVybiB0aGUgdHJhbnNhY3Rpb24sIGl0IGhhcyBub3RoaW5nIHRvIGZvcndhcmQgd2l0aCBzZW5kVHJhbnNhY3Rpb25cbiAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5jb21taXQoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHByaXZhdGUgd2ViM186IEV1bGl0aC5XZWIzOyAvLyBAdG9kbyAtIGNvdWxkIGxvc2UgdGhpcyB3ZWIzXyBvYmplY3QgYW5kIGp1c3Qgc3RvcmUgcHJvdmlkZXJfXG4gICAgICAgIHByaXZhdGUgaW5UcmFuc2FjdGlvbl86IGJvb2xlYW47XG4gICAgICAgIHByaXZhdGUgVFhJRF86IHN0cmluZztcbiAgICAgICAgcHJpdmF0ZSBsb2dnZXJfOiBFdWxpdGguTG9nZ2luZy5JTG9nZ2VyO1xuICAgICAgICBwcml2YXRlIG9uZ29pbmdUcmFuc2FjdGlvbnNfOiBQcm9taXNlPHN0cmluZz5bXSA9IFtdO1xuICAgICAgICBwcml2YXRlIGFnZW50Q29udHJhY3RBZGRyZXNzXzogUHJvbWlzZTxzdHJpbmc+O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqICBAdHlwZWRlZiBFdWxpdGguQXRvbWljVHguTmVzdGVkVHJhbnNhY3Rpb25cbiAgICAgKlxuICAgICAqICBcXGJyaWVmIFRoaXMgYWN0cyBxdWl0ZSBzaW1pbGFybHkgdG8gYSBBdG9taWNUeCAoYW5kIHNvbWVkYXkgbWF5YmUgbWVyZ2VkIHdpdGggdGhhdClcbiAgICAgKlxuICAgICAqICBJdCBhY3RzIGFzIGEgbmVzdGVkIHRyYW5zYWN0aW9uLlxuICAgICAqXG4gICAgICogIEV4YW1wbGUgVXNhZ2U6XG4gICAgICogICAgICB+fn5cbiAgICAgKiAgICAgICAgICBjb25zdCBhdG9taWNUeCA9IG5ldyBFdWxpdGguQXRvbWljVHguVHJhbnNhY3Rpb24oeyBwcm92aWRlciwgc2lnbmVyOiBhY2N0IH0pO1xuICAgICAqXG4gICAgICogICAgICAgICAgY29uc3Qgc3dhcEF0b21pY1R4OiBFdWxpdGguQXRvbWljVHguTmVzdGVkVHJhbnNhY3Rpb24gPSBhd2FpdCBFdWxpdGguVW5pc3dhcC5zdGFydFN3YXAoe1xuICAgICAqICAgICAgICAgICAgICByZXF1ZXN0OiBxdW90ZS5zd2FwUmVxdWVzdCxcbiAgICAgKiAgICAgICAgICAgICAgcGFyZW50VHg6IGF0b21pY1R4XG4gICAgICogICAgICAgICAgfSk7XG4gICAgICogICAgICAgICAgYXdhaXQgc3dhcEF0b21pY1R4LmNvbW1pdCgpOyAvLyBOT1RFOiBjcml0aWNhbCB0byBhd2FpdCBoZXJlLCBiZWZvcmUgY29tbWl0aW5nIHRoZSBwYXJlbnQgdHJhbnNhY3Rpb24hXG4gICAgICpcbiAgICAgKiAgICAgICAgICAvLyBDb21taXQsIHNpZ24sIGFuZCBjb21wbGV0ZSB0aGUgb3BlcmF0aW9uIC0gdGhpcyB3aWxsIG9ubHkgcmV0dXJuIGlmL3doZW4gdGhlIGVudGlyZSBvcGVyYXRpb24gc3VjY2VlZWRzIChhbmQgdGhyb3dzIG9uIGFueSBwYXJ0IGZhaWxpbmcpXG4gICAgICogICAgICAgICAgYXdhaXQgYXRvbWljVHguY29tbWl0QW5kU2VuZEFuZFdhaXQoKTtcbiAgICAgKiAgICAgIH5+flxuICAgICAqL1xuICAgIGV4cG9ydCBjbGFzcyBOZXN0ZWRUcmFuc2FjdGlvbiB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiAgTmVzd