eulith-web3js-core
Version:
Eulith core web3js SDK (code to access Eulith services via web3js)
374 lines • 63.3 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.OnChainAgents = void 0;
const protocol_kit_1 = require("@safe-global/protocol-kit");
const assert_1 = require("assert");
const safe_core_sdk_1 = __importDefault(require("@safe-global/safe-core-sdk"));
const Eulith = __importStar(require("../src/index"));
// note returns contract_type, but doesn't filter on contract_type
function fetchContractDetailsForAuthorizedAddress_(provider, authorizedAddress) {
return __awaiter(this, void 0, void 0, function* () {
const authorizedAddressLC = authorizedAddress.toLowerCase();
const result = yield provider.request({ method: "eulith_get_contracts", params: [] });
const firstMatch = result == null
? null
: result["contracts"].find((contract) => contract.authorized_address.toLowerCase() == authorizedAddressLC);
if (firstMatch) {
return {
contract_address: firstMatch === null || firstMatch === void 0 ? void 0 : firstMatch.contract_address,
authorized_address: firstMatch === null || firstMatch === void 0 ? void 0 : firstMatch.authorized_address,
safe_address: firstMatch === null || firstMatch === void 0 ? void 0 : firstMatch.safe_address,
contract_type: firstMatch === null || firstMatch === void 0 ? void 0 : firstMatch.contract_type,
network_name: firstMatch === null || firstMatch === void 0 ? void 0 : firstMatch.network_name,
chain_id: firstMatch === null || firstMatch === void 0 ? void 0 : firstMatch.chain_id
};
}
else {
throw new Error(`No agent found for the authorized address: ${authorizedAddress}; did you call Eulith.OnChainAgents.createUncheckedAgent, or Eulith.OnChainAgents.createArmorAgent, or Eulith.OnChainAgents.getAgent(createUncheckedAgentIfNoneExists)?`);
}
});
}
function createUnchecked_(provider, authorizedAddress) {
return __awaiter(this, void 0, void 0, function* () {
const result = yield provider.request({
method: "eulith_new_contract",
params: [{ authorized_address: authorizedAddress }]
});
return result === null || result === void 0 ? void 0 : result.contract_address; // should never return null - caller checks
});
}
/**
* OnChainAgents are smart contracts employed mostly transparently to execute programmed sequences
* of ethereum transactions on your behelf.
*
* OnChainAgents are key to how AtomicTx (atomic transactions) work, for example, but other facilities also
* leverage this special contract.
*
* Frequently, the Eulith APIs manage these OnChainAgents completely transparently, and it can be ignored.
* But, in some cases, the user must be aware of the agent (contract) (for example, to approve transfers).
*
* Typically, all the user will need to know is, occasionally, to get information about the agent (like its contract address)
* via accessing its object:
* const contractAddress: string = Eulith.OnChainAgents.getAgent({provider, authorizedAddress}).contractAddress OR
* const contractAddress: string = Eulith.OnChainAgents.getAgent({provider, authoriziedSigner}).contractAddress
*/
var OnChainAgents;
(function (OnChainAgents) {
/**
* @typedef Eulith.OnChainAgents.Type
*/
let Type;
(function (Type) {
/**
* An Unchecked Agent is the historical one, basic one, of of pre-april 2023.
*
* It provides less checking and security than the armor contracts.
*
* Associated value 'call' is historical.
*/
Type["Unchecked"] = "call";
/**
* An Armor Agent is one that uses a Safe (see URL)
*/
Type["Armor"] = "armor";
})(Type = OnChainAgents.Type || (OnChainAgents.Type = {}));
/**
* Eulith.OnChainAgents.getAgent
*
* Fetch the current agent refernece for the argument authorized address (or authoriziedSigner).
* provider (maybe Eulith.Provider | Eulith.Web3) used to communicate with the Ethereum network.
* createUncheckedAgentIfNoneExists - defaults to true if not specified.
*
* Note - there can never be more than one IAgent associated with a given authorizedAddress.
*
* See also Eulith.OnChainAgents.createUncheckedAgent
* See also Eulith.OnChainAgents.createArmorAgent
*/
function getAgent({ provider, authorizedAddress, authoriziedSigner, createUncheckedAgentIfNoneExists }) {
return __awaiter(this, void 0, void 0, function* () {
const useProvider = Eulith.Provider.ProviderOrWeb3(provider);
const useAuthorizedAddress = authorizedAddress !== null && authorizedAddress !== void 0 ? authorizedAddress : authoriziedSigner.address;
try {
const r = yield fetchContractDetailsForAuthorizedAddress_(useProvider, useAuthorizedAddress);
switch (r.contract_type) {
case "call":
return new UncheckedAgent_(r.contract_address, useAuthorizedAddress);
case "armor":
return new ArmorAgent_(r.contract_address, useAuthorizedAddress, r.safe_address);
default:
throw new Error("getAgent: unrecognized contract-type");
}
}
catch (e) {
if (createUncheckedAgentIfNoneExists !== null && createUncheckedAgentIfNoneExists !== void 0 ? createUncheckedAgentIfNoneExists : true) {
return yield createUncheckedAgent({ provider: useProvider, authorizedAddress: useAuthorizedAddress });
}
}
return null;
});
}
OnChainAgents.getAgent = getAgent;
/**
* Eulith.OnChainAgents.createUncheckedAgent is provided for logical consistency, but typically won't be used directly - instead - just use Eulith.OnChainAgents.getAgent();
*
* Note this routine will fail if the agent already exists (another reason to just call Eulith.OnChainAgents.getAgent ())
*/
function createUncheckedAgent({ provider, authorizedAddress }) {
return __awaiter(this, void 0, void 0, function* () {
const useProvider = Eulith.Provider.ProviderOrWeb3(provider);
const constractAddress = yield createUnchecked_(useProvider, authorizedAddress);
return new UncheckedAgent_(constractAddress, authorizedAddress);
});
}
OnChainAgents.createUncheckedAgent = createUncheckedAgent;
/**
* Eulith.OnChainAgents.createArmorAgent
*
* A Eulith 'armor' agent, is like any Eulith agent, allowing you to automate a sequence of operations on the
* Ethereum network, but in this case, much of that automation is actually performed by a 'safe' <https://safe.global/>
*
* This creation process may optionally either create your safe, or re-use an existing safe. But either way, it registers
* a Eulith armor agent as a module on that safe, allowing it to direct the safe to perform transactions on it's behalf
* (transitively on your behalf, triggered by your use of an 'authorizedSigner').
*
* You own/control that authorizedSigner. This allows the safe to operated by a SINGLE signer, instead of the requiring the N (safe threshold)
* signers.
*
* The basic process is:
*
* - construct an armor contract with this API (passing in a safe, or the data needed to create a safe).
* - get the owners of the safe to 'sign' the request to enable the Eulith Armor agent.
* - enableArmor () - which completes the setup - activating your safe and your authorized signers ability to use it
* - then start using your authorized signer - its now allowed to use the contents of this safe.
*
* provider is the Eulith provider Ethereum network provider (or a Eulith.Web from which a provider can be extracted)
*
* authorizedAddress is the address of some user-controlled signer (typically KMS-based, for example),
* which will be used to OPERATE and make requests on some associated safe (via the armor agent).
*
* safeAddress is the address of the existing safe to associate with the new armor, and if null or omitted
* a new safe will be automatically created.
*
* NOTE, if safeAddress is not specified, the safe will be automatically created by the Eulith server
* and owners etc calculated automatically.
*
* setupSigner: a signer is needed to send any ethereum transaction message, but which one is used here does not matter, except that it pays the
* gas for this setup transaction.
*
* Note - this will FAIL if any Eulith Agent already exists for this authorizedAddress.
*
* Exactly one of safeAddress (case of existing safe), or safeCreationParamters, must be provided (NOTE KRISTIAN - THIS IS A DEPARTURE FROM YOUR CURRENT API - A SUGGESTION).
*/
function createArmorAgent({ provider, authorizedAddress, safeAddress, setupSigner }) {
return __awaiter(this, void 0, void 0, function* () {
const useProvider = Eulith.Provider.ProviderOrWeb3(provider);
assert_1.strict.notEqual(provider, null, "provider is required for Eulith.OnChainAgents.createArmorAgent");
assert_1.strict.notEqual(authorizedAddress, null, "authorizedAddress is required for Eulith.OnChainAgents.createArmorAgent");
assert_1.strict.notEqual(setupSigner, null, "setupSigner is required for Eulith.OnChainAgents.createArmorAgent");
function createArmorContractInEulith(provider, authorizedAddress, preexistingSafeAddress) {
return __awaiter(this, void 0, void 0, function* () {
// @Kristian/Moh/Ian: I suggest replacing the safe_already_exists parameter with a preexisting_save_address parameter, which can be null
const result = yield provider.request({
method: "eulith_new_contract",
params: [
{
authorized_address: authorizedAddress,
contract_type: "armor",
safe_already_exists: preexistingSafeAddress != null
}
]
});
return new Eulith.Signing.UnsignedTransaction(result);
});
}
// Create the armor contract.
// First create it in the eulith db, and that then generates some gorp to be run on the ethereum network to complete
// the setup (creates various contract instances - I'm guessing). Though this CODE be done inside the Eulith call
// server, it isn't for gas cost reasons.
const preexistingSafeAddress = safeAddress;
const contractCreateTx = yield createArmorContractInEulith(useProvider, authorizedAddress, preexistingSafeAddress);
// const txHash: string = (await contractCreateTx.signAndSendAndWait(setupSigner, useProvider)).transactionHash;
const useSetupSigner = Eulith.Signing.SigningService.assure(setupSigner, useProvider);
const txHash = (yield useSetupSigner.sendTransactionAndWait(contractCreateTx)).transactionHash;
function eulithSubmitArmorHash(provider, txHash, preexistingSafeAddress) {
return __awaiter(this, void 0, void 0, function* () {
const result = yield provider.request({
method: "eulith_submit_armor_hash",
params: [{ tx_hash: txHash, safe_address: preexistingSafeAddress !== null && preexistingSafeAddress !== void 0 ? preexistingSafeAddress : undefined }]
});
if (result != true) {
throw new Error("eulith_submit_armor_hash failed");
}
});
}
yield eulithSubmitArmorHash(useProvider, txHash, preexistingSafeAddress);
const r = yield getAgent({ provider, authorizedAddress });
assert_1.strict.equal(r.type, Type.Armor, "internal error: agent associasted with this address is not an armor agent");
return r;
});
}
OnChainAgents.createArmorAgent = createArmorAgent;
/**
* Eulith.OnChainAgents.contractAddress
*
* Shorthand for getAgent(...sameargs).contractAddress
*/
function contractAddress({ provider, authorizedAddress, authoriziedSigner, createUncheckedAgentIfNoneExists }) {
return __awaiter(this, void 0, void 0, function* () {
return (yield getAgent({ provider, authorizedAddress, authoriziedSigner, createUncheckedAgentIfNoneExists }))
.contractAddress;
});
}
OnChainAgents.contractAddress = contractAddress;
/**
* Eulith.OnChainAgents.armorAgent
*
* Access the existing armor agent for authorized address/signer: Shorthand for getAgent(...sameargs) - check is right type, and return it as IArmorAgent
*
* note - never returns null, or creates anything - just returns the existing agent, or throws
*/
function armorAgent({ provider, authorizedAddress, authoriziedSigner }) {
return __awaiter(this, void 0, void 0, function* () {
const agent = yield getAgent({
provider,
authorizedAddress,
authoriziedSigner,
createUncheckedAgentIfNoneExists: false
});
assert_1.strict.equal(agent.type, Type.Armor, "Eulith.OnChainAgents.armorAgent found agent but not armor agent");
return agent;
});
}
OnChainAgents.armorAgent = armorAgent;
class UncheckedAgent_ {
constructor(contractAddress, authorizedAddress) {
this.contractAddress_ = contractAddress;
this.authorizedAddress_ = authorizedAddress;
}
get type() {
return Type.Unchecked;
}
get contractAddress() {
return this.contractAddress_;
}
get authorizedAddress() {
return this.authorizedAddress_;
}
}
class ArmorAgent_ {
constructor(contractAddress, authorizedAddress, safeAddress) {
this.contractAddress_ = contractAddress;
this.authorizedAddress_ = authorizedAddress;
this.safeAddress_ = safeAddress;
}
authorizeForOwner({ provider, authorizingOwner }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (this.authorizedAddress_ == authorizingOwner.address) {
(_a = provider.logger) === null || _a === void 0 ? void 0 : _a.log(Eulith.Logging.LogLevel.WARNING, `Generally ill-advised, having the same owner be authorized address and owner`);
}
const useProvider = Eulith.Provider.ProviderOrWeb3(provider);
const useAuthorizingOwner = Eulith.Signing.SigningService.assure(authorizingOwner, provider);
const ethAdapter = new protocol_kit_1.Web3Adapter({
web3: new Eulith.Web3({ provider: useProvider, signer: useAuthorizingOwner }),
signerAddress: useAuthorizingOwner.address
});
const agentContractInfo = yield fetchContractDetailsForAuthorizedAddress_(useProvider, this.authorizedAddress_);
if (agentContractInfo.contract_type != "armor") {
throw new Error(`No armor contract found for the authorized address: ${this.authorizedAddress_}; did you call createArmor?`);
}
let safeAddress = agentContractInfo.safe_address;
let armorAddress = agentContractInfo.contract_address;
const safe = yield safe_core_sdk_1.default.create({ ethAdapter, safeAddress });
const enableModuleTx = yield safe.createEnableModuleTx(armorAddress);
const signature = yield safe.signTypedData(enableModuleTx);
function submitEnableModuleSignature(provider, signature, moduleAddress, ownerAddress) {
return __awaiter(this, void 0, void 0, function* () {
const result = yield provider.request({
method: "eulith_submit_enable_safe_signature",
params: [{ signature, module_address: moduleAddress, owner_address: ownerAddress }]
});
});
}
yield submitEnableModuleSignature(useProvider, signature.data, armorAddress, authorizingOwner.address);
});
}
enableArmor({ provider, signerForThisTx, forSafe }) {
return __awaiter(this, void 0, void 0, function* () {
const useProvider = Eulith.Provider.ProviderOrWeb3(provider).cloneWithURLAdditions({
auth_address: this.authorizedAddress
});
if (forSafe.safeAddress) {
// const result = await provider.request({
// method: "eulith_submit_armor_hash",
// params: [{ signature, module_address: moduleAddress, owner_address: ownerAddress }]
// });
throw new Error("NYI");
}
else if (forSafe.newSafe.owners && forSafe.newSafe.approvalThreshold) {
const result = yield useProvider.request({
method: "eulith_enable_safe_tx",
params: [{ owners: forSafe.newSafe.owners, owner_threshold: forSafe.newSafe.approvalThreshold }]
});
// @todo document/rethink this use of defaultSigner
// const txHash = await useProvider.signAndSendTransaction(result, signerForThisTx);
const txHash = yield Eulith.Signing.SigningService.assure(signerForThisTx, provider).sendTransaction(result);
// @kristian - UNCLEAR if we need to wait for this tx, so assuming yes. But sample rust code I looked at didn't
yield Eulith.Utils.waitForTxReceipt({ provider: useProvider, txHash });
// I THINK that's it - and we have enabled the armor?
}
else {
throw new Error("enableArmor requires either forSafe.safeAddress or forSafe.newSafe parameters to be filled out");
}
});
}
get type() {
return Type.Armor;
}
get safeAddress() {
return this.safeAddress_;
}
get contractAddress() {
return this.contractAddress_;
}
get authorizedAddress() {
return this.authorizedAddress_;
}
}
})(OnChainAgents = exports.OnChainAgents || (exports.OnChainAgents = {}));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib24tY2hhaW4tYWdlbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL29uLWNoYWluLWFnZW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVBLDREQUF3RDtBQUN4RCxtQ0FBMEM7QUFDMUMsK0VBQThDO0FBRTlDLHFEQUF1QztBQUV2QyxrRUFBa0U7QUFDbEUsU0FBZSx5Q0FBeUMsQ0FBQyxRQUF5QixFQUFFLGlCQUF5Qjs7UUFDekcsTUFBTSxtQkFBbUIsR0FBRyxpQkFBaUIsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUM1RCxNQUFNLE1BQU0sR0FBRyxNQUFNLFFBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxNQUFNLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDdEYsTUFBTSxVQUFVLEdBQ1osTUFBTSxJQUFJLElBQUk7WUFDVixDQUFDLENBQUMsSUFBSTtZQUNOLENBQUMsQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxRQUFRLENBQUMsa0JBQWtCLENBQUMsV0FBVyxFQUFFLElBQUksbUJBQW1CLENBQUMsQ0FBQztRQUNuSCxJQUFJLFVBQVUsRUFBRTtZQUNaLE9BQU87Z0JBQ0gsZ0JBQWdCLEVBQUUsVUFBVSxhQUFWLFVBQVUsdUJBQVYsVUFBVSxDQUFFLGdCQUFnQjtnQkFDOUMsa0JBQWtCLEVBQUUsVUFBVSxhQUFWLFVBQVUsdUJBQVYsVUFBVSxDQUFFLGtCQUFrQjtnQkFDbEQsWUFBWSxFQUFFLFVBQVUsYUFBVixVQUFVLHVCQUFWLFVBQVUsQ0FBRSxZQUFZO2dCQUN0QyxhQUFhLEVBQUUsVUFBVSxhQUFWLFVBQVUsdUJBQVYsVUFBVSxDQUFFLGFBQWE7Z0JBQ3hDLFlBQVksRUFBRSxVQUFVLGFBQVYsVUFBVSx1QkFBVixVQUFVLENB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Y3Rpb25Db25maWcgfSBmcm9tIFwid2ViMy1jb3JlXCI7XG5pbXBvcnQgeyBTYWZlVHJhbnNhY3Rpb24sIFNhZmVTaWduYXR1cmUsIEV0aEFkYXB0ZXIgfSBmcm9tIFwiQHNhZmUtZ2xvYmFsL3NhZmUtY29yZS1zZGstdHlwZXNcIjtcbmltcG9ydCB7IFdlYjNBZGFwdGVyIH0gZnJvbSBcIkBzYWZlLWdsb2JhbC9wcm90b2NvbC1raXRcIjtcbmltcG9ydCB7IHN0cmljdCBhcyBhc3NlcnQgfSBmcm9tIFwiYXNzZXJ0XCI7XG5pbXBvcnQgU2FmZSBmcm9tIFwiQHNhZmUtZ2xvYmFsL3NhZmUtY29yZS1zZGtcIjtcblxuaW1wb3J0ICogYXMgRXVsaXRoIGZyb20gXCIuLi9zcmMvaW5kZXhcIjtcblxuLy8gbm90ZSByZXR1cm5zIGNvbnRyYWN0X3R5cGUsIGJ1dCBkb2Vzbid0IGZpbHRlciBvbiBjb250cmFjdF90eXBlXG5hc3luYyBmdW5jdGlvbiBmZXRjaENvbnRyYWN0RGV0YWlsc0ZvckF1dGhvcml6ZWRBZGRyZXNzXyhwcm92aWRlcjogRXVsaXRoLlByb3ZpZGVyLCBhdXRob3JpemVkQWRkcmVzczogc3RyaW5nKSB7XG4gICAgY29uc3QgYXV0aG9yaXplZEFkZHJlc3NMQyA9IGF1dGhvcml6ZWRBZGRyZXNzLnRvTG93ZXJDYXNlKCk7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHJvdmlkZXIucmVxdWVzdCh7IG1ldGhvZDogXCJldWxpdGhfZ2V0X2NvbnRyYWN0c1wiLCBwYXJhbXM6IFtdIH0pO1xuICAgIGNvbnN0IGZpcnN0TWF0Y2ggPVxuICAgICAgICByZXN1bHQgPT0gbnVsbFxuICAgICAgICAgICAgPyBudWxsXG4gICAgICAgICAgICA6IHJlc3VsdFtcImNvbnRyYWN0c1wiXS5maW5kKChjb250cmFjdCkgPT4gY29udHJhY3QuYXV0aG9yaXplZF9hZGRyZXNzLnRvTG93ZXJDYXNlKCkgPT0gYXV0aG9yaXplZEFkZHJlc3NMQyk7XG4gICAgaWYgKGZpcnN0TWF0Y2gpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGNvbnRyYWN0X2FkZHJlc3M6IGZpcnN0TWF0Y2g/LmNvbnRyYWN0X2FkZHJlc3MsXG4gICAgICAgICAgICBhdXRob3JpemVkX2FkZHJlc3M6IGZpcnN0TWF0Y2g/LmF1dGhvcml6ZWRfYWRkcmVzcyxcbiAgICAgICAgICAgIHNhZmVfYWRkcmVzczogZmlyc3RNYXRjaD8uc2FmZV9hZGRyZXNzLFxuICAgICAgICAgICAgY29udHJhY3RfdHlwZTogZmlyc3RNYXRjaD8uY29udHJhY3RfdHlwZSxcbiAgICAgICAgICAgIG5ldHdvcmtfbmFtZTogZmlyc3RNYXRjaD8ubmV0d29ya19uYW1lLFxuICAgICAgICAgICAgY2hhaW5faWQ6IGZpcnN0TWF0Y2g/LmNoYWluX2lkXG4gICAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYE5vIGFnZW50IGZvdW5kIGZvciB0aGUgYXV0aG9yaXplZCBhZGRyZXNzOiAke2F1dGhvcml6ZWRBZGRyZXNzfTsgZGlkIHlvdSBjYWxsIEV1bGl0aC5PbkNoYWluQWdlbnRzLmNyZWF0ZVVuY2hlY2tlZEFnZW50LCBvciBFdWxpdGguT25DaGFpbkFnZW50cy5jcmVhdGVBcm1vckFnZW50LCBvciBFdWxpdGguT25DaGFpbkFnZW50cy5nZXRBZ2VudChjcmVhdGVVbmNoZWNrZWRBZ2VudElmTm9uZUV4aXN0cyk/YFxuICAgICAgICApO1xuICAgIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gY3JlYXRlVW5jaGVja2VkXyhwcm92aWRlcjogRXVsaXRoLlByb3ZpZGVyLCBhdXRob3JpemVkQWRkcmVzczogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBwcm92aWRlci5yZXF1ZXN0KHtcbiAgICAgICAgbWV0aG9kOiBcImV1bGl0aF9uZXdfY29udHJhY3RcIixcbiAgICAgICAgcGFyYW1zOiBbeyBhdXRob3JpemVkX2FkZHJlc3M6IGF1dGhvcml6ZWRBZGRyZXNzIH1dXG4gICAgfSk7XG4gICAgcmV0dXJuIHJlc3VsdD8uY29udHJhY3RfYWRkcmVzczsgLy8gc2hvdWxkIG5ldmVyIHJldHVybiBudWxsIC0gY2FsbGVyIGNoZWNrc1xufVxuXG4vKipcbiAqICBPbkNoYWluQWdlbnRzIGFyZSBzbWFydCBjb250cmFjdHMgZW1wbG95ZWQgbW9zdGx5IHRyYW5zcGFyZW50bHkgdG8gZXhlY3V0ZSBwcm9ncmFtbWVkIHNlcXVlbmNlc1xuICogIG9mIGV0aGVyZXVtIHRyYW5zYWN0aW9ucyBvbiB5b3VyIGJlaGVsZi5cbiAqXG4gKiAgT25DaGFpbkFnZW50cyBhcmUga2V5IHRvIGhvdyBBdG9taWNUeCAoYXRvbWljIHRyYW5zYWN0aW9ucykgd29yaywgZm9yIGV4YW1wbGUsIGJ1dCBvdGhlciBmYWNpbGl0aWVzIGFsc29cbiAqICBsZXZlcmFnZSB0aGlzIHNwZWNpYWwgY29udHJhY3QuXG4gKlxuICogIEZyZXF1ZW50bHksIHRoZSBFdWxpdGggQVBJcyBtYW5hZ2UgdGhlc2UgT25DaGFpbkFnZW50cyBjb21wbGV0ZWx5IHRyYW5zcGFyZW50bHksIGFuZCBpdCBjYW4gYmUgaWdub3JlZC5cbiAqICBCdXQsIGluIHNvbWUgY2FzZXMsIHRoZSB1c2VyIG11c3QgYmUgYXdhcmUgb2YgdGhlIGFnZW50IChjb250cmFjdCkgKGZvciBleGFtcGxlLCAgdG8gYXBwcm92ZSB0cmFuc2ZlcnMpLlxuICpcbiAqICBUeXBpY2FsbHksIGFsbCB0aGUgdXNlciB3aWxsIG5lZWQgdG8ga25vdyBpcywgb2NjYXNpb25hbGx5LCB0byBnZXQgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGFnZW50IChsaWtlIGl0cyBjb250cmFjdCBhZGRyZXNzKVxuICogIHZpYSBhY2Nlc3NpbmcgaXRzIG9iamVjdDpcbiAqICAgICAgY29uc3QgY29udHJhY3RBZGRyZXNzOiBzdHJpbmcgPSBFdWxpdGguT25DaGFpbkFnZW50cy5nZXRBZ2VudCh7cHJvdmlkZXIsIGF1dGhvcml6ZWRBZGRyZXNzfSkuY29udHJhY3RBZGRyZXNzICAgIE9SXG4gKiAgICAgIGNvbnN0IGNvbnRyYWN0QWRkcmVzczogc3RyaW5nID0gRXVsaXRoLk9uQ2hhaW5BZ2VudHMuZ2V0QWdlbnQoe3Byb3ZpZGVyLCBhdXRob3JpemllZFNpZ25lcn0pLmNvbnRyYWN0QWRkcmVzc1xuICovXG5leHBvcnQgbW9kdWxlIE9uQ2hhaW5BZ2VudHMge1xuICAgIC8qKlxuICAgICAqICBAdHlwZWRlZiBFdWxpdGguT25DaGFpbkFnZW50cy5UeXBlXG4gICAgICovXG4gICAgZXhwb3J0IGVudW0gVHlwZSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiAgQW4gVW5jaGVja2VkIEFnZW50IGlzIHRoZSBoaXN0b3JpY2FsIG9uZSwgYmFzaWMgb25lLCBvZiBvZiBwcmUtYXByaWwgMjAyMy5cbiAgICAgICAgICpcbiAgICAgICAgICogIEl0IHByb3ZpZGVzIGxlc3MgY2hlY2tpbmcgYW5kIHNlY3VyaXR5IHRoYW4gdGhlIGFybW9yIGNvbnRyYWN0cy5cbiAgICAgICAgICpcbiAgICAgICAgICogIEFzc29jaWF0ZWQgdmFsdWUgJ2NhbGwnIGlzIGhpc3RvcmljYWwuXG4gICAgICAgICAqL1xuICAgICAgICBVbmNoZWNrZWQgPSBcImNhbGxcIixcblxuICAgICAgICAvKipcbiAgICAgICAgICogIEFuIEFybW9yIEFnZW50IGlzIG9uZSB0aGF0IHVzZXMgYSBTYWZlIChzZWUgVVJMKVxuICAgICAgICAgKi9cbiAgICAgICAgQXJtb3IgPSBcImFybW9yXCJcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiAgQHR5cGVkZWYgRXVsaXRoLk9uQ2hhaW5BZ2VudHMuSUFnZW50XG4gICAgICpcbiAgICAgKiAgICAgQW4gQWdlbnQgaXMgYSBFdWxpdGggb2JqZWN0IHRoYXQgbGl2ZXMgb24gdGhlIEV0aGVyZXVtIG5ldHdvcmsgKGl0cyBhIHNwZWNpYWwgY29udHJhY3QgaW5zdGFuY2UpLCB0aGF0IGNhbiBhY3Qgb24gaXRzICdvd25lcnMnIChyZWFsbHkgYXV0aG9yaXplZCBhZGRyZXNzKSBiZWhhbGYuXG4gICAgICpcbiAgICAgKiBBbGwgeW91IG5lZWQgaXMgdG8gYmUgYWJsZSB0byBzaWduIHJlcXVlc3RzIHVzaW5nIHRoZSBzaWduZXIgKHdpdGggdGhlIGFkZHJlc3MgPT0gdGhlIGF1dGhvcml6ZWQgYWRkcmVzcykuXG4gICAgICpcbiAgICAgKiBUaGVuIHlvdSBjYW4gY29tYmluZSBtdWx0aXBsZSB0cmFuc2FjdGlvbnMgaW50byBhdG9taWMgdHJhbnNhY3Rpb25zLCBhbmQgcGVyZm9ybSBvdGhlciBkZWxlZ2F0aW5nIGFjdGlvbnMgdGhyb3VnaCB0aGUgYWdlbnQuXG4gICAgICovXG4gICAgZXhwb3J0IGludGVyZmFjZSBJQWdlbnQge1xuICAgICAgICByZWFkb25seSB0eXBlOiBUeXBlO1xuICAgICAgICByZWFkb25seSBjb250cmFjdEFkZHJlc3M6IHN0cmluZztcbiAgICAgICAgcmVhZG9ubHkgYXV0aG9yaXplZEFkZHJlc3M6IHN0cmluZztcblxuICAgICAgICAvLyAvLyBBbiBBUEkgTElLRSB0aGlzIHdvdWxkIHByb2JhYmx5IGJlIFNhZmUsIGFuZCByZWFzb25hYmxlLCBhbmQgcGVyaGFwcyB1c2VmdWwsIGJ1dCBub3QgYSBwcmlvcml0eSBhdCB0aGlzIFRpbWU7IGluY2x1ZGVkIG1lcmVseSBhcyBhIGNvbW1lbnRcbiAgICAgICAgLy8gLy8gVG8gbWFrZSB0aGUgc3RydWN0dXJlIG9mIHRoaXMgY29kZS9kZXNpZ24gbW9yZSBjbGVhclxuICAgICAgICAvLyAvLyBUaGUgRnVuY3Rpb24gb2YgdGhlIEFQSSB3b3VsZCBiZSB0byB1bnJlZ2lzdGVyIHRoZSBhc3NvY2lhdGVkIGNvbnRyYWN0IGFuZCBmcmVlLXVwIGFuZCByZXNvdXJjZXMgdXNlZCBieSBpdCwgYW5kIGFsbG93IHRoZSBzaWduZXIgdG8gYmVcbiAgICAgICAgLy8gLy8gdXNlZCBpbiBhIG5ldyBzdHlsZSBhcmdlbnQgKGUuZyBzd2l0Y2ggZnJvbiBVbmNoZWNrZWQgYWdlbnQgdG8gYXJtb3IgYWdlbnQpLlxuICAgICAgICAvLyByZW1vdmVBZ2VudCh7IHByb3ZpZGVyLCAgIGF1dGhvcml6ZWRTaWduZXIgfTogeyBwcm92aWRlcjogRXVsaXRoLlByb3ZpZGVyIHwgRXVsaXRoLldlYjM7ICBhdXRob3JpemVkU2lnbmVyOiBFdWxpdGguU2lnbmluZy5TaWduaW5nU2VydmljZSB8IEV1bGl0aC5TaWduaW5nLklDcnlwdG9ncmFwaGljU2lnbmVyOyB9KTogUHJvbWlzZTx2b2lkPjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiAgQHR5cGVkZWYgRXVsaXRoLk9uQ2hhaW5BZ2VudHMuSUFybW9yQWdlbnRcbiAgICAgKi9cbiAgICBleHBvcnQgaW50ZXJmYWNlIElBcm1vckFnZW50IGV4dGVuZHMgSUFnZW50IHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqICBBbiBhcm1vciBhZ2VudCBpcyBhc3NvY2lhdGVkIHdpdGggYSAoZ25vc2lzKSBzYWZlIG9uIHRoZSBldGhlcmV1bSBibG9ja2NoYWluIG5ldHdvcmsuIFRoaXMgaXMgdGhlIHNhZmVBZGRyZXNzIChldGhlcmV1bSBhZGRyZXNzKVxuICAgICAgICAgKiAgb2YgdGhlIHNhZmUuXG4gICAgICAgICAqL1xuICAgICAgICByZWFkb25seSBzYWZlQWRkcmVzczogc3RyaW5nO1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiAgYXV0aG9yaXppbmdPd25lciAob3duZXIgb2YgdGhlIHNhZmUpIGF1dGhvcml6ZXMgdGhpcyBhcm1vckFnZW50IHRvIHV0aWxpemUgaXRzIGFzc29jaWF0ZWQgc2FmZS4gVGhlIGF1dGhvcml6aW5nT3duZXJcbiAgICAgICAgICogIHNpZ25lciB3aWxsIGJlIGFza2VkIHRvIHNpZ24gYSBzdHJ1Y3R1cmUgd2hpY2ggcGVybWl0cyB0aGUgYXV0aG9yaXplZFNpZ25lciBhc3NvY2lhdGVkIHdpdGggdGhpcyBhZ2VudFxuICAgICAgICAgKiAgdG8gdXRpbGl6ZSB0aGUgc2FmZSBhcyBpdCBzZWVzIGZpdC5cbiAgICAgICAgICpcbiAgICAgICAgICogICAgICBAdG9kbzogU1VHR0VTVEVEIENIQU5HRSBUTyBFWElTVElORyBTRU1BTlRJQ1M6XG4gICAgICAgICAqICAgICAgICAgIGF1dGhvcml6aW5nT3duZXIuYWRkcmVzcyBtdXN0IGFscmVhZHkgYmUgbGlzdGVkIGFzIGFuIG93bmVyIG9mIHRoZSBzYWZlLlxuICAgICAgICAgKlxuICAgICAgICAgKiAgTk9URSAtIGF1dGhvcml6aW5nT3duZXIgbXVzdCBzdXBwb3J0IHNpZ25UeXBlZERhdGEgKGkuZS4gaGF2ZSBhIG5vbi1udWxsIHR5cGVkRGF0YVNpZ25lciBwcm9wZXJ0eSBvZiBTaWduaW5nU2VydmljZSBvciBiZSBhIElDcnlwdG9ncmFwaGljU2lnbmVyKVxuICAgICAgICAgKi9cbiAgICAgICAgYXV0aG9yaXplRm9yT3duZXIoe1xuICAgICAgICAgICAgcHJvdmlkZXIsXG4gICAgICAgICAgICBhdXRob3JpemluZ093bmVyXG4gICAgICAgIH06IHtcbiAgICAgICAgICAgIHByb3ZpZGVyOiBFdWxpdGguUHJvdmlkZXIgfCBFdWxpdGguV2ViMztcbiAgICAgICAgICAgIGF1dGhvcml6aW5nT3duZXI6IEV1bGl0aC5TaWduaW5nLklDcnlwdG9ncmFwaGljU2lnbmVyIHwgRXVsaXRoLlNpZ25pbmcuU2lnbmluZ1NlcnZpY2U7XG4gICAgICAgIH0pOiBQcm9taXNlPHZvaWQ+O1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiAgcGVyZm9ybSB0aGUgdmFyaW91cyB0cmFuc2FjdGlvbnMgcmVxdWlyZWQgdG8gZW5hYmxlIHRoZSBhcm1vciBmb3IgdGhlIGFyZ3VtZW50IHNhZmUuXG4gICAgICAgICAqXG4gICAgICAgICAqICBUaGlzIHJlcXVpcmVzIHNpZ25lckZvclRoaXNUeCwgdG8gYWN0dWF0ZSB0aGlzIG9wZXJhdGlvbiwgYW5kIHBheSB0aGUgZ2FzIGZvciB0aGUgdHJhbnNhY3Rpb24uIEJ1dFxuICAgICAgICAgKiAgc2lnbmVyRm9yVGhpc1R4IG1heSBiZSBhbnkgc2lnbmVyLCBub3QgY29ubmVjdGVkIHRvIHRoZSBzYWZlLlxuICAgICAgICAgKlxuICAgICAgICAgKiAgQHRvZG8gc29tZSBmdXR1cmUgdmVyc2lvbiBvZiB0aGlzIEFQSSBtYXkgbW92ZSB0aGUgJ2ZvclNhZmUnIG9iamVjdCB0byB0aGUgY3JlYXRlQXJtb3IgY2FsbFxuICAgICAgICAgKi9cbiAgICAgICAgZW5hYmxlQXJtb3Ioe1xuICAgICAgICAgICAgcHJvdmlkZXIsXG4gICAgICAgICAgICBzaWduZXJGb3JUaGlzVHgsXG4gICAgICAgICAgICBmb3JTYWZlXG4gICAgICAgIH06IHtcbiAgICAgICAgICAgIHByb3ZpZGVyOiBFdWxpdGguUHJvdmlkZXIgfCBFdWxpdGguV2ViMztcbiAgICAgICAgICAgIHNpZ25lckZvclRoaXNUeDogRXVsaXRoLlNpZ25pbmcuSUNyeXB0b2dyYXBoaWNTaWduZXIgfCBFdWxpdGguU2lnbmluZy5TaWduaW5nU2VydmljZTtcbiAgICAgICAgICAgIGZvclNhZmU6IHsgc2FmZUFkZHJlc3M/OiBzdHJpbmc7IG5ld1NhZmU/OiB7IG93bmVyczogc3RyaW5nW107IGFwcHJvdmFsVGhyZXNob2xkOiBudW1iZXIgfSB9O1xuICAgICAgICB9KTogUHJvbWlzZTx2b2lkPjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiAgRXVsaXRoLk9uQ2hhaW5BZ2VudHMuZ2V0QWdlbnRcbiAgICAgKlxuICAgICAqICBGZXRjaCB0aGUgY3VycmVudCBhZ2VudCByZWZlcm5lY2UgZm9yIHRoZSBhcmd1bWVudCBhdXRob3JpemVkIGFkZHJlc3MgKG9yIGF1dGhvcml6aWVkU2lnbmVyKS5cbiAgICAgKiAgcHJvdmlkZXIgKG1heWJlIEV1bGl0aC5Qcm92aWRlciB8IEV1bGl0aC5XZWIzKSB1c2VkIHRvIGNvbW11bmljYXRlIHdpdGggdGhlIEV0aGVyZXVtIG5ldHdvcmsuXG4gICAgICogIGNyZWF0ZVVuY2hlY2tlZEFnZW50SWZOb25lRXhpc3RzIC0gZGVmYXVsdHMgdG8gdHJ1ZSBpZiBub3Qgc3BlY2lmaWVkLlxuICAgICAqXG4gICAgICogICBOb3RlIC0gdGhlcmUgY2FuIG5ldmVyIGJlIG1vcmUgdGhhbiBvbmUgSUFnZW50IGFzc29jaWF0ZWQgd2l0aCBhIGdpdmVuIGF1dGhvcml6ZWRBZGRyZXNzLlxuICAgICAqXG4gICAgICogIFNlZSBhbHNvIEV1bGl0aC5PbkNoYWluQWdlbnRzLmNyZWF0ZVVuY2hlY2tlZEFnZW50XG4gICAgICogIFNlZSBhbHNvIEV1bGl0aC5PbkNoYWluQWdlbnRzLmNyZWF0ZUFybW9yQWdlbnRcbiAgICAgKi9cbiAgICBleHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0QWdlbnQoe1xuICAgICAgICBwcm92aWRlcixcbiAgICAgICAgYXV0aG9yaXplZEFkZHJlc3MsXG4gICAgICAgIGF1dGhvcml6aWVkU2lnbmVyLFxuICAgICAgICBjcmVhdGVVbmNoZWNrZWRBZ2VudElmTm9uZUV4aXN0c1xuICAgIH06IHtcbiAgICAgICAgcHJvdmlkZXI6IEV1bGl0aC5Qcm92aWRlciB8IEV1bGl0aC5XZWIzO1xuICAgICAgICBhdXRob3JpemVkQWRkcmVzcz86IHN0cmluZztcbiAgICAgICAgYXV0aG9yaXppZWRTaWduZXI/OiBFdWxpdGguU2lnbmluZy5TaWduaW5nU2VydmljZSB8IEV1bGl0aC5TaWduaW5nLklDcnlwdG9ncmFwaGljU2lnbmVyO1xuICAgICAgICBjcmVhdGVVbmNoZWNrZWRBZ2VudElmTm9uZUV4aXN0cz86IGJvb2xlYW47XG4gICAgfSk6IFByb21pc2U8SUFnZW50PiB7XG4gICAgICAgIGNvbnN0IHVzZVByb3ZpZGVyID0gRXVsaXRoLlByb3ZpZGVyLlByb3ZpZGVyT3JXZWIzKHByb3ZpZGVyKTtcbiAgICAgICAgY29uc3QgdXNlQXV0aG9yaXplZEFkZHJlc3MgPSBhdXRob3JpemVkQWRkcmVzcyA/PyBhdXRob3JpemllZFNpZ25lci5hZGRyZXNzO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgciA9IGF3YWl0IGZldGNoQ29udHJhY3REZXRhaWxzRm9yQXV0aG9yaXplZEFkZHJlc3NfKHVzZVByb3ZpZGVyLCB1c2VBdXRob3JpemVkQWRkcmVzcyk7XG4gICAgICAgICAgICBzd2l0Y2ggKHIuY29udHJhY3RfdHlwZSkge1xuICAgICAgICAgICAgICAgIGNhc2UgXCJjYWxsXCI6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBuZXcgVW5jaGVja2VkQWdlbnRfKHIuY29udHJhY3RfYWRkcmVzcywgdXNlQXV0aG9yaXplZEFkZHJlc3MpO1xuICAgICAgICAgICAgICAgIGNhc2UgXCJhcm1vclwiOlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gbmV3IEFybW9yQWdlbnRfKHIuY29udHJhY3RfYWRkcmVzcywgdXNlQXV0aG9yaXplZEFkZHJlc3MsIHIuc2FmZV9hZGRyZXNzKTtcbiAgICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJnZXRBZ2VudDogdW5yZWNvZ25pemVkIGNvbnRyYWN0LXR5cGVcIik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGlmIChjcmVhdGVVbmNoZWNrZWRBZ2VudElmTm9uZUV4aXN0cyA/PyB0cnVlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IGNyZWF0ZVVuY2hlY2tlZEFnZW50KHsgcHJvdmlkZXI6IHVzZVByb3ZpZGVyLCBhdXRob3JpemVkQWRkcmVzczogdXNlQXV0aG9yaXplZEFkZHJlc3MgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogIEV1bGl0aC5PbkNoYWluQWdlbnRzLmNyZWF0ZVVuY2hlY2tlZEFnZW50IGlzIHByb3ZpZGVkIGZvciBsb2dpY2FsIGNvbnNpc3RlbmN5LCBidXQgdHlwaWNhbGx5IHdvbid0IGJlIHVzZWQgZGlyZWN0bHkgLSBpbnN0ZWFkIC0ganVzdCB1c2UgRXVsaXRoLk9uQ2hhaW5BZ2VudHMuZ2V0QWdlbnQoKTtcbiAgICAgKlxuICAgICAqICAgTm90ZSB0aGlzIHJvdXRpbmUgd2lsbCBmYWlsIGlmIHRoZSBhZ2VudCBhbHJlYWR5IGV4aXN0cyAoYW5vdGhlciByZWFzb24gdG8ganVzdCBjYWxsIEV1bGl0aC5PbkNoYWluQWdlbnRzLmdldEFnZW50ICgpKVxuICAgICAqL1xuICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVVbmNoZWNrZWRBZ2VudCh7XG4gICAgICAgIHByb3ZpZGVyLFxuICAgICAgICBhdXRob3JpemVkQWRkcmVzc1xuICAgIH06IHtcbiAgICAgICAgcHJvdmlkZXI6IEV1bGl0aC5Qcm92aWRlciB8IEV1bGl0aC5XZWIzO1xuICAgICAgICBhdXRob3JpemVkQWRkcmVzczogc3RyaW5nO1xuICAgIH0pOiBQcm9taXNlPElBZ2VudD4ge1xuICAgICAgICBjb25zdCB1c2VQcm92aWRlciA9IEV1bGl0aC5Qcm92aWRlci5Qcm92aWRlck9yV2ViMyhwcm92aWRlcik7XG4gICAgICAgIGNvbnN0IGNvbnN0cmFjdEFkZHJlc3MgPSBhd2FpdCBjcmVhdGVVbmNoZWNrZWRfKHVzZVByb3ZpZGVyLCBhdXRob3JpemVkQWRkcmVzcyk7XG4gICAgICAgIHJldHVybiBuZXcgVW5jaGVja2VkQWdlbnRfKGNvbnN0cmFjdEFkZHJlc3MsIGF1dGhvcml6ZWRBZGRyZXNzKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiAgRXVsaXRoLk9uQ2hhaW5BZ2VudHMuY3JlYXRlQXJtb3JBZ2VudFxuICAgICAqXG4gICAgICogIEEgRXVsaXRoICdhcm1vcicgYWdlbnQsIGlzIGxpa2UgYW55IEV1bGl0aCBhZ2VudCwgYWxsb3dpbmcgeW91IHRvIGF1dG9tYXRlIGEgc2VxdWVuY2Ugb2Ygb3BlcmF0aW9ucyBvbiB0aGVcbiAgICAgKiAgRXRoZXJldW0gbmV0d29yaywgYnV0IGluIHRoaXMgY2FzZSwgbXVjaCBvZiB0aGF0IGF1dG9tYXRpb24gaXMgYWN0dWFsbHkgcGVyZm9ybWVkIGJ5IGEgJ3NhZmUnIDxodHRwczovL3NhZmUuZ2xvYmFsLz5cbiAgICAgKlxuICAgICAqICBUaGlzIGNyZWF0aW9uIHByb2Nlc3MgbWF5IG9wdGlvbmFsbHkgZWl0aGVyIGNyZWF0ZSB5b3VyIHNhZmUsIG9yIHJlLXVzZSBhbiBleGlzdGluZyBzYWZlLiBCdXQgZWl0aGVyIHdheSwgaXQgcmVnaXN0ZXJzXG4gICAgICogIGEgRXVsaXRoIGFybW9yIGFnZW50IGFzIGEgbW9kdWxlIG9uIHRoYXQgc2FmZSwgYWxsb3dpbmcgaXQgdG8gZGlyZWN0IHRoZSBzYWZlIHRvIHBlcmZvcm0gdHJhbnNhY3Rpb25zIG9uIGl0J3MgYmVoYWxmXG4gICAgICogICh0cmFuc2l0aXZlbHkgb24geW91ciBiZWhhbGYsIHRyaWdnZXJlZCBieSB5b3VyIHVzZSBvZiBhbiAnYXV0aG9yaXplZFNpZ25lcicpLlxuICAgICAqXG4gICAgICogIFlvdSBvd24vY29udHJvbCB0aGF0IGF1dGhvcml6ZWRTaWduZXIuIFRoaXMgYWxsb3dzIHRoZSBzYWZlIHRvIG9wZXJhdGVkIGJ5IGEgU0lOR0xFIHNpZ25lciwgaW5zdGVhZCBvZiB0aGUgcmVxdWlyaW5nIHRoZSBOIChzYWZlIHRocmVzaG9sZClcbiAgICAgKiAgc2lnbmVycy5cbiAgICAgKlxuICAgICAqICBUaGUgYmFzaWMgcHJvY2VzcyBpczpcbiAgICAgKlxuICAgICAqICAgICAgLSAgIGNvbnN0cnVjdCBhbiBhcm1vciBjb250cmFjdCB3aXRoIHRoaXMgQVBJIChwYXNzaW5nIGluIGEgc2FmZSwgb3IgdGhlIGRhdGEgbmVlZGVkIHRvIGNyZWF0ZSBhIHNhZmUpLlxuICAgICAqICAgICAgLSAgIGdldCB0aGUgb3duZXJzIG9mIHRoZSBzYWZlIHRvICdzaWduJyB0aGUgcmVxdWVzdCB0byBlbmFibGUgdGhlIEV1bGl0aCBBcm1vciBhZ2VudC5cbiAgICAgKiAgICAgIC0gICBlbmFibGVBcm1vciAoKSAtIHdoaWNoIGNvbXBsZXRlcyB0aGUgc2V0dXAgLSBhY3RpdmF0aW5nIHlvdXIgc2FmZSBhbmQgeW91ciBhdXRob3JpemVkIHNpZ25lcnMgYWJpbGl0eSB0byB1c2UgaXRcbiAgICAgKiAgICAgIC0gICB0aGVuIHN0YXJ0IHVzaW5nIHlvdXIgYXV0aG9yaXplZCBzaWduZXIgLSBpdHMgbm93IGFsbG93ZWQgdG8gdXNlIHRoZSBjb250ZW50cyBvZiB0aGlzIHNhZmUuXG4gICAgICpcbiAgICAgKiAgcHJvdmlkZXIgaXMgdGhlIEV1bGl0aCBwcm92aWRlciBFdGhlcmV1bSBuZXR3b3JrIHByb3ZpZGVyIChvciBhIEV1bGl0aC5XZWIgZnJvbSB3aGljaCBhIHByb3ZpZGVyIGNhbiBiZSBleHRyYWN0ZWQpXG4gICAgICpcbiAgICAgKiAgYXV0aG9yaXplZEFkZHJlc3MgaXMgdGhlIGFkZHJlc3Mgb2Ygc29tZSB1c2VyLWNvbnRyb2xsZWQgc2lnbmVyICh0eXBpY2FsbHkgS01TLWJhc2VkLCBmb3IgZXhhbXBsZSksXG4gICAgICogICAgICB3aGljaCB3aWxsIGJlIHVzZWQgdG8gT1BFUkFURSBhbmQgbWFrZSByZXF1ZXN0cyBvbiBzb21lIGFzc29jaWF0ZWQgc2FmZSAodmlhIHRoZSBhcm1vciBhZ2VudCkuXG4gICAgICpcbiAgICAgKiAgc2FmZUFkZHJlc3MgaXMgdGhlIGFkZHJlc3Mgb2YgdGhlIGV4aXN0aW5nIHNhZmUgdG8gYXNzb2NpYXRlIHdpdGggdGhlIG5ldyBhcm1vciwgYW5kIGlmIG51bGwgb3Igb21pdHRlZFxuICAgICAqICAgICAgYSBuZXcgc2FmZSB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgY3JlYXRlZC5cbiAgICAgKlxuICAgICAqICBOT1RFLCBpZiBzYWZlQWRkcmVzcyBpcyBub3Qgc3BlY2lmaWVkLCB0aGUgc2FmZSB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgY3JlYXRlZCBieSB0aGUgRXVsaXRoIHNlcnZlclxuICAgICAqICBhbmQgb3duZXJzIGV0YyBjYWxjdWxhdGVkIGF1dG9tYXRpY2FsbHkuXG4gICAgICpcbiAgICAgKiAgc2V0dXBTaWduZXI6IGEgc2lnbmVyIGlzIG5lZWRlZCB0byBzZW5kIGFueSBldGhlcmV1bSB0cmFuc2FjdGlvbiBtZXNzYWdlLCBidXQgd2hpY2ggb25lIGlzIHVzZWQgaGVyZSBkb2VzIG5vdCBtYXR0ZXIsIGV4Y2VwdCB0aGF0IGl0IHBheXMgdGhlXG4gICAgICogIGdhcyBmb3IgdGhpcyBzZXR1cCB0cmFuc2FjdGlvbi5cbiAgICAgKlxuICAgICAqICBOb3RlIC0gdGhpcyB3aWxsIEZBSUwgaWYgYW55IEV1bGl0aCBBZ2VudCBhbHJlYWR5IGV4aXN0cyBmb3IgdGhpcyBhdXRob3JpemVkQWRkcmVzcy5cbiAgICAgKlxuICAgICAqICBFeGFjdGx5IG9uZSBvZiBzYWZlQWRkcmVzcyAoY2FzZSBvZiBleGlzdGluZyBzYWZlKSwgb3Igc2FmZUNyZWF0aW9uUGFyYW10ZXJzLCBtdXN0IGJlIHByb3ZpZGVkIChOT1RFIEtSSVNUSUFOIC0gVEhJUyBJUyBBIERFUEFSVFVSRSBGUk9NIFlPVVIgQ1VSUkVOVCBBUEkgLSBBIFNVR0dFU1RJT04pLlxuICAgICAqL1xuICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVBcm1vckFnZW50KHtcbiAgICAgICAgcHJvdmlkZXIsXG4gICAgICAgIGF1dGhvcml6ZWRBZGRyZXNzLFxuICAgICAgICBzYWZlQWRkcmVzcyxcbiAgICAgICAgc2V0dXBTaWduZXJcbiAgICB9OiB7XG4gICAgICAgIHByb3ZpZGVyOiBFdWxpdGguUHJvdmlkZXIgfCBFdWxpdGguV2ViMztcbiAgICAgICAgYXV0aG9yaXplZEFkZHJlc3M6IHN0cmluZztcbiAgICAgICAgc2FmZUFkZHJlc3M/OiBzdHJpbmc7XG4gICAgICAgIHNldHVwU2lnbmVyOiBFdWxpdGguU2lnbmluZy5JQ3J5cHRvZ3JhcGhpY1NpZ25lciB8IEV1bGl0aC5TaWduaW5nLlNpZ25pbmdTZXJ2aWNlO1xuICAgIH0pOiBQcm9taXNlPElBZ2VudD4ge1xuICAgICAgICBjb25zdCB1c2VQcm92aWRlciA9IEV1bGl0aC5Qcm92aWRlci5Qcm92aWRlck9yV2ViMyhwcm92aWRlcik7XG5cbiAgICAgICAgYXNzZXJ0Lm5vdEVxdWFsKHByb3ZpZGVyLCBudWxsLCBcInByb3ZpZGVyIGlzIHJlcXVpcmVkIGZvciBFdWxpdGguT25DaGFpbkFnZW50cy5jcmVhdGVBcm1vckFnZW50XCIpO1xuICAgICAgICBhc3NlcnQubm90RXF1YWwoXG4gICAgICAgICAgICBhdXRob3JpemVkQWRkcmVzcyxcbiAgICAgICAgICAgIG51bGwsXG4gICAgICAgICAgICBcImF1dGhvcml6ZWRBZGRyZXNzIGlzIHJlcXVpcmVkIGZvciBFdWxpdGguT25DaGFpbkFnZW50cy5jcmVhdGVBcm1vckFnZW50XCJcbiAgICAgICAgKTtcbiAgICAgICAgYXNzZXJ0Lm5vdEVxdWFsKHNldHVwU2lnbmVyLCBudWxsLCBcInNldHVwU2lnbmVyIGlzIHJlcXVpcmVkIGZvciBFdWxpdGguT25DaGFpbkFnZW50cy5jcmVhdGVBcm1vckFnZW50XCIpO1xuXG4gICAgICAgIGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUFybW9yQ29udHJhY3RJbkV1bGl0aChcbiAgICAgICAgICAgIHByb3ZpZGVyOiBFdWxpdGguUHJvdmlkZXIsXG4gICAgICAgICAgICBhdXRob3JpemVkQWRkcmVzczogc3RyaW5nLFxuICAgICAgICAgICAgcHJlZXhpc3RpbmdTYWZlQWRkcmVzcz86IHN0cmluZ1xuICAgICAgICApOiBQcm9taXNlPEV1bGl0aC5TaWduaW5nLlVuc2lnbmVkVHJhbnNhY3Rpb24+IHtcbiAgICAgICAgICAgIC8vIEBLcmlzdGlhbi9Nb2gvSWFuOiBJIHN1Z2dlc3QgcmVwbGFjaW5nIHRoZSBzYWZlX2FscmVhZHlfZXhpc3RzIHBhcmFtZXRlciB3aXRoIGEgcHJlZXhpc3Rpbmdfc2F2ZV9hZGRyZXNzIHBhcmFtZXRlciwgd2hpY2ggY2FuIGJlIG51bGxcbiAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHByb3ZpZGVyLnJlcXVlc3Qoe1xuICAgICAgICAgICAgICAgIG1ldGhvZDogXCJldWxpdGhfbmV3X2NvbnRyYWN0XCIsXG4gICAgICAgICAgICAgICAgcGFyYW1zOiBbXG4g