UNPKG

eulith-web3js-core

Version:

Eulith core web3js SDK (code to access Eulith services via web3js)

374 lines 63.3 kB
"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