@neo-one/server-plugin-network
Version:
NEO•ONE Server network plugin.
453 lines (452 loc) • 63.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const client_common_1 = require("@neo-one/client-common");
const client_core_1 = require("@neo-one/client-core");
const client_full_core_1 = require("@neo-one/client-full-core");
const node_core_1 = require("@neo-one/node-core");
const server_plugin_1 = require("@neo-one/server-plugin");
const utils_1 = require("@neo-one/utils");
const fs = tslib_1.__importStar(require("fs-extra"));
const path = tslib_1.__importStar(require("path"));
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const constants_1 = require("./constants");
const node_1 = require("./node");
const types_1 = require("./types");
const NODES_PATH = 'nodes';
const NODES_OPTIONS_PATH = 'options';
const DEFAULT_MAIN_SEEDS = [
{ type: 'tcp', host: 'node1.nyc3.bridgeprotocol.io', port: 10333 },
{ type: 'tcp', host: 'node2.nyc3.bridgeprotocol.io', port: 10333 },
{ type: 'tcp', host: 'seed1.switcheo.com', port: 10333 },
{ type: 'tcp', host: 'seed2.switcheo.com', port: 10333 },
{ type: 'tcp', host: 'seed3.switcheo.com', port: 10333 },
{ type: 'tcp', host: 'seed1.aphelion-neo.com', port: 10333 },
{ type: 'tcp', host: 'seed2.aphelion-neo.com', port: 10333 },
{ type: 'tcp', host: 'seed3.aphelion-neo.com', port: 10333 },
{ type: 'tcp', host: 'seed4.aphelion-neo.com', port: 10333 },
];
const DEFAULT_TEST_SEEDS = [
{ type: 'tcp', host: 'seed1.neo.org', port: 20333 },
{ type: 'tcp', host: 'seed2.neo.org', port: 20333 },
{ type: 'tcp', host: 'seed3.neo.org', port: 20333 },
{ type: 'tcp', host: 'seed4.neo.org', port: 20333 },
{ type: 'tcp', host: 'seed5.neo.org', port: 20333 },
];
class NetworkResourceAdapter {
constructor({ name, type, dataPath, resourceType, nodesPath, nodesOptionsPath, nodes: nodesIn, }) {
this.live = async () => {
await Promise.all(this.nodes.map(async (node) => {
try {
await node.live(30);
}
catch (_a) {
await node.stop();
await node.start();
await node.live(30);
}
}));
};
this.ready = async () => {
await Promise.all(this.nodes.map(async (node) => {
try {
await node.ready(30);
}
catch (_a) {
await node.stop();
await node.start();
await node.ready(30);
}
}));
};
this.name = name;
this.type = type;
this.dataPath = dataPath;
this.resourceType = resourceType;
this.nodesPath = nodesPath;
this.nodesOptionsPath = nodesOptionsPath;
this.nodes$ = new rxjs_1.BehaviorSubject(nodesIn);
this.state = 'stopped';
this.resource$ = this.nodes$.pipe(operators_1.switchMap((nodes) => rxjs_1.combineLatest([rxjs_1.timer(0, 2500), rxjs_1.combineLatest(nodes.map((node) => node.node$))]).pipe(utils_1.mergeScanLatest(async (_prev, [_time, currentNodes]) => {
const readyNode = currentNodes.find((node) => node.ready) ||
currentNodes.find((node) => node.live) ||
currentNodes[0];
let height;
let peers;
if (readyNode !== undefined) {
const client = new client_full_core_1.ReadClient(new client_core_1.NEOONEDataProvider({
network: this.name,
rpcURL: readyNode.rpcAddress,
}));
try {
[height, peers] = await Promise.all([client.getBlockCount(), client.getConnectedPeers()]);
}
catch (_a) {
}
}
return {
plugin: this.resourceType.plugin.name,
resourceType: this.resourceType.name,
name: this.name,
baseName: this.name,
state: this.state,
type: this.type,
height,
peers: peers === undefined ? peers : peers.length,
nodes: currentNodes,
live: this.live,
ready: this.ready,
};
}))), operators_1.shareReplay(1));
}
static async init(options) {
const staticOptions = this.getStaticOptions(options);
const files = await fs.readdir(staticOptions.nodesOptionsPath);
const nodeOptionss = await Promise.all(files.map(async (file) => this.readNodeOptions(staticOptions, path.resolve(staticOptions.nodesOptionsPath, file))));
const nodes = nodeOptionss.map((nodeOptions) => this.createNodeAdapter(staticOptions, nodeOptions));
return new this({
name: staticOptions.name,
binary: staticOptions.binary,
dataPath: staticOptions.dataPath,
portAllocator: staticOptions.portAllocator,
resourceType: staticOptions.resourceType,
nodesPath: staticOptions.nodesPath,
nodesOptionsPath: staticOptions.nodesOptionsPath,
type: nodeOptionss[0].type,
nodes,
});
}
static create(adapterOptions, options) {
const staticOptions = this.getStaticOptions(adapterOptions);
let type;
let nodeSettings;
if (staticOptions.name === constants_1.constants.NETWORK_NAME.MAIN) {
type = types_1.NetworkType.Main;
nodeSettings = [[staticOptions.name, this.getMainSettings(staticOptions)]];
}
else if (staticOptions.name === constants_1.constants.NETWORK_NAME.TEST) {
type = types_1.NetworkType.Test;
nodeSettings = [[staticOptions.name, this.getTestSettings(staticOptions)]];
}
else {
type = types_1.NetworkType.Private;
nodeSettings = this.getPrivateNetSettings(staticOptions);
}
const nodeOptionss = nodeSettings.map(([name, settings]) => ({
type,
name,
dataPath: path.resolve(staticOptions.nodesPath, name),
settings,
options,
}));
const nodeOptionsAndNodes = nodeOptionss.map((nodeOptions) => [
nodeOptions,
this.createNodeAdapter(staticOptions, nodeOptions),
]);
return new server_plugin_1.TaskList({
initialContext: {
resourceAdapter: new this({
name: staticOptions.name,
binary: staticOptions.binary,
dataPath: staticOptions.dataPath,
portAllocator: staticOptions.portAllocator,
resourceType: staticOptions.resourceType,
nodesPath: staticOptions.nodesPath,
nodesOptionsPath: staticOptions.nodesOptionsPath,
type: nodeSettings[0][1].type,
nodes: nodeOptionsAndNodes.map((value) => value[1]),
}),
dependencies: [],
},
tasks: [
{
title: 'Create data directories',
task: async () => {
await Promise.all([fs.ensureDir(staticOptions.nodesPath), fs.ensureDir(staticOptions.nodesOptionsPath)]);
},
},
{
title: 'Create nodes',
task: () => new server_plugin_1.TaskList({
tasks: nodeOptionsAndNodes.map(([nodeOptions, node]) => ({
title: `Create node ${nodeOptions.name}`,
task: async () => {
await this.writeNodeOptions(staticOptions, nodeOptions);
await node.create();
},
})),
concurrent: true,
}),
},
],
});
}
static getStaticOptions(options) {
return {
name: options.name,
binary: options.binary,
dataPath: options.dataPath,
portAllocator: options.portAllocator,
resourceType: options.resourceType,
nodesPath: path.resolve(options.dataPath, NODES_PATH),
nodesOptionsPath: path.resolve(options.dataPath, NODES_OPTIONS_PATH),
};
}
static getPrivateNetSettings(options) {
const primaryPrivateKey = client_common_1.crypto.wifToPrivateKey(constants_1.constants.PRIVATE_NET_PRIVATE_KEY, client_common_1.common.NEO_PRIVATE_KEY_VERSION);
const primaryPublicKey = client_common_1.common.stringToECPoint(constants_1.constants.PRIVATE_NET_PUBLIC_KEY);
client_common_1.crypto.addPublicKey(primaryPrivateKey, primaryPublicKey);
const primaryAddress = client_common_1.common.uInt160ToString(client_common_1.crypto.privateKeyToScriptHash(primaryPrivateKey));
const configurationName = `${options.name}-0`;
const configuration = [
{
name: configurationName,
rpcPort: this.getRPCPort(options, configurationName),
listenTCPPort: this.getListenTCPPort(options, configurationName),
telemetryPort: this.getTelemetryPort(options, configurationName),
privateKey: primaryPrivateKey,
publicKey: primaryPublicKey,
},
];
const secondsPerBlock = 15;
const standbyValidators = configuration.map(({ publicKey }) => client_common_1.common.ecPointToString(publicKey));
return configuration.map((config) => {
const { name, rpcPort, listenTCPPort, telemetryPort, privateKey } = config;
const otherConfiguration = configuration.filter(({ name: otherName }) => name !== otherName);
const settings = {
type: types_1.NetworkType.Private,
isTestNet: false,
rpcPort,
listenTCPPort,
telemetryPort,
privateNet: true,
secondsPerBlock,
standbyValidators,
address: primaryAddress,
consensus: {
enabled: true,
options: {
privateKey: client_common_1.common.privateKeyToString(privateKey),
privateNet: true,
},
},
seeds: otherConfiguration.map((otherConfig) => node_core_1.createEndpoint({
type: 'tcp',
host: 'localhost',
port: otherConfig.listenTCPPort,
})),
rpcEndpoints: otherConfiguration.map((otherConfig) => `http://localhost:${otherConfig.rpcPort}/rpc`),
};
return [name, settings];
});
}
static getMainSettings(options) {
return {
type: types_1.NetworkType.Main,
isTestNet: false,
rpcPort: this.getRPCPort(options, options.name),
listenTCPPort: this.getListenTCPPort(options, options.name),
telemetryPort: this.getTelemetryPort(options, options.name),
consensus: {
enabled: false,
options: {
privateKey: 'doesntmatter',
privateNet: false,
},
},
seeds: DEFAULT_MAIN_SEEDS.map(node_core_1.createEndpoint),
rpcEndpoints: [
'http://node1.nyc3.bridgeprotocol.io:10332',
'http://node2.nyc3.bridgeprotocol.io:10332',
'https://seed1.switcheo.network:10331',
'https://seed2.switcheo.network:10331',
'https://seed3.switcheo.network:10331',
'http://seed1.aphelion-neo.com:10332',
'http://seed2.aphelion-neo.com:10332',
'http://seed3.aphelion-neo.com:10332',
'http://seed4.aphelion-neo.com:10332',
],
};
}
static getTestSettings(options) {
return {
type: types_1.NetworkType.Test,
isTestNet: true,
rpcPort: this.getRPCPort(options, options.name),
listenTCPPort: this.getListenTCPPort(options, options.name),
telemetryPort: this.getTelemetryPort(options, options.name),
consensus: {
enabled: false,
options: {
privateKey: 'doesntmatter',
privateNet: false,
},
},
seeds: DEFAULT_TEST_SEEDS.map(node_core_1.createEndpoint),
rpcEndpoints: [
'http://test1.cityofzion.io:8880',
'http://test2.cityofzion.io:8880',
'http://test3.cityofzion.io:8880',
'http://test4.cityofzion.io:8880',
'http://test5.cityofzion.io:8880',
'https://seed1.neo.org:20332',
'http://seed2.neo.org:20332',
'http://seed3.neo.org:20332',
'http://seed4.neo.org:20332',
'http://seed5.neo.org:20332',
],
};
}
static createNodeAdapter({ resourceType, binary }, { name, dataPath, settings, options }) {
if (options.type === undefined || options.type === 'neo-one') {
return new node_1.NEOONENodeAdapter({
monitor: resourceType.plugin.monitor,
name,
binary,
dataPath,
settings,
});
}
throw new Error(`Unknown Node type ${options.type}`);
}
static getRPCPort(options, name) {
return this.getPort(options, `${name}-rpc-http`);
}
static getListenTCPPort(options, name) {
return this.getPort(options, `${name}-listen-tcp`);
}
static getTelemetryPort(options, name) {
return this.getPort(options, `${name}-telemetry`);
}
static getPort(options, name) {
return options.portAllocator.allocatePort({
plugin: options.resourceType.plugin.name,
resourceType: options.resourceType.name,
resource: options.name,
name,
});
}
static async writeNodeOptions(options, nodeOptions) {
try {
const nodeOptionsPath = this.getNodeOptionsPath(options, nodeOptions.name);
await fs.writeFile(nodeOptionsPath, JSON.stringify(nodeOptions));
}
catch (error) {
options.resourceType.plugin.monitor
.withData({
[utils_1.labels.NODE_NAME]: nodeOptions.name,
})
.logError({
name: 'neo_network_resource_adapter_write_node_options_error',
message: 'Failed to persist node options',
error,
});
throw error;
}
}
static async readNodeOptions({ resourceType }, nodeOptionsPath) {
try {
const contents = await fs.readFile(nodeOptionsPath, 'utf8');
return JSON.parse(contents);
}
catch (error) {
resourceType.plugin.monitor
.withData({
[utils_1.labels.NODE_OPTIONSPATH]: nodeOptionsPath,
})
.logError({
name: 'neo_network_resource_adapter_read_node_options_error',
message: 'Failed to read node options.',
error,
});
throw error;
}
}
static getNodeOptionsPath({ nodesOptionsPath }, name) {
return path.resolve(nodesOptionsPath, `${name}.json`);
}
getDebug() {
return [
['Type', this.type],
['Data Path', this.dataPath],
['Nodes Path', this.nodesPath],
['Nodes Options Path', this.nodesOptionsPath],
['State', this.state],
[
'Nodes',
{
type: 'describe',
table: this.nodes.map((node) => [node.name, { type: 'describe', table: node.getDebug() }]),
},
],
];
}
get nodes() {
return this.nodes$.value;
}
async destroy() {
this.nodes$.next([]);
}
delete(_options) {
return new server_plugin_1.TaskList({
tasks: [
{
title: 'Clean up local files',
task: async () => {
await fs.remove(this.dataPath);
},
},
],
});
}
start(_options) {
return new server_plugin_1.TaskList({
tasks: [
{
title: 'Start nodes',
task: () => new server_plugin_1.TaskList({
tasks: this.nodes.map((node) => ({
title: `Start node ${node.name}`,
task: async () => {
await node.start();
},
})),
concurrent: true,
}),
},
{
title: 'Wait for network to be alive',
task: async () => {
const start = utils_1.utils.nowSeconds();
await this.live();
this.resourceType.plugin.monitor.log({
name: 'neo_network_resource_adapter_node_live',
message: `Started in ${utils_1.utils.nowSeconds() - start} seconds`,
});
},
},
],
});
}
stop(_options) {
return new server_plugin_1.TaskList({
tasks: [
{
title: 'Stop nodes',
task: () => new server_plugin_1.TaskList({
tasks: this.nodes.map((node) => ({
title: `Stop node ${node.name}`,
task: async () => {
await node.stop();
},
})),
concurrent: true,
}),
},
],
});
}
}
exports.NetworkResourceAdapter = NetworkResourceAdapter;
//# sourceMappingURL=data:application/json;charset=utf8;base64,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d0JBQ0gsVUFBVSxFQUFFLElBQUk7cUJBQ2pCLENBQUM7aUJBQ0w7Z0JBQ0Q7b0JBQ0UsS0FBSyxFQUFFLDhCQUE4QjtvQkFDckMsSUFBSSxFQUFFLEtBQUssSUFBSSxFQUFFO3dCQUNmLE1BQU0sS0FBSyxHQUFHLGFBQUssQ0FBQyxVQUFVLEVBQUUsQ0FBQzt3QkFDakMsTUFBTSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7d0JBQ2xCLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUM7NEJBQ25DLElBQUksRUFBRSx3Q0FBd0M7NEJBQzlDLE9BQU8sRUFBRSxjQUFjLGFBQUssQ0FBQyxVQUFVLEVBQUUsR0FBRyxLQUFLLFVBQVU7eUJBQzVELENBQUMsQ0FBQztvQkFDTCxDQUFDO2lCQUNGO2FBQ0Y7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDO0lBOEJNLElBQUksQ0FBQyxRQUFnQztRQUMxQyxPQUFPLElBQUksd0JBQVEsQ0FBQztZQUNsQixLQUFLLEVBQUU7Z0JBQ0w7b0JBQ0UsS0FBSyxFQUFFLFlBQVk7b0JBQ25CLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FDVCxJQUFJLHdCQUFRLENBQUM7d0JBQ1gsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDOzRCQUMvQixLQUFLLEVBQUUsYUFBYSxJQUFJLENBQUMsSUFBSSxFQUFFOzRCQUMvQixJQUFJLEVBQUUsS0FBSyxJQUFJLEVBQUU7Z0NBQ2YsTUFBTSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7NEJBQ3BCLENBQUM7eUJBQ0YsQ0FBQyxDQUFDO3dCQUNILFVBQVUsRUFBRSxJQUFJO3FCQUNqQixDQUFDO2lCQUNMO2FBQ0Y7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDO0NBQ0Y7QUE3ZkQsd0RBNmZDIiwiZmlsZSI6Im5lby1vbmUtc2VydmVyLXBsdWdpbi1uZXR3b3JrL3NyYy9OZXR3b3JrUmVzb3VyY2VBZGFwdGVyLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY29tbW9uLCBjcnlwdG8gfSBmcm9tICdAbmVvLW9uZS9jbGllbnQtY29tbW9uJztcbmltcG9ydCB7IE5FT09ORURhdGFQcm92aWRlciB9IGZyb20gJ0BuZW8tb25lL2NsaWVudC1jb3JlJztcbmltcG9ydCB7IFJlYWRDbGllbnQgfSBmcm9tICdAbmVvLW9uZS9jbGllbnQtZnVsbC1jb3JlJztcbmltcG9ydCB7IGNyZWF0ZUVuZHBvaW50LCBFbmRwb2ludENvbmZpZyB9IGZyb20gJ0BuZW8tb25lL25vZGUtY29yZSc7XG5pbXBvcnQge1xuICBCaW5hcnksXG4gIERlc2NyaWJlVGFibGUsXG4gIFBvcnRBbGxvY2F0b3IsXG4gIFJlc291cmNlU3RhdGUsXG4gIFN1YkRlc2NyaWJlVGFibGUsXG4gIFRhc2tMaXN0LFxufSBmcm9tICdAbmVvLW9uZS9zZXJ2ZXItcGx1Z2luJztcbmltcG9ydCB7IGxhYmVscywgbWVyZ2VTY2FuTGF0ZXN0LCB1dGlscyB9IGZyb20gJ0BuZW8tb25lL3V0aWxzJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzLWV4dHJhJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIGNvbWJpbmVMYXRlc3QsIE9ic2VydmFibGUsIHRpbWVyIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBzaGFyZVJlcGxheSwgc3dpdGNoTWFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgY29uc3RhbnRzIH0gZnJvbSAnLi9jb25zdGFudHMnO1xuaW1wb3J0IHsgTmV0d29yaywgTmV0d29ya1Jlc291cmNlT3B0aW9ucywgTmV0d29ya1Jlc291cmNlVHlwZSB9IGZyb20gJy4vTmV0d29ya1Jlc291cmNlVHlwZSc7XG5pbXBvcnQgeyBORU9PTkVOb2RlQWRhcHRlciwgTm9kZSwgTm9kZUFkYXB0ZXIgfSBmcm9tICcuL25vZGUnO1xuaW1wb3J0IHsgTmV0d29ya1R5cGUsIE5vZGVTZXR0aW5ncyB9IGZyb20gJy4vdHlwZXMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE5vZGVPcHRpb25zIHtcbiAgcmVhZG9ubHkgdHlwZTogTmV0d29ya1R5cGU7XG4gIHJlYWRvbmx5IG5hbWU6IHN0cmluZztcbiAgcmVhZG9ubHkgZGF0YVBhdGg6IHN0cmluZztcbiAgcmVhZG9ubHkgc2V0dGluZ3M6IE5vZGVTZXR0aW5ncztcbiAgcmVhZG9ubHkgb3B0aW9uczogTmV0d29ya1Jlc291cmNlT3B0aW9ucztcbn1cblxuY29uc3QgTk9ERVNfUEFUSCA9ICdub2Rlcyc7XG5jb25zdCBOT0RFU19PUFRJT05TX1BBVEggPSAnb3B0aW9ucyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTmV0d29ya1Jlc291cmNlQWRhcHRlckluaXRPcHRpb25zIHtcbiAgcmVhZG9ubHkgbmFtZTogc3RyaW5nO1xuICByZWFkb25seSBkYXRhUGF0aDogc3RyaW5nO1xuICByZWFkb25seSBiaW5hcnk6IEJpbmFyeTtcbiAgcmVhZG9ubHkgcG9ydEFsbG9jYXRvcjogUG9ydEFsbG9jYXRvcjtcbiAgcmVhZG9ubHkgcmVzb3VyY2VUeXBlOiBOZXR3b3JrUmVzb3VyY2VUeXBlO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE5ldHdvcmtSZXNvdXJjZUFkYXB0ZXJTdGF0aWNPcHRpb25zIGV4dGVuZHMgTmV0d29ya1Jlc291cmNlQWRhcHRlckluaXRPcHRpb25zIHtcbiAgcmVhZG9ubHkgbm9kZXNQYXRoOiBzdHJpbmc7XG4gIHJlYWRvbmx5IG5vZGVzT3B0aW9uc1BhdGg6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOZXR3b3JrUmVzb3VyY2VBZGFwdGVyT3B0aW9ucyBleHRlbmRzIE5ldHdvcmtSZXNvdXJjZUFkYXB0ZXJTdGF0aWNPcHRpb25zIHtcbiAgcmVhZG9ubHkgdHlwZTogTmV0d29ya1R5cGU7XG4gIHJlYWRvbmx5IG5vZGVzOiByZWFkb25seSBOb2RlQWRhcHRlcltdO1xufVxuXG5jb25zdCBERUZBVUxUX01BSU5fU0VFRFM6IHJlYWRvbmx5IEVuZHBvaW50Q29uZmlnW10gPSBbXG4gIHsgdHlwZTogJ3RjcCcsIGhvc3Q6ICdub2RlMS5ueWMzLmJyaWRnZXByb3RvY29sLmlvJywgcG9ydDogMTAzMzMgfSxcbiAgeyB0eXBlOiAndGNwJywgaG9zdDogJ25vZGUyLm55YzMuYnJpZGdlcHJvdG9jb2wuaW8nLCBwb3J0OiAxMDMzMyB9LFxuICB7IHR5cGU6ICd0Y3AnLCBob3N0OiAnc2VlZDEuc3dpdGNoZW8uY29tJywgcG9ydDogMTAzMzMgfSxcbiAgeyB0eXBlOiAndGNwJywgaG9zdDogJ3NlZWQyLnN3aXRjaGVvLmNvbScsIHBvcnQ6IDEwMzMzIH0sXG4gIHsgdHlwZTogJ3RjcCcsIGhvc3Q6ICdzZWVkMy5zd2l0Y2hlby5jb20nLCBwb3J0OiAxMDMzMyB9LFxuICB7IHR5cGU6ICd0Y3AnLCBob3N0OiAnc2VlZDEuYXBoZWxpb24tbmVvLmNvbScsIHBvcnQ6IDEwMzMzIH0sXG4gIHsgdHlwZTogJ3RjcCcsIGhvc3Q6ICdzZWVkMi5hcGhlbGlvbi1uZW8uY29tJywgcG9ydDogMTAzMzMgfSxcbiAgeyB0eXBlOiAndGNwJywgaG9zdDogJ3NlZWQzLmFwaGVsaW9uLW5lby5jb20nLCBwb3J0OiAxMDMzMyB9LFxuICB7IHR5cGU6ICd0Y3AnLCBob3N0OiAnc2VlZDQuYXBoZWxpb24tbmVvLmNvbScsIHBvcnQ6IDEwMzMzIH0sXG5dO1xuXG5jb25zdCBERUZBVUxUX1RFU1RfU0VFRFM6IHJlYWRvbmx5IEVuZHBvaW50Q29uZmlnW10gPSBbXG4gIHsgdHlwZTogJ3RjcCcsIGhvc3Q6ICdzZWVkMS5uZW8ub3JnJywgcG9ydDogMjAzMzMgfSxcbiAgeyB0eXBlOiAndGNwJywgaG9zdDogJ3NlZWQyLm5lby5vcmcnLCBwb3J0OiAyMDMzMyB9LFxuICB7IHR5cGU6ICd0Y3AnLCBob3N0OiAnc2VlZDMubmVvLm9yZycsIHBvcnQ6IDIwMzMzIH0sXG4gIHsgdHlwZTogJ3RjcCcsIGhvc3Q6ICdzZWVkNC5uZW8ub3JnJywgcG9ydDogMjAzMzMgfSxcbiAgeyB0eXBlOiAndGNwJywgaG9zdDogJ3NlZWQ1Lm5lby5vcmcnLCBwb3J0OiAyMDMzMyB9LFxuXTtcblxuZXhwb3J0IGNsYXNzIE5ldHdvcmtSZXNvdXJjZUFkYXB0ZXIge1xuICBwdWJsaWMgc3RhdGljIGFzeW5jIGluaXQob3B0aW9uczogTmV0d29ya1Jlc291cmNlQWRhcHRlckluaXRPcHRpb25zKTogUHJvbWlzZTxOZXR3b3JrUmVzb3VyY2VBZGFwdGVyPiB7XG4gICAgY29uc3Qgc3RhdGljT3B0aW9ucyA9IHRoaXMuZ2V0U3RhdGljT3B0aW9ucyhvcHRpb25zKTtcbiAgICBjb25zdCBmaWxlcyA9IGF3YWl0IGZzLnJlYWRkaXIoc3RhdGljT3B0aW9ucy5ub2Rlc09wdGlvbnNQYXRoKTtcbiAgICBjb25zdCBub2RlT3B0aW9uc3MgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgIGZpbGVzLm1hcChhc3luYyAoZmlsZSkgPT5cbiAgICAgICAgdGhpcy5yZWFkTm9kZU9wdGlvbnMoc3RhdGljT3B0aW9ucywgcGF0aC5yZXNvbHZlKHN0YXRpY09wdGlvbnMubm9kZXNPcHRpb25zUGF0aCwgZmlsZSkpLFxuICAgICAgKSxcbiAgICApO1xuXG4gICAgY29uc3Qgbm9kZXMgPSBub2RlT3B0aW9uc3MubWFwKChub2RlT3B0aW9ucykgPT4gdGhpcy5jcmVhdGVOb2RlQWRhcHRlcihzdGF0aWNPcHRpb25zLCBub2RlT3B0aW9ucykpO1xuXG4gICAgcmV0dXJuIG5ldyB0aGlzKHtcbiAgICAgIG5hbWU6IHN0YXRpY09wdGlvbnMubmFtZSxcbiAgICAgIGJpbmFyeTogc3RhdGljT3B0aW9ucy5iaW5hcnksXG4gICAgICBkYXRhUGF0aDogc3RhdGljT3B0aW9ucy5kYXRhUGF0aCxcbiAgICAgIHBvcnRBbGxvY2F0b3I6IHN0YXRpY09wdGlvbnMucG9ydEFsbG9jYXRvcixcbiAgICAgIHJlc291cmNlVHlwZTogc3RhdGljT3B0aW9ucy5yZXNvdXJjZVR5cGUsXG4gICAgICBub2Rlc1BhdGg6IHN0YXRpY09wdGlvbnMubm9kZXNQYXRoLFxuICAgICAgbm9kZXNPcHRpb25zUGF0aDogc3RhdGljT3B0aW9ucy5ub2Rlc09wdGlvbnNQYXRoLFxuICAgICAgdHlwZTogbm9kZU9wdGlvbnNzWzBdLnR5cGUsXG4gICAgICBub2RlcyxcbiAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBzdGF0aWMgY3JlYXRlKGFkYXB0ZXJPcHRpb25zOiBOZXR3b3JrUmVzb3VyY2VBZGFwdGVySW5pdE9wdGlvbnMsIG9wdGlvbnM6IE5ldHdvcmtSZXNvdXJjZU9wdGlvbnMpOiBUYXNrTGlzdCB7XG4gICAgY29uc3Qgc3RhdGljT3B0aW9ucyA9IHRoaXMuZ2V0U3RhdGljT3B0aW9ucyhhZGFwdGVyT3B0aW9ucyk7XG4gICAgbGV0IHR5cGU6IE5vZGVPcHRpb25zWyd0eXBlJ107XG4gICAgbGV0IG5vZGVTZXR0aW5nczogUmVhZG9ubHlBcnJheTxyZWFkb25seSBbc3RyaW5nLCBOb2RlU2V0dGluZ3NdPjtcbiAgICBpZiAoc3RhdGljT3B0aW9ucy5uYW1lID09PSBjb25zdGFudHMuTkVUV09SS19OQU1FLk1BSU4pIHtcbiAgICAgIHR5cGUgPSBOZXR3b3JrVHlwZS5NYWluO1xuICAgICAgbm9kZVNldHRpbmdzID0gW1tzdGF0aWNPcHRpb25zLm5hbWUsIHRoaXMuZ2V0TWFpblNldHRpbmdzKHN0YXRpY09wdGlvbnMpXSBhcyBjb25zdF07XG4gICAgfSBlbHNlIGlmIChzdGF0aWNPcHRpb25zLm5hbWUgPT09IGNvbnN0YW50cy5ORVRXT1JLX05BTUUuVEVTVCkge1xuICAgICAgdHlwZSA9IE5ldHdvcmtUeXBlLlRlc3Q7XG4gICAgICBub2RlU2V0dGluZ3MgPSBbW3N0YXRpY09wdGlvbnMubmFtZSwgdGhpcy5nZXRUZXN0U2V0dGluZ3Moc3RhdGljT3B0aW9ucyldIGFzIGNvbnN0XTtcbiAgICB9IGVsc2Uge1xuICAgICAgdHlwZSA9IE5ldHdvcmtUeXBlLlByaXZhdGU7XG4gICAgICBub2RlU2V0dGluZ3MgPSB0aGlzLmdldFByaXZhdGVOZXRTZXR0aW5ncyhzdGF0aWNPcHRpb25zKTtcbiAgICB9XG5cbiAgICBjb25zdCBub2RlT3B0aW9uc3MgPSBub2RlU2V0dGluZ3MubWFwPE5vZGVPcHRpb25zPigoW25hbWUsIHNldHRpbmdzXSkgPT4gKHtcbiAgICAgIHR5cGUsXG4gICAgICBuYW1lLFxuICAgICAgZGF0YVBhdGg6IHBhdGgucmVzb2x2ZShzdGF0aWNPcHRpb25zLm5vZGVzUGF0aCwgbmFtZSksXG4gICAgICBzZXR0aW5ncyxcbiAgICAgIG9wdGlvbnMsXG4gICAgfSkpO1xuXG4gICAgY29uc3Qgbm9kZU9wdGlvbnNBbmROb2RlcyA9IG5vZGVPcHRpb25zcy5tYXA8W05vZGVPcHRpb25zLCBOb2RlQWRhcHRlcl0+KChub2RlT3B0aW9ucykgPT4gW1xuICAgICAgbm9kZU9wdGlvbnMsXG4gICAgICB0aGlzLmNyZWF0ZU5vZGVBZGFwdGVyKHN0YXRpY09wdGlvbnMsIG5vZGVPcHRpb25zKSxcbiAgICBdKTtcblxuICAgIHJldHVybiBuZXcgVGFza0xpc3Qoe1xuICAgICAgaW5pdGlhbENvbnRleHQ6IHtcbiAgICAgICAgcmVzb3VyY2VBZGFwdGVyOiBuZXcgdGhpcyh7XG4gICAgICAgICAgbmFtZTogc3RhdGljT3B0aW9ucy5uYW1lLFxuICAgICAgICAgIGJpbmFyeTogc3RhdGljT3B0aW9ucy5iaW5hcnksXG4gICAgICAgICAgZGF0YVBhdGg6IHN0YXRpY09wdGlvbnMuZGF0YVBhdGgsXG4gICAgICAgICAgcG9ydEFsbG9jYXRvcjogc3RhdGljT3B0aW9ucy5wb3J0QWxsb2NhdG9yLFxuICAgICAgICAgIHJlc291cmNlVHlwZTogc3RhdGljT3B0aW9ucy5yZXNvdXJjZVR5cGUsXG4gICAgICAgICAgbm9kZXNQYXRoOiBzdGF0aWNPcHRpb25zLm5vZGVzUGF0aCxcbiAgICAgICAgICBub2Rlc09wdGlvbnNQYXRoOiBzdGF0aWNPcHRpb25zLm5vZGVzT3B0aW9uc1BhdGgsXG4gICAgICAgICAgdHlwZTogbm9kZVNldHRpbmdzWzBdWzFdLnR5cGUsXG4gICAgICAgICAgbm9kZXM6IG5vZGVPcHRpb25zQW5kTm9kZXMubWFwKCh2YWx1ZSkgPT4gdmFsdWVbMV0pLFxuICAgICAgICB9KSxcblxuICAgICAgICBkZXBlbmRlbmNpZXM6IFtdLFxuICAgICAgfSxcbiAgICAgIHRhc2tzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICB0aXRsZTogJ0NyZWF0ZSBkYXRhIGRpcmVjdG9yaWVzJyxcbiAgICAgICAgICB0YXNrOiBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChbZnMuZW5zdXJlRGlyKHN0YXRpY09wdGlvbnMubm9kZXNQYXRoKSwgZnMuZW5zdXJlRGlyKHN0YXRpY09wdGlvbnMubm9kZXNPcHRpb25zUGF0aCldKTtcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgdGl0bGU6ICdDcmVhdGUgbm9kZXMnLFxuICAgICAgICAgIHRhc2s6ICgpID0+XG4gICAgICAgICAgICBuZXcgVGFza0xpc3Qoe1xuICAgICAgICAgICAgICB0YXNrczogbm9kZU9wdGlvbnNBbmROb2Rlcy5tYXAoKFtub2RlT3B0aW9ucywgbm9kZV0pID0+ICh7XG4gICAgICAgICAgICAgICAgdGl0bGU6IGBDcmVhdGUgbm9kZSAke25vZGVPcHRpb25zLm5hbWV9YCxcbiAgICAgICAgICAgICAgICB0YXNrOiBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLndyaXRlTm9kZU9wdGlvbnMoc3RhdGljT3B0aW9ucywgbm9kZU9wdGlvbnMpO1xuICAgICAgICAgICAgICAgICAgYXdhaXQgbm9kZS5jcmVhdGUoKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICB9KSksXG5cbiAgICAgICAgICAgICAgY29uY3VycmVudDogdHJ1ZSxcbiAgICAgICAgICAgIH0pLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGdldFN0YXRpY09wdGlvbnMob3B0aW9uczogTmV0d29ya1Jlc291cmNlQWRhcHRlckluaXRPcHRpb25zKTogTmV0d29ya1Jlc291cmNlQWRhcHRlclN0YXRpY09wdGlvbnMge1xuICAgIHJldHVybiB7XG4gICAgICBuYW1lOiBvcHRpb25zLm5hbWUsXG4gICAgICBiaW5hcnk6IG9wdGlvbnMuYmluYXJ5LFxuICAgICAgZGF0YVBhdGg6IG9wdGlvbnMuZGF0YVBhdGgsXG4gICAgICBwb3J0QWxsb2NhdG9yOiBvcHRpb25zLnBvcnRBbGxvY2F0b3IsXG4gICAgICByZXNvdXJjZVR5cGU6IG9wdGlvbnMucmVzb3VyY2VUeXBlLFxuICAgICAgbm9kZXNQYXRoOiBwYXRoLnJlc29sdmUob3B0aW9ucy5kYXRhUGF0aCwgTk9ERVNfUEFUSCksXG4gICAgICBub2Rlc09wdGlvbnNQYXRoOiBwYXRoLnJlc29sdmUob3B0aW9ucy5kYXRhUGF0aCwgTk9ERVNfT1BUSU9OU19QQVRIKSxcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgZ2V0UHJpdmF0ZU5ldFNldHRpbmdzKFxuICAgIG9wdGlvbnM6IE5ldHdvcmtSZXNvdXJjZUFkYXB0ZXJTdGF0aWNPcHRpb25zLFxuICApOiBSZWFkb25seUFycmF5PHJlYWRvbmx5IFtzdHJpbmcsIE5vZGVTZXR0aW5nc10+IHtcbiAgICBjb25zdCBwcmltYXJ5UHJpdmF0ZUtleSA9IGNyeXB0by53aWZUb1ByaXZhdGVLZXkoY29uc3RhbnRzLlBSSVZBVEVfTkVUX1BSSVZBVEVfS0VZLCBjb21tb24uTkVPX1BSSVZBVEVfS0VZX1ZFUlNJT04pO1xuICAgIGNvbnN0IHByaW1hcnlQdWJsaWNLZXkgPSBjb21tb24uc3RyaW5nVG9FQ1BvaW50KGNvbnN0YW50cy5QUklWQVRFX05FVF9QVUJMSUNfS0VZKTtcbiAgICBjcnlwdG8uYWRkUHVibGljS2V5KHByaW1hcnlQcml2YXRlS2V5LCBwcmltYXJ5UHVibGljS2V5KTtcblxuICAgIGNvbnN0IHByaW1hcnlBZGRyZXNzID0gY29tbW9uLnVJbnQxNjBUb1N0cmluZyhjcnlwdG8ucHJpdmF0ZUtleVRvU2NyaXB0SGFzaChwcmltYXJ5UHJpdmF0ZUtleSkpO1xuXG4gICAgY29uc3QgY29uZmlndXJhdGlvbk5hbWUgPSBgJHtvcHRpb25zLm5hbWV9LTBgO1xuICAgIGNvbnN0IGNvbmZpZ3VyYXRpb24gPSBbXG4gICAgICB7XG4gICAgICAgIG5hbWU6IGNvbmZpZ3VyYXRpb25OYW1lLFxuICAgICAgICBycGNQb3J0OiB0aGlzLmdldFJQQ1BvcnQob3B0aW9ucywgY29uZmlndXJhdGlvbk5hbWUpLFxuICAgICAgICBsaXN0ZW5UQ1BQb3J0OiB0aGlzLmdldExpc3RlblRDUFBvcnQob3B0aW9ucywgY29uZmlndXJhdGlvbk5hbWUpLFxuICAgICAgICB0ZWxlbWV0cnlQb3J0OiB0aGlzLmdldFRlbGVtZXRyeVBvcnQob3B0aW9ucywgY29uZmlndXJhdGlvbk5hbWUpLFxuICAgICAgICBwcml2YXRlS2V5OiBwcmltYXJ5UHJpdmF0ZUtleSxcbiAgICAgICAgcHVibGljS2V5OiBwcmltYXJ5UHVibGljS2V5LFxuICAgICAgfSxcbiAgICBdO1xuICAgIGNvbnN0IHNlY29uZHNQZXJCbG9jayA9IDE1O1xuICAgIGNvbnN0IHN0YW5kYnlWYWxpZGF0b3JzID0gY29uZmlndXJhdGlvbi5tYXAoKHsgcHVibGljS2V5IH0pID0+IGNvbW1vbi5lY1BvaW50VG9TdHJpbmcocHVibGljS2V5KSk7XG5cbiAgICByZXR1cm4gY29uZmlndXJhdGlvbi5tYXA8cmVhZG9ubHkgW3N0cmluZywgTm9kZVNldHRpbmdzXT4oKGNvbmZpZykgPT4ge1xuICAgICAgY29uc3QgeyBuYW1lLCBycGNQb3J0LCBsaXN0ZW5UQ1BQb3J0LCB0ZWxlbWV0cnlQb3J0LCBwcml2YXRlS2V5IH0gPSBjb25maWc7XG4gICAgICBjb25zdCBvdGhlckNvbmZpZ3VyYXRpb24gPSBjb25maWd1cmF0aW9uLmZpbHRlcigoeyBuYW1lOiBvdGhlck5hbWUgfSkgPT4gbmFtZSAhPT0gb3RoZXJOYW1lKTtcblxuICAgICAgY29uc3Qgc2V0dGluZ3MgPSB7XG4gICAgICAgIHR5cGU6IE5ldHdvcmtUeXBlLlByaXZhdGUsXG4gICAgICAgIGlzVGVzdE5ldDogZmFsc2UsXG4gICAgICAgIHJwY1BvcnQsXG4gICAgICAgIGxpc3RlblRDUFBvcnQsXG4gICAgICAgIHRlbGVtZXRyeVBvcnQsXG4gICAgICAgIHByaXZhdGVOZXQ6IHRydWUsXG4gICAgICAgIHNlY29uZHNQZXJCbG9jayxcbiAgICAgICAgc3RhbmRieVZhbGlkYXRvcnMsXG4gICAgICAgIGFkZHJlc3M6IHByaW1hcnlBZGRyZXNzLFxuICAgICAgICBjb25zZW5zdXM6IHtcbiAgICAgICAgICBlbmFibGVkOiB0cnVlLFxuICAgICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICAgIHByaXZhdGVLZXk6IGNvbW1vbi5wcml2YXRlS2V5VG9TdHJpbmcocHJpdmF0ZUtleSksXG4gICAgICAgICAgICBwcml2YXRlTmV0OiB0cnVlLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG5cbiAgICAgICAgc2VlZHM6IG90aGVyQ29uZmlndXJhdGlvbi5tYXAoKG90aGVyQ29uZmlnKSA9PlxuICAgICAgICAgIGNyZWF0ZUVuZHBvaW50KHtcbiAgICAgICAgICAgIHR5cGU6ICd0Y3AnLFxuICAgICAgICAgICAgaG9zdDogJ2xvY2FsaG9zdCcsXG4gICAgICAgICAgICBwb3J0OiBvdGhlckNvbmZpZy5saXN0ZW5UQ1BQb3J0LFxuICAgICAgICAgIH0pLFxuICAgICAgICApLFxuXG4gICAgICAgIHJwY0VuZHBvaW50czogb3RoZXJDb25maWd1cmF0aW9uLm1hcCgob3RoZXJDb25maWcpID0+IGBodHRwOi8vbG9jYWxob3N0OiR7b3RoZXJDb25maWcucnBjUG9ydH0vcnBjYCksXG4gICAgICB9O1xuXG4gICAgICByZXR1cm4gW25hbWUsIHNldHRpbmdzXSBhcyBjb25zdDtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGdldE1haW5TZXR0aW5ncyhvcHRpb25zOiBOZXR3b3JrUmVzb3VyY2VBZGFwdGVyU3RhdGljT3B0aW9ucyk6IE5vZGVTZXR0aW5ncyB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHR5cGU6IE5ldHdvcmtU