bitcoind-regtest
Version:
Run bitcoind in regtest mode for your own blockchain
893 lines • 58.4 kB
JavaScript
'use strict';
var _get = require('babel-runtime/helpers/get')['default'];
var _inherits = require('babel-runtime/helpers/inherits')['default'];
var _createDecoratedClass = require('babel-runtime/helpers/create-decorated-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var _slicedToArray = require('babel-runtime/helpers/sliced-to-array')['default'];
var _regeneratorRuntime = require('babel-runtime/regenerator')['default'];
var _Promise = require('babel-runtime/core-js/promise')['default'];
var _getIterator = require('babel-runtime/core-js/get-iterator')['default'];
var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default'];
Object.defineProperty(exports, '__esModule', {
value: true
});
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _events = require('events');
var _coreDecorators = require('core-decorators');
var _readyMixin = require('ready-mixin');
var _readyMixin2 = _interopRequireDefault(_readyMixin);
var _tmp = require('tmp');
var _tmp2 = _interopRequireDefault(_tmp);
var _rimraf2 = require('rimraf');
var _rimraf3 = _interopRequireDefault(_rimraf2);
var _crypto = require('crypto');
var _crypto2 = _interopRequireDefault(_crypto);
var _child_process = require('child_process');
var _bitcoreLib = require('bitcore-lib');
var _bitcoreLib2 = _interopRequireDefault(_bitcoreLib);
var _bitcoreP2p = require('bitcore-p2p');
var _bitcoreP2p2 = _interopRequireDefault(_bitcoreP2p);
var _bitcoindRpcClient = require('bitcoind-rpc-client');
var _bitcoindRpcClient2 = _interopRequireDefault(_bitcoindRpcClient);
var _makeConcurrent = require('make-concurrent');
var _makeConcurrent2 = _interopRequireDefault(_makeConcurrent);
var _promiseUsefulUtils = require('promise-useful-utils');
var _promiseUsefulUtils2 = _interopRequireDefault(_promiseUsefulUtils);
var _wallet = require('./wallet');
var _wallet2 = _interopRequireDefault(_wallet);
var tmpfs = _promiseUsefulUtils2['default'].promisifyAll(_tmp2['default']);
var rimraf = _promiseUsefulUtils2['default'].promisify(_rimraf3['default']);
var encode = function encode(s) {
return Array.prototype.reverse.call(new Buffer(s)).toString('hex');
};
/**
* @event Bitcoind#error
* @param {Error} err
*/
/**
* @event Bitcoind#data
* @param {string} data
*/
/**
* @event Bitcoind#exit
* @param {number} code
* @param {string} signal
*/
/**
* @event Bitcoind#block
* @param {string} hash
*/
/**
* @event Bitcoind#tx
* @param {string} txid
*/
/**
* @class Bitcoind
*/
var Bitcoind = (function (_EventEmitter) {
_inherits(Bitcoind, _EventEmitter);
/**
* @constructor
* @param {Object} [opts]
* @param {Object} [opts.wallet] Wallet options
* @param {function} [opts.wallet.preloadsPoolSize=_.constant(10)] Preload pool size
* @param {function} [opts.wallet.keysPoolSize=() => _.random(80, 100)]
* @param {function} [opts.wallet.newKeyTimeout=() => _.random(60, 90) * 1000]
* @param {function} [opts.wallet.inputsCount=() => _.random(2, 3)]
* @param {function} [opts.wallet.outputsCount=() => _.random(3, 5)]
* @param {Object} [opts.generate] Generate options
* @param {Object} [opts.generate.txs]
* @param {function} [opts.generate.txs.background=_.constant(true)]
* @param {function} [opts.generate.txs.timeout=() => _.random(6, 9, true) * 1000]
* @param {function} [opts.generate.txs.minInBlock=_.constant(5)]
* @param {Object} [opts.generate.blocks]
* @param {function} [opts.generate.blocks.background=_.constant(true)]
* @param {function} [opts.generate.blocks.timeout=() => _.random(60, 90, true) * 1000]
* @param {Object} [opts.bitcoind]
* @param {function} [opts.bitcoind.path=() => '../bitcoind/bitcoind-rev-3224936']
* @param {function} [opts.bitcoind.datadir=async () => _.first(tmpfs.dir({prefix: 'bitcoind-regtest-'}))]
* @param {function} [opts.bitcoind.port=() => _.random(20000, 30000)]
* @param {function} [opts.bitcoind.rpcport=() => _.random(20000, 30000)]
* @param {function} [opts.bitcoind.rpcuser=() => crypto.randomBytes(10).toString('hex')]
* @param {function} [opts.bitcoind.rpcpassword=() => crypto.randomBytes(10).toString('hex')]
*/
function Bitcoind(opts) {
var _this2 = this;
_classCallCheck(this, _Bitcoind);
_get(Object.getPrototypeOf(_Bitcoind.prototype), 'constructor', this).call(this);
_promiseUsefulUtils2['default']['try'](function callee$2$0() {
var onErrorExit;
return _regeneratorRuntime.async(function callee$2$0$(context$3$0) {
var _this = this;
while (1) switch (context$3$0.prev = context$3$0.next) {
case 0:
this._opts = _lodash2['default'].defaultsDeep(_lodash2['default'].cloneDeep(opts) || {}, {
wallet: {
preloadsPoolSize: _lodash2['default'].constant(10),
keysPoolSize: function keysPoolSize() {
return _lodash2['default'].random(80, 100);
},
newKeyTimeout: function newKeyTimeout() {
return _lodash2['default'].random(60, 90) * 1000;
},
inputsCount: function inputsCount() {
return _lodash2['default'].random(2, 2);
},
outputsCount: function outputsCount() {
return _lodash2['default'].random(3, 5);
}
},
generate: {
txs: {
background: _lodash2['default'].constant(true),
timeout: function timeout() {
return _lodash2['default'].random(6, 9, true) * 1000;
},
minInBlock: _lodash2['default'].constant(5)
},
blocks: {
background: _lodash2['default'].constant(true),
timeout: function timeout() {
return _lodash2['default'].random(60, 90, true) * 1000;
}
}
},
bitcoind: {
path: function path() {
return '../bitcoind/bitcoind-rev-3224936';
},
datadir: function datadir() {
return _regeneratorRuntime.async(function datadir$(context$4$0) {
while (1) switch (context$4$0.prev = context$4$0.next) {
case 0:
context$4$0.t0 = _lodash2['default'];
context$4$0.next = 3;
return _regeneratorRuntime.awrap(tmpfs.dirAsync({
prefix: 'bitcoind-regtest-',
keep: false,
unsafeCleanup: true
}));
case 3:
context$4$0.t1 = context$4$0.sent;
return context$4$0.abrupt('return', context$4$0.t0.first.call(context$4$0.t0, context$4$0.t1));
case 5:
case 'end':
return context$4$0.stop();
}
}, null, _this);
},
port: function port() {
return _lodash2['default'].random(20000, 30000);
},
rpcport: function rpcport() {
return _lodash2['default'].random(20000, 30000);
},
rpcuser: function rpcuser() {
return _crypto2['default'].randomBytes(10).toString('hex');
},
rpcpassword: function rpcpassword() {
return _crypto2['default'].randomBytes(10).toString('hex');
}
}
});
// process options
context$3$0.t0 = this._opts.bitcoind.path();
context$3$0.next = 4;
return _regeneratorRuntime.awrap(this._opts.bitcoind.datadir());
case 4:
context$3$0.t1 = context$3$0.sent;
context$3$0.t2 = this._opts.bitcoind.port();
context$3$0.t3 = this._opts.bitcoind.rpcport();
context$3$0.t4 = this._opts.bitcoind.rpcuser();
context$3$0.t5 = this._opts.bitcoind.rpcpassword();
this._processOpts = {
path: context$3$0.t0,
datadir: context$3$0.t1,
port: context$3$0.t2,
rpcport: context$3$0.t3,
rpcuser: context$3$0.t4,
rpcpassword: context$3$0.t5
};
// init bitcoind process
this._process = (0, _child_process.spawn)(this._opts.bitcoind.path(), ['-regtest', '-txindex=1', '-server', '-datadir=' + this._processOpts.datadir, '-port=' + this._processOpts.port, '-rpcport=' + this._processOpts.rpcport, '-rpcuser=' + this._processOpts.rpcuser, '-rpcpassword=' + this._processOpts.rpcpassword, '-printtoconsole'], {
cwd: __dirname,
stdio: ['ignore', 'pipe', 'pipe']
});
this._process.on('error', function (err) {
_this.emit('error', err);
});
this._process.on('exit', function (code, signal) {
_this.emit('exit', code, signal);
});
this._process.stdout.on('error', function (err) {
_this.emit('error', err);
});
this._process.stdout.on('data', function (data) {
_this.emit('data', data);
});
this._process.stderr.on('error', function (err) {
_this.emit('error', err);
});
this._process.stderr.on('data', function (data) {
_this.emit('data', data);
});
// await running bitcoind
onErrorExit = undefined;
context$3$0.prev = 18;
context$3$0.next = 21;
return _regeneratorRuntime.awrap(new _Promise(function (resolve, reject) {
setTimeout(resolve, 1000);
onErrorExit = function (code, signal) {
if (code instanceof Error && signal === undefined) {
reject(code);
}
reject(new Error('Exit with code = ' + code + ' on signal = ' + signal));
};
_this._process.on('error', onErrorExit);
_this._process.on('exit', onErrorExit);
}));
case 21:
context$3$0.prev = 21;
this._process.removeListener('error', onErrorExit);
this._process.removeListener('exit', onErrorExit);
return context$3$0.finish(21);
case 25:
// initialize peer
this._peer = new _bitcoreP2p2['default'].Peer({
host: '127.0.0.1',
port: this._processOpts.port,
network: _bitcoreLib2['default'].Networks.get('regtest')
});
this._peer.on('inv', function (message) {
var names = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = _getIterator(message.inventory), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var inv = _step.value;
// store inv type name
names.push(_bitcoreP2p2['default'].Inventory.TYPE_NAME[inv.type]);
// store inv if tx type
if (inv.type === _bitcoreP2p2['default'].Inventory.TYPE.TX) {
_this.emit('tx', encode(inv.hash));
}
// emit block if block type
if (inv.type === _bitcoreP2p2['default'].Inventory.TYPE.BLOCK) {
_this.emit('block', encode(inv.hash));
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator['return']) {
_iterator['return']();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
});
context$3$0.next = 29;
return _regeneratorRuntime.awrap(new _Promise(function (resolve) {
_this._peer.once('ready', resolve);
_this._peer.connect();
}));
case 29:
// wait wallet initialization
this._wallet = new _wallet2['default'](this);
this._wallet.on('error', this.emit.bind(this, 'error'));
context$3$0.next = 33;
return _regeneratorRuntime.awrap(this._wallet.ready);
case 33:
case 'end':
return context$3$0.stop();
}
}, null, _this2, [[18,, 21, 25]]);
}).then(function () {
_this2._ready(null);
}, function (err) {
_this2._ready(err);
});
// loop for creating new transactions in background
this.ready.then(function callee$2$0() {
return _regeneratorRuntime.async(function callee$2$0$(context$3$0) {
while (1) switch (context$3$0.prev = context$3$0.next) {
case 0:
if (!true) {
context$3$0.next = 14;
break;
}
context$3$0.prev = 1;
context$3$0.next = 4;
return _regeneratorRuntime.awrap(_promiseUsefulUtils2['default'].delay(this._opts.generate.txs.timeout()));
case 4:
if (!this._opts.generate.txs.background()) {
context$3$0.next = 7;
break;
}
context$3$0.next = 7;
return _regeneratorRuntime.awrap(this.generateTxs(1));
case 7:
context$3$0.next = 12;
break;
case 9:
context$3$0.prev = 9;
context$3$0.t0 = context$3$0['catch'](1);
this.emit(context$3$0.t0);
case 12:
context$3$0.next = 0;
break;
case 14:
case 'end':
return context$3$0.stop();
}
}, null, _this2, [[1, 9]]);
});
// loop for creating new blocks in background
this.ready.then(function callee$2$0() {
return _regeneratorRuntime.async(function callee$2$0$(context$3$0) {
while (1) switch (context$3$0.prev = context$3$0.next) {
case 0:
if (!true) {
context$3$0.next = 14;
break;
}
context$3$0.prev = 1;
context$3$0.next = 4;
return _regeneratorRuntime.awrap(_promiseUsefulUtils2['default'].delay(this._opts.generate.blocks.timeout()));
case 4:
if (!this._opts.generate.blocks.background()) {
context$3$0.next = 7;
break;
}
context$3$0.next = 7;
return _regeneratorRuntime.awrap(this.generateBlocks(1));
case 7:
context$3$0.next = 12;
break;
case 9:
context$3$0.prev = 9;
context$3$0.t0 = context$3$0['catch'](1);
this.emit(context$3$0.t0);
case 12:
context$3$0.next = 0;
break;
case 14:
case 'end':
return context$3$0.stop();
}
}, null, _this2, [[1, 9]]);
});
}
/**
* Return value for given option `name`
* @param {string} name
* @return {*}
*/
_createDecoratedClass(Bitcoind, [{
key: 'getOption',
value: function getOption(name) {
return _lodash2['default'].get(this._opts, name);
}
/**
* @param {string} name
* @param {*}
*/
}, {
key: 'setOption',
value: function setOption(name, value) {
_lodash2['default'].set(this._opts, name, value);
}
/**
* Return instance of `RpcClient`
* @return {RpcClient}
*/
}, {
key: 'getRpcClient',
value: function getRpcClient() {
return new _bitcoindRpcClient2['default']({
host: '127.0.0.1',
port: this._processOpts.rpcport,
user: this._processOpts.rpcuser,
pass: this._processOpts.rpcpassword
});
}
/**
* Same as func getRpcClient
*/
}, {
key: 'withLock',
decorators: [(0, _makeConcurrent2['default'])({ concurrency: 1 })],
value: function withLock(fn) {
return fn();
}
/**
* Generate `count` blocks
* @param {number} count
* @return {Promise.<Array.<string>>}
*/
}, {
key: 'generateBlocks',
decorators: [(0, _makeConcurrent2['default'])({ concurrency: 1 })],
value: function generateBlocks(count) {
var hashes, requiredMemPoolSize, memPoolSize;
return _regeneratorRuntime.async(function generateBlocks$(context$2$0) {
var _this3 = this;
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
context$2$0.next = 2;
return _regeneratorRuntime.awrap(this.ready);
case 2:
hashes = [];
case 3:
if (!(count > 0)) {
context$2$0.next = 15;
break;
}
requiredMemPoolSize = this._opts.generate.txs.minInBlock();
context$2$0.next = 7;
return _regeneratorRuntime.awrap(this.rpc.getMemPoolInfo());
case 7:
memPoolSize = context$2$0.sent.result.size;
context$2$0.next = 10;
return _regeneratorRuntime.awrap(this.generateTxs(requiredMemPoolSize - memPoolSize));
case 10:
context$2$0.next = 12;
return _regeneratorRuntime.awrap(this.withLock(function callee$2$0() {
var ret;
return _regeneratorRuntime.async(function callee$2$0$(context$3$0) {
while (1) switch (context$3$0.prev = context$3$0.next) {
case 0:
context$3$0.next = 2;
return _regeneratorRuntime.awrap(this.rpc.generate(1));
case 2:
ret = context$3$0.sent;
hashes.push(ret.result[0]);
case 4:
case 'end':
return context$3$0.stop();
}
}, null, _this3);
}));
case 12:
count -= 1;
context$2$0.next = 3;
break;
case 15:
return context$2$0.abrupt('return', hashes);
case 16:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
/**
* Generate `count` transactions
* @param {number} count
* @return {Promise.<Array.<?string>>}
*/
}, {
key: 'generateTxs',
value: function generateTxs(count) {
return _regeneratorRuntime.async(function generateTxs$(context$2$0) {
var _this4 = this;
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
context$2$0.next = 2;
return _regeneratorRuntime.awrap(this.ready);
case 2:
context$2$0.next = 4;
return _regeneratorRuntime.awrap(_Promise.all(_lodash2['default'].times(count).map(function () {
return _this4._wallet.generateTx();
})));
case 4:
return context$2$0.abrupt('return', context$2$0.sent);
case 5:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
/**
* @typedef {Object} Bitcoind~PreloadObject
* @property {string} txId
* @property {number} outIndex
* @property {number} value
* @property {bitcore.Script} script
* @property {bitcore.PrivateKey} privKey
*/
/**
* Return Object with utxo data and with private key (balance is 10btc)
* @return {Promise.<Bitcoind~PreloadObject>}
*/
}, {
key: 'getPreload',
value: function getPreload() {
return _regeneratorRuntime.async(function getPreload$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
context$2$0.next = 2;
return _regeneratorRuntime.awrap(this.ready);
case 2:
context$2$0.next = 4;
return _regeneratorRuntime.awrap(this._wallet.getPreload());
case 4:
return context$2$0.abrupt('return', context$2$0.sent);
case 5:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
/**
* Return instance of `Bitcoind` that have same blockchain
* @param {Object} [opts]
* @param {boolean} [opts.connected=true]
* @return {Promise.<Bitcoind>}
*/
}, {
key: 'fork',
value: function fork(opts) {
var bopts, other, _ref, _ref2, mhash, ohash;
return _regeneratorRuntime.async(function fork$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
bopts = _lodash2['default'].cloneDeep(this._opts);
bopts.generate.txs.background = _lodash2['default'].constant(false);
bopts.generate.blocks.background = _lodash2['default'].constant(false);
other = new Bitcoind(bopts);
context$2$0.next = 6;
return _regeneratorRuntime.awrap(other.ready);
case 6:
context$2$0.next = 8;
return _regeneratorRuntime.awrap(this.connect(other));
case 8:
context$2$0.next = 10;
return _regeneratorRuntime.awrap(this.rpc.getBlockCount());
case 10:
context$2$0.t0 = context$2$0.sent.result;
if (!(context$2$0.t0 > 0)) {
context$2$0.next = 27;
break;
}
context$2$0.next = 14;
return _regeneratorRuntime.awrap(other.generateBlocks(1));
case 14:
if (!true) {
context$2$0.next = 27;
break;
}
context$2$0.next = 17;
return _regeneratorRuntime.awrap(_Promise.all([this.rpc.getBestBlockHash(), other.rpc.getBestBlockHash()]));
case 17:
_ref = context$2$0.sent;
_ref2 = _slicedToArray(_ref, 2);
mhash = _ref2[0];
ohash = _ref2[1];
if (!(mhash.result === ohash.result)) {
context$2$0.next = 23;
break;
}
return context$2$0.abrupt('break', 27);
case 23:
context$2$0.next = 25;
return _regeneratorRuntime.awrap(_promiseUsefulUtils2['default'].delay(500));
case 25:
context$2$0.next = 14;
break;
case 27:
other.setOption('generate.txs.background', this._opts.generate.txs.background);
other.setOption('generate.blocks.background', this._opts.generate.blocks.background);
if (Object(opts).connected) {
context$2$0.next = 32;
break;
}
context$2$0.next = 32;
return _regeneratorRuntime.awrap(this.disconnect(other));
case 32:
return context$2$0.abrupt('return', other);
case 33:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
/**
* @param {Bitcoind}
* @return {Promise}
*/
}, {
key: 'connect',
value: function connect(other) {
var node;
return _regeneratorRuntime.async(function connect$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
node = '127.0.0.1:' + other._processOpts.port;
context$2$0.next = 3;
return _regeneratorRuntime.awrap(this.rpc.addNode(node, 'onetry'));
case 3:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
/**
* @param {Bitcoind}
* @return {Promise}
*/
}, {
key: 'disconnect',
value: function disconnect(other) {
var node;
return _regeneratorRuntime.async(function disconnect$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
node = '127.0.0.1:' + other._processOpts.port;
context$2$0.next = 3;
return _regeneratorRuntime.awrap(this.rpc.disconnectNode(node));
case 3:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
/**
* @return {Promise}
*/
}, {
key: 'terminate',
decorators: [(0, _makeConcurrent2['default'])({ concurrency: 1 })],
value: function terminate() {
var onError, onExit;
return _regeneratorRuntime.async(function terminate$(context$2$0) {
var _this5 = this;
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
onError = undefined;
onExit = undefined;
if (!(this._process === null)) {
context$2$0.next = 4;
break;
}
throw new Error('Child process already terminated');
case 4:
context$2$0.prev = 4;
context$2$0.next = 7;
return _regeneratorRuntime.awrap(new _Promise(function (resolve, reject) {
onError = reject;
onExit = function callee$3$0(code, signal) {
return _regeneratorRuntime.async(function callee$3$0$(context$4$0) {
while (1) switch (context$4$0.prev = context$4$0.next) {
case 0:
if (code === 0 && signal === null) {
context$4$0.next = 2;
break;
}
return context$4$0.abrupt('return', reject(new Error('Exit with code = ' + code + ' on signal = ' + signal)));
case 2:
context$4$0.prev = 2;
this._wallet.removeAllListeners();
this._peer.removeAllListeners();
this._process.removeAllListeners();
this._process = null;
context$4$0.next = 9;
return _regeneratorRuntime.awrap(rimraf(this._processOpts.datadir));
case 9:
resolve();
context$4$0.next = 15;
break;
case 12:
context$4$0.prev = 12;
context$4$0.t0 = context$4$0['catch'](2);
reject(context$4$0.t0);
case 15:
case 'end':
return context$4$0.stop();
}
}, null, _this5, [[2, 12]]);
};
_this5._process.on('error', onError);
_this5._process.on('exit', onExit);
_this5._process.kill('SIGTERM');
}));
case 7:
context$2$0.prev = 7;
if (this._process !== null) {
this._process.removeListener('error', onError);
this._process.removeListener('exit', onExit);
}
return context$2$0.finish(7);
case 10:
case 'end':
return context$2$0.stop();
}
}, null, this, [[4,, 7, 10]]);
}
}, {
key: 'rpc',
get: function get() {
return this.getRpcClient();
}
/**
* @param {function} fn
* @return {Promise}
*/
}]);
var _Bitcoind = Bitcoind;
Bitcoind = (0, _coreDecorators.mixin)(_readyMixin2['default'])(Bitcoind) || Bitcoind;
return Bitcoind;
})(_events.EventEmitter);
exports['default'] = Bitcoind;
module.exports = exports['default'];
//# sourceMappingURL=data:application/json;base64,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QUFHLENBQUM7Ozs7O0FBSTlELDBCQUFJLENBQUMsT0FBTyxDQUFDLGtCQUFrQixFQUFFLENBQUE7QUFDakMsMEJBQUksQ0FBQyxLQUFLLENBQUMsa0JBQWtCLEVBQUUsQ0FBQTtBQUMvQiwwQkFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsRUFBRSxDQUFBO0FBQ2xDLDBCQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQTs7dURBQ2QsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDOzs7QUFDdkMsNkJBQU8sRUFBRSxDQUFBOzs7Ozs7OztBQUVULDRCQUFNLGdCQUFLLENBQUE7Ozs7Ozs7ZUFFZCxDQUFBOztBQUVELHFCQUFLLFFBQVEsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFBO0FBQ2xDLHFCQUFLLFFBQVEsQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFBOztBQUVoQyxxQkFBSyxRQUFRLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFBO2FBQzlCLENBQUM7Ozs7O0FBRUYsZ0JBQUksSUFBSSxDQUFDLFFBQVEsS0FBSyxJQUFJLEVBQUU7QUFDMUIsa0JBQUksQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQTtBQUM5QyxrQkFBSSxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFBO2FBQzdDOzs7Ozs7OztLQUVKOzs7U0FsS08sZUFBRztBQUFFLGFBQU8sSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFBO0tBQUU7Ozs7Ozs7O2tCQXJOdEIsUUFBUTtBQUFSLFVBQVEsR0FENUIsbURBQWlCLENBQ0csUUFBUSxLQUFSLFFBQVE7U0FBUixRQUFROzs7cUJBQVIsUUFBUSIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBfIGZyb20gJ2xvZGFzaCdcbmltcG9ydCB7IEV2ZW50RW1pdHRlciB9IGZyb20gJ2V2ZW50cydcbmltcG9ydCB7IG1peGluIH0gZnJvbSAnY29yZS1kZWNvcmF0b3JzJ1xuaW1wb3J0IFJlYWR5TWl4aW4gZnJvbSAncmVhZHktbWl4aW4nXG5pbXBvcnQgX3RtcGZzIGZyb20gJ3RtcCdcbmltcG9ydCBfcmltcmFmIGZyb20gJ3JpbXJhZidcbmltcG9ydCBjcnlwdG8gZnJvbSAnY3J5cHRvJ1xuaW1wb3J0IHsgc3Bhd24gfSBmcm9tICdjaGlsZF9wcm9jZXNzJ1xuaW1wb3J0IGJpdGNvcmUgZnJvbSAnYml0Y29yZS1saWInXG5pbXBvcnQgcDJwIGZyb20gJ2JpdGNvcmUtcDJwJ1xuaW1wb3J0IFJwY0NsaWVudCBmcm9tICdiaXRjb2luZC1ycGMtY2xpZW50J1xuaW1wb3J0IG1ha2VDb25jdXJyZW50IGZyb20gJ21ha2UtY29uY3VycmVudCdcbmltcG9ydCBQVXRpbHMgZnJvbSAncHJvbWlzZS11c2VmdWwtdXRpbHMnXG5cbmltcG9ydCBXYWxsZXQgZnJvbSAnLi93YWxsZXQnXG5cbmxldCB0bXBmcyA9IFBVdGlscy5wcm9taXNpZnlBbGwoX3RtcGZzKVxubGV0IHJpbXJhZiA9IFBVdGlscy5wcm9taXNpZnkoX3JpbXJhZilcblxubGV0IGVuY29kZSA9IChzKSA9PiB7XG4gIHJldHVybiBBcnJheS5wcm90b3R5cGUucmV2ZXJzZS5jYWxsKG5ldyBCdWZmZXIocykpLnRvU3RyaW5nKCdoZXgnKVxufVxuXG4vKipcbiAqIEBldmVudCBCaXRjb2luZCNlcnJvclxuICogQHBhcmFtIHtFcnJvcn0gZXJyXG4gKi9cblxuLyoqXG4gKiBAZXZlbnQgQml0Y29pbmQjZGF0YVxuICogQHBhcmFtIHtzdHJpbmd9IGRhdGFcbiAqL1xuXG4vKipcbiAqIEBldmVudCBCaXRjb2luZCNleGl0XG4gKiBAcGFyYW0ge251bWJlcn0gY29kZVxuICogQHBhcmFtIHtzdHJpbmd9IHNpZ25hbFxuICovXG5cbi8qKlxuICogQGV2ZW50IEJpdGNvaW5kI2Jsb2NrXG4gKiBAcGFyYW0ge3N0cmluZ30gaGFzaFxuICovXG5cbi8qKlxuICogQGV2ZW50IEJpdGNvaW5kI3R4XG4gKiBAcGFyYW0ge3N0cmluZ30gdHhpZFxuICovXG5cbi8qKlxuICogQGNsYXNzIEJpdGNvaW5kXG4gKi9cbkBtaXhpbihSZWFkeU1peGluKVxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQml0Y29pbmQgZXh0ZW5kcyBFdmVudEVtaXR0ZXIge1xuICAvKipcbiAgICogQGNvbnN0cnVjdG9yXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0c11cbiAgICogQHBhcmFtIHtPYmplY3R9IFtvcHRzLndhbGxldF0gV2FsbGV0IG9wdGlvbnNcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW29wdHMud2FsbGV0LnByZWxvYWRzUG9vbFNpemU9Xy5jb25zdGFudCgxMCldIFByZWxvYWQgcG9vbCBzaXplXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLndhbGxldC5rZXlzUG9vbFNpemU9KCkgPT4gXy5yYW5kb20oODAsIDEwMCldXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLndhbGxldC5uZXdLZXlUaW1lb3V0PSgpID0+IF8ucmFuZG9tKDYwLCA5MCkgKiAxMDAwXVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbb3B0cy53YWxsZXQuaW5wdXRzQ291bnQ9KCkgPT4gXy5yYW5kb20oMiwgMyldXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLndhbGxldC5vdXRwdXRzQ291bnQ9KCkgPT4gXy5yYW5kb20oMywgNSldXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0cy5nZW5lcmF0ZV0gR2VuZXJhdGUgb3B0aW9uc1xuICAgKiBAcGFyYW0ge09iamVjdH0gW29wdHMuZ2VuZXJhdGUudHhzXVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbb3B0cy5nZW5lcmF0ZS50eHMuYmFja2dyb3VuZD1fLmNvbnN0YW50KHRydWUpXVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbb3B0cy5nZW5lcmF0ZS50eHMudGltZW91dD0oKSA9PiBfLnJhbmRvbSg2LCA5LCB0cnVlKSAqIDEwMDBdXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLmdlbmVyYXRlLnR4cy5taW5JbkJsb2NrPV8uY29uc3RhbnQoNSldXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0cy5nZW5lcmF0ZS5ibG9ja3NdXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLmdlbmVyYXRlLmJsb2Nrcy5iYWNrZ3JvdW5kPV8uY29uc3RhbnQodHJ1ZSldXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLmdlbmVyYXRlLmJsb2Nrcy50aW1lb3V0PSgpID0+IF8ucmFuZG9tKDYwLCA5MCwgdHJ1ZSkgKiAxMDAwXVxuICAgKiBAcGFyYW0ge09iamVjdH0gW29wdHMuYml0Y29pbmRdXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLmJpdGNvaW5kLnBhdGg9KCkgPT4gJy4uL2JpdGNvaW5kL2JpdGNvaW5kLXJldi0zMjI0OTM2J11cbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW29wdHMuYml0Y29pbmQuZGF0YWRpcj1hc3luYyAoKSA9PiBfLmZpcnN0KHRtcGZzLmRpcih7cHJlZml4OiAnYml0Y29pbmQtcmVndGVzdC0nfSkpXVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbb3B0cy5iaXRjb2luZC5wb3J0PSgpID0+IF8ucmFuZG9tKDIwMDAwLCAzMDAwMCldXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLmJpdGNvaW5kLnJwY3BvcnQ9KCkgPT4gXy5yYW5kb20oMjAwMDAsIDMwMDAwKV1cbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW29wdHMuYml0Y29pbmQucnBjdXNlcj0oKSA9PiBjcnlwdG8ucmFuZG9tQnl0ZXMoMTApLnRvU3RyaW5nKCdoZXgnKV1cbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW29wdHMuYml0Y29pbmQucnBjcGFzc3dvcmQ9KCkgPT4gY3J5cHRvLnJhbmRvbUJ5dGVzKDEwKS50b1N0cmluZygnaGV4JyldXG4gICAqL1xuICBjb25zdHJ1Y3RvciAob3B0cykge1xuICAgIHN1cGVyKClcblxuICAgIFBVdGlscy50cnkoYXN5bmMgKCkgPT4ge1xuICAgICAgdGhpcy5fb3B0cyA9IF8uZGVmYXVsdHNEZWVwKF8uY2xvbmVEZWVwKG9wdHMpIHx8IHt9LCB7XG4gICAgICAgIHdhbGxldDoge1xuICAgICAgICAgIHByZWxvYWRzUG9vbFNpemU6IF8uY29uc3RhbnQoMTApLFxuICAgICAgICAgIGtleXNQb29sU2l6ZTogKCkgPT4gXy5yYW5kb20oODAsIDEwMCksXG4gICAgICAgICAgbmV3S2V5VGltZW91dDogKCkgPT4gXy5yYW5kb20oNjAsIDkwKSAqIDEwMDAsXG4gICAgICAgICAgaW5wdXRzQ291bnQ6ICgpID0+IF8ucmFuZG9tKDIsIDIpLFxuICAgICAgICAgIG91dHB1dHNDb3VudDogKCkgPT4gXy5yYW5kb20oMywgNSlcbiAgICAgICAgfSxcbiAgICAgICAgZ2VuZXJhdGU6IHtcbiAgICAgICAgICB0eHM6IHtcbiAgICAgICAgICAgIGJhY2tncm91bmQ6IF8uY29uc3RhbnQodHJ1ZSksXG4gICAgICAgICAgICB0aW1lb3V0OiAoKSA9PiBfLnJhbmRvbSg2LCA5LCB0cnVlKSAqIDEwMDAsXG4gICAgICAgICAgICBtaW5JbkJsb2NrOiBfLmNvbnN0YW50KDUpXG4gICAgICAgICAgfSxcbiAgICAgICAgICBibG9ja3M6IHtcbiAgICAgICAgICAgIGJhY2tncm91bmQ6IF8uY29uc3RhbnQodHJ1ZSksXG4gICAgICAgICAgICB0aW1lb3V0OiAoKSA9PiBfLnJhbmRvbSg2MCwgOTAsIHRydWUpICogMTAwMFxuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgYml0Y29pbmQ6IHtcbiAgICAgICAgICBwYXRoOiAoKSA9PiAnLi4vYml0Y29pbmQvYml0Y29pbmQtcmV2LTMyMjQ5MzYnLFxuICAgICAgICAgIGRhdGFkaXI6IGFzeW5jICgpID0+IF8uZmlyc3QoYXdhaXQgdG1wZnMuZGlyQXN5bmMoe1xuICAgICAgICAgICAgcHJlZml4OiAnYml0Y29pbmQtcmVndGVzdC0nLFxuICAgICAgICAgICAga2VlcDogZmFsc2UsXG4gICAgICAgICAgICB1bnNhZmVDbGVhbnVwOiB0cnVlXG4gICAgICAgICAgfSkpLFxuICAgICAgICAgIHBvcnQ6ICgpID0+IF8ucmFuZG9tKDIwMDAwLCAzMDAwMCksXG4gICAgICAgICAgcnBjcG9ydDogKCkgPT4gXy5yYW5kb20oMjAwMDAsIDMwMDAwKSxcbiAgICAgICAgICBycGN1c2VyOiAoKSA9PiBjcnlwdG8ucmFuZG9tQnl0ZXMoMTApLnRvU3RyaW5nKCdoZXgnKSxcbiAgICAgICAgICBycGNwYXNzd29yZDogKCkgPT4gY3J5cHRvLnJhbmRvbUJ5dGVzKDEwKS50b1N0cmluZygnaGV4JylcbiAgICAgICAgfVxuICAgICAgfSlcblxuICAgICAgLy8gcHJvY2VzcyBvcHRpb25zXG4gICAgICB0aGlzLl9wcm9jZXNzT3B0cyA9IHtcbiAgICAgICAgcGF0aDogdGhpcy5fb3B0cy5iaXRjb2luZC5wYXRoKCksXG4gICAgICAgIGRhdGFkaXI6IGF3YWl0IHRoaXMuX29wdHMuYml0Y29pbmQuZGF0YWRpcigpLFxuICAgICAgICBwb3J0OiB0aGlzLl9vcHRzLmJpdGNvaW5kLnBvcnQoKSxcbiAgICAgICAgcnBjcG9ydDogdGhpcy5fb3B0cy5iaXRjb2luZC5ycGNwb3J0KCksXG4gICAgICAgIHJwY3VzZXI6IHRoaXMuX29wdHMuYml0Y29pbmQucnBjdXNlcigpLFxuICAgICAgICBycGNwYXNzd29yZDogdGhpcy5fb3B0cy5iaXRjb2luZC5ycGNwYXNzd29yZCgpXG4gICAgICB9XG5cbiAgICAgIC8vIGluaXQgYml0Y29pbmQgcHJvY2Vzc1xuICAgICAgdGhpcy5fcHJvY2VzcyA9IHNwYXduKHRoaXMuX29wdHMuYml0Y29pbmQucGF0aCgpLCBbXG4gICAgICAgIGAtcmVndGVzdGAsXG4gICAgICAgIGAtdHhpbmRleD0xYCxcbiAgICAgICAgYC1zZXJ2ZXJgLFxuICAgICAgICBgLWRhdGFkaXI9JHt0aGlzLl9wcm9jZXNzT3B0cy5kYXRhZGlyfWAsXG4gICAgICAgIGAtcG9ydD0ke3RoaXMuX3Byb2Nlc3NPcHRzLnBvcnR9YCxcbiAgICAgICAgYC1ycGNwb3J0PSR7dGhpcy5fcHJvY2Vzc09wdHMucnBjcG9ydH1gLFxuICAgICAgICBgLXJwY3VzZXI9JHt0aGlzLl9wcm9jZXNzT3B0cy5ycGN1c2VyfWAsXG4gICAgICAgIGAtcnBjcGFzc3dvcmQ9JHt0aGlzLl9wcm9jZXNzT3B0cy5ycGNwYXNzd29yZH1gLFxuICAgICAgICBgLXByaW50dG9jb25zb2xlYFxuICAgICAgXSwge1xuICAgICAgICBjd2Q6IF9fZGlybmFtZSxcbiAgICAgICAgc3RkaW86IFsnaWdub3JlJywgJ3BpcGUnLCAncGlwZSddXG4gICAgICB9KVxuICAgICAgdGhpcy5fcHJvY2Vzcy5vbignZXJyb3InLCAoZXJyKSA9PiB7IHRoaXMuZW1pdCgnZXJyb3InLCBlcnIpIH0pXG4gICAgICB0aGlzLl9wcm9jZXNzLm9uKCdleGl0JywgKGNvZGUsIHNpZ25hbCkgPT4geyB0aGlzLmVtaXQoJ2V4aXQnLCBjb2RlLCBzaWduYWwpIH0pXG4gICAgICB0aGlzLl9wcm9jZXNzLnN0ZG91dC5vbignZXJyb3InLCAoZXJyKSA9PiB7IHRoaXMuZW1pdCgnZXJyb3InLCBlcnIpIH0pXG4gICAgICB0aGlzLl9wcm9jZXNzLnN0ZG91dC5vbignZGF0YScsIChkYXRhKSA9PiB7IHRoaXMuZW1pdCgnZGF0YScsIGRhdGEpIH0pXG4gICAgICB0aGlzLl9wcm9jZXNzLnN0ZGVyci5vbignZXJyb3InLCAoZXJyKSA9PiB7IHRoaXMuZW1pdCgnZXJyb3InLCBlcnIpIH0pXG4gICAgICB0aGlzLl9wcm9jZXNzLnN0ZGVyci5vbignZGF0YScsIChkYXRhKSA9PiB7IHRoaXMuZW1pdCgnZGF0YScsIGRhdGEpIH0pXG5cbiAgICAgIC8vIGF3YWl0IHJ1bm5pbmcgYml0Y29pbmRcbiAgICAgIGxldCBvbkVycm9yRXhpdFxuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgIHNldFRpbWVvdXQocmVzb2x2ZSwgMTAwMClcblxuICAgICAgICAgIG9uRXJyb3JFeGl0ID0gKGNvZGUsIHNpZ25hbCkgPT4ge1xuICAgICAgICAgICAgaWYgKGNvZGUgaW5zdGFuY2VvZiBFcnJvciAmJiBzaWduYWwgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICByZWplY3QoY29kZSlcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRXhpdCB3aXRoIGNvZGUgPSAke2NvZGV9IG9uIHNpZ25hbCA9ICR7c2lnbmFsfWApKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIHRoaXMuX3Byb2Nlc3Mub24oJ2Vycm9yJywgb25FcnJvckV4aXQpXG4gICAgICAgICAgdGhpcy5fcHJvY2Vzcy5vbignZXhpdCcsIG9uRXJyb3JFeGl0KVxuICAgICAgICB9KVxuICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgdGhpcy5fcHJvY2Vzcy5yZW1vdmVMaXN0ZW5lcignZXJyb3InLCBvbkVycm9yRXhpdClcbiAgICAgICAgdGhpcy5fcHJvY2Vzcy5yZW1vdmVMaXN0ZW5lcignZXhpdCcsIG9uRXJyb3JFeGl0KVxuICAgICAgfVxuXG4gICAgICAvLyBpbml0aWFsaXplIHBlZXJcbiAgICAgIHRoaXMuX3BlZXIgPSBuZXcgcDJwLlBlZXIoe1xuICAgICAgICBob3N0OiAnMTI3LjAuMC4xJyxcbiAgICAgICAgcG9ydDogdGhpcy5fcHJvY2Vzc09wdHMucG9ydCxcbiAgICAgICAgbmV0d29yazogYml0Y29yZS5OZXR3b3Jrcy5nZXQoJ3JlZ3Rlc3QnKVxuICAgICAgfSlcblxuICAgICAgdGhpcy5fcGVlci5vbignaW52JywgKG1lc3NhZ2UpID0+IHtcbiAgICAgICAgbGV0IG5hbWVzID0gW11cblxuICAgICAgICBmb3IgKGxldCBpbnYgb2YgbWVzc2FnZS5pbnZlbnRvcnkpIHtcbiAgICAgICAgICAvLyBzdG9yZSBpbnYgdHlwZSBuYW1lXG4gICAgICAgICAgbmFtZXMucHVzaChwMnAuSW52ZW50b3J5LlRZUEVfTkFNRVtpbnYudHlwZV0pXG5cbiAgICAgICAgICAvLyBzdG9yZSBpbnYgaWYgdHggdHlwZVxuICAgICAgICAgIGlmIChpbnYudHlwZSA9PT0gcDJwLkludmVudG9yeS5UWVBFLlRYKSB7XG4gICAgICAgICAgICB0aGlzLmVtaXQoJ3R4JywgZW5jb2RlKGludi5oYXNoKSlcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBlbWl0IGJsb2NrIGlmIGJsb2NrIHR5cGVcbiAgICAgICAgICBpZiAoaW52LnR5cGUgPT09IHAycC5JbnZlbnRvcnkuVFlQRS5CTE9DSykge1xuICAgICAgICAgICAgdGhpcy5lbWl0KCdibG9jaycsIGVuY29kZShpbnYuaGFzaCkpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9KVxuXG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICB0aGlzLl9wZWVyLm9uY2UoJ3JlYWR5JywgcmVzb2x2ZSlcbiAgICAgICAgdGhpcy5fcGVlci5jb25uZWN0KClcbiAgICAgIH0pXG5cbiAgICAgIC8vIHdhaXQgd2FsbGV0IGluaXRpYWxpemF0aW9uXG4gICAgICB0aGlzLl93YWxsZXQgPSBuZXcgV2FsbGV0KHRoaXMpXG4gICAgICB0aGlzLl93YWxsZXQub24oJ2Vycm9yJywgdGhpcy5lbWl0LmJpbmQodGhpcywgJ2Vycm9yJykpXG4gICAgICBhd2FpdCB0aGlzLl93YWxsZXQucmVhZHlcbiAgICB9KVxuICAgIC50aGVuKCgpID0+IHsgdGhpcy5fcmVhZHkobnVsbCkgfSwgKGVycikgPT4geyB0aGlzLl9yZWFkeShlcnIpIH0pXG5cbiAgICAvLyBsb29wIGZvciBjcmVhdGluZyBuZXcgdHJhbnNhY3Rpb25zIGluIGJhY2tncm91bmRcbiAgICB0aGlzLnJlYWR5LnRoZW4oYXN5bmMgKCkgPT4ge1xuICAgICAgd2hpbGUgKHRydWUpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCBQVXRpbHMuZGVsYXkodGhpcy5fb3B0cy5nZW5lcmF0ZS50eHMudGltZW91dCgpKVxuICAgICAgICAgIGlmICh0aGlzLl9vcHRzLmdlbmVyYXRlLnR4cy5iYWNrZ3JvdW5kKCkpIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZ2VuZXJhdGVUeHMoMSlcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIHRoaXMuZW1pdChlcnIpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KVxuXG4gICAgLy8gbG9vcCBmb3IgY3JlYXRpbmcgbmV3IGJsb2NrcyBpbiBiYWNrZ3JvdW5kXG4gICAgdGhpcy5yZWFkeS50aGVuKGFzeW5jICgpID0+IHtcbiAgICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgYXdhaXQgUFV0aWxzLmRlbGF5KHRoaXMuX29wdHMuZ2VuZXJhdGUuYmxvY2tzLnRpbWVvdXQoKSlcbiAgICAgICAgICBpZiAodGhpcy5fb3B0cy5nZW5lcmF0ZS5ibG9ja3MuYmFja2dyb3VuZCgpKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmdlbmVyYXRlQmxvY2tzKDEpXG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICB0aGlzLmVtaXQoZXJyKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gdmFsdWUgZm9yIGdpdmVuIG9wdGlvbiBgbmFtZWBcbiAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWVcbiAgICogQHJldHVybiB7Kn1cbiAgICovXG4gIGdldE9wdGlvbiAobmFtZSkgeyByZXR1cm4gXy5nZXQodGhpcy5fb3B0cywgbmFtZSkgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZVxuICAgKiBAcGFyYW0geyp9XG4gICAqL1xuICBzZXRPcHRpb24gKG5hbWUsIHZhbHVlKSB7IF8uc2V0KHRoaXMuX29wdHMsIG5hbWUsIHZhbHVlKSB9XG5cbiAgLyoqXG4gICAqIFJldHVybiBpbnN0YW5jZSBvZiBgUnBjQ2xpZW50YFxuICAgKiBAcmV0dXJuIHtScGNDbGllbnR9XG4gICAqL1xuICBnZXRScGNDbGllbnQgKCkge1xuICAgIHJldHVybiBuZXcgUnBjQ2xpZW50KHtcbiAgICAgIGhvc3Q6ICcxMjcuMC4wLjEnLFxuICAgICAgcG9ydDogdGhpcy5fcHJvY2Vzc09wdHMucnBjcG9ydCxcbiAgICAgIHVzZXI6IHRoaXMuX3Byb2Nlc3NPcHRzLnJwY3VzZXIsXG4gICAgICBwYXNzOiB0aGlzLl9wcm9jZXNzT3B0cy5ycGNwYXNzd29yZFxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogU2FtZSBhcyBmdW5jIGdldFJwY0NsaWVudFxuICAgKi9cbiAgZ2V0IHJwYyAoKSB7IHJldHVybiB0aGlzLmdldFJwY0NsaWVudCgpIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gZm5cbiAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICovXG4gIEBtYWtlQ29uY3VycmVudCh7Y29uY3VycmVuY3k6IDF9KVxuICB3aXRoTG9jayAoZm4pIHsgcmV0dXJuIGZuKCkgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBgY291bnRgIGJsb2Nrc1xuICAgKiBAcGFyYW0ge251bWJlcn0gY291bnRcbiAgICogQHJldHVybiB7UHJvbWlzZS48QXJyYXkuPHN0cmluZz4+fVxuICAgKi9cbiAgQG1ha2VDb25jdXJyZW50KHtjb25jdXJyZW5jeTogMX0pXG4gIGFzeW5jIGdlbmVyYXRlQmxvY2tzIChjb3VudCkge1xuICAgIGF3YWl0IHRoaXMucmVhZHlcblxuICAgIGxldCBoYXNoZXMgPSBbXVxuICAgIGZvciAoOyBjb3VudCA+IDA7IGNvdW50IC09IDEpIHtcbiAgICAgIGxldCByZXF1aXJlZE1lbVBvb2xTaXplID0gdGhpcy5fb3B0cy5nZW5lcmF0ZS50eHMubWluSW5CbG9jaygpXG4gICAgICBsZXQgbWVtUG9vbFNpemUgPSAoYXdhaXQgdGhpcy5ycGMuZ2V0TWVtUG9vbEluZm8oKSkucmVzdWx0LnNpemVcbiAgICAgIGF3YWl0IHRoaXMuZ2VuZXJhdGVUeHMocmVxdWlyZWRNZW1Qb29sU2l6ZSAtIG1lbVBvb2xTaXplKVxuICAgICAgYXdhaXQgdGhpcy53aXRoTG9jayhhc3luYyAoKSA9PiB7XG4gICAgICAgIGxldCByZXQgPSBhd2FpdCB0aGlzLnJwYy5nZW5lcmF0ZSgxKVxuICAgICAgICBoYXNoZXMucHVzaChyZXQucmVzdWx0WzBdKVxuICAgICAgfSlcbiAgICB9XG5cbiAgICByZXR1cm4gaGFzaGVzXG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGUgYGNvdW50YCB0cmFuc2FjdGlvbnNcbiAgICogQHBhcmFtIHtudW1iZXJ9IGNvdW50XG4gICAqIEByZXR1cm4ge1Byb21pc2UuPEFycmF5Ljw/c3RyaW5nPj59XG4gICAqL1xuICBhc3luYyBnZW5lcmF0ZVR4cyAoY291bnQpIHtcbiAgICBhd2FpdCB0aGlzLnJlYWR5XG4gICAgcmV0dXJuIGF3YWl0KiBfLnRpbWVzKGNvdW50KS5tYXAoKCkgPT4ge1xuICAgICAgcmV0dXJuIHRoaXMuX3dhbGxldC5nZW5lcmF0ZVR4KClcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IEJpdGNvaW5kflByZWxvYWRPYmplY3RcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IHR4SWRcbiAgICogQHByb3BlcnR5IHtudW1iZXJ9IG91dEluZGV4XG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSB2YWx1ZVxuICAgKiBAcHJvcGVydHkge2JpdGNvcmUuU2NyaXB0fSBzY3JpcHRcbiAgICogQHByb3BlcnR5IHtiaXRjb3JlLlByaXZhdGVLZXl9IHByaXZLZXlcbiAgICovXG5cbiAgLyoqXG4gICAqIFJldHVybiBPYmplY3Qgd2l0aCB1dHhvIGRhdGEgYW5kIHdpdGggcHJpdmF0ZSBrZXkgKGJhbGFuY2UgaXMgMTBidGMpXG4gICAqIEByZXR1cm4ge1Byb21pc2UuPEJpdGNvaW5kflByZWxvYWRPYmplY3Q+fVxuICAgKi9cbiAgYXN5bmMgZ2V0UHJlbG9hZCAoKSB7XG4gICAgYXdhaXQgdGhpcy5yZWFkeVxuICAgIHJldHVybiBhd2FpdCB0aGlzLl93YWxsZXQuZ2V0UHJlbG9hZCgpXG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIGluc3RhbmNlIG9mIGBCaXRjb2luZGAgdGhhdCBoYXZlIHNhbWUgYmxvY2tjaGFpblxuICAgKiBAcGFyYW0ge09iamVjdH0gW29wdHNdXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuY29ubmVjdGVkPXRydWVdXG4gICAqIEByZXR1cm4ge1Byb21pc2UuPEJpdGNvaW5kPn1cbiAgICovXG4gIGFzeW5jIGZvcmsgKG9wdHMpIHtcbiAgICBsZXQgYm9wdHMgPSBfLmNsb25lRGVlcCh0aGlzLl9vcHRzKVxuICAgIGJvcHRzLmdlbmVyYXRlLnR4cy5iYWNrZ3JvdW5kID0gXy5jb25zdGFudChmYWxzZSlcbiAgICBib3B0cy5nZW5lcmF0ZS5ibG9ja3MuYmFja2dyb3VuZCA9IF8uY29uc3RhbnQoZmFsc2UpXG4gICAgbGV0IG90aGVyID0gbmV3IEJpdGNvaW5kKGJvcHRzKVxuXG4gICAgYXdhaXQgb3RoZXIucmVhZHlcbiAgICBhd2FpdCB0aGlzLmNvbm5lY3Qob3RoZXIpXG5cbiAgICBpZiAoKGF3YWl0IHRoaXMucnBjLmdldEJsb2NrQ291bnQoKSkucmVzdWx0ID4gMCkge1xuICAgICAgYXdhaXQgb3RoZXIuZ2VuZXJhdGVCbG9ja3MoMSlcblxuICAgICAgd2hpbGUgKHRydWUpIHtcbiAgICAgICAgbGV0IFttaGFzaCwgb2hhc2hdID0gYXdhaXQqIFt0aGlzLnJwYy5nZXRCZXN0QmxvY2tIYXNoKCksIG90aGVyLnJwYy5nZXRCZXN0QmxvY2tIYXNoKCldXG4gICAgICAgIGlmIChtaGFzaC5yZXN1bHQgPT09IG9oYXNoLnJlc3VsdCkge1xuICAgICAgICAgIGJyZWFrXG4gICAgICAgIH1cblxuICAgICAgICBhd2FpdCBQVXRpbHMuZGVsYXkoNTAwKVxuICAgICAgfVxuICAgIH1cblxuICAgIG90aGVyLnNldE9wdGlvbignZ2VuZXJhdGUudHhzLmJhY2tncm91bmQnLCB0aGlzLl9vcHRzLmdlbmVyYXRlLnR4cy5iYWNrZ3JvdW5kKVxuICAgIG90aGVyLnNldE9wdGlvbignZ2VuZXJhdGUuYmxvY2tzLmJhY2tncm91bmQnLCB0aGlzLl9vcHRzLmdlbmVyYXRlLmJsb2Nrcy5iYWNrZ3JvdW5kKVxuXG4gICAgaWYgKCFPYmplY3Qob3B0cykuY29ubmVjdGVkKSB7XG4gICAgICBhd2FpdCB0aGlzLmRpc2Nvbm5lY3Qob3RoZXIpXG4gICAgfVxuXG4gICAgcmV0dXJuIG90aGVyXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtCaXRjb2luZH1cbiAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICovXG4gIGFzeW5jIGNvbm5lY3QgKG90aGVyKSB7XG4gICAgbGV0IG5vZGUgPSBgMTI3LjAuMC4xOiR7b3RoZXIuX3Byb2Nlc3NPcHRzLnBvcnR9YFxuICAgIGF3YWl0IHRoaXMucnBjLmFkZE5vZGUobm9kZSwgJ29uZXRyeScpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtCaXRjb2luZH1cbiAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICovXG4gIGFzeW5jIGRpc2Nvbm5lY3QgKG90aGVyKSB7XG4gICAgbGV0IG5vZGUgPSBgMTI3LjAuMC4xOiR7b3RoZXIuX3Byb2Nlc3NPcHRzLnBvcnR9YFxuICAgIGF3YWl0IHRoaXMucnBjLmRpc2Nvbm5lY3ROb2RlKG5vZGUpXG4gIH1cblxuICAvKipcbiAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICovXG4gIEBtYWtlQ29uY3VycmVudCh7Y29uY3VycmVuY3k6IDF9KVxuICBhc3luYyB0ZXJtaW5hdGUgKCkge1xuICAgIGxldCBvbkVycm9yXG4gICAgbGV0IG9uRXhpdFxuXG4gICAgaWYgKHRoaXMuX3Byb2Nlc3MgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2hpbGQgcHJvY2VzcyBhbHJlYWR5IHRlcm1pbmF0ZWQnKVxuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIG9uRXJyb3IgPSByZWplY3RcbiAgICAgICAgb25FeGl0ID0gYXN5bmMgKGNvZGUsIHNpZ25hbCkgPT4ge1xuICAgICAgICAgIGlmICghKGNvZGUgPT09IDAgJiYgc2lnbmFsID09PSBudWxsKSkge1xuICAgICAgICAgICAgcmV0dXJuIHJlamVjdChcbiAgICAgICAgICAgICAgbmV3IEVycm9yKGBFeGl0IHdpdGggY29kZSA9ICR7Y29kZX0gb24gc2lnbmFsID0gJHtzaWduYWx9YCkpXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHRoaXMuX3dhbGxldC5yZW1vdmVBbGxMaXN0ZW5lcnMoKVxuICAgICAgICAgICAgdGhpcy5fcGVlci5yZW1vdmVBbGxMaXN0ZW5lcnMoKVxuICAgICAgICAgICAgdGhpcy5fcHJvY2Vzcy5yZW1vdmVBbGxMaXN0ZW5lcnMoKVxuICAgICAgICAgICAgdGhpcy5fcHJvY2VzcyA9IG51bGxcbiAgICAgICAgICAgIGF3YWl0IHJpbXJhZih0aGlzLl9wcm9jZXNzT3B0cy5kYXRhZGlyKVxuICAgICAgICAgICAgcmVzb2x2ZSgpXG4gICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICByZWplY3QoZXJyKVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX3Byb2Nlc3Mub24oJ2Vycm9yJywgb25FcnJvcilcbiAgICAgICAgdGhpcy5fcHJvY2Vzcy5vbignZXhpdCcsIG9uRXhpdClcblxuICAgICAgICB0aGlzLl9wcm9jZXNzLmtpbGwoJ1NJR1RFUk0nKVxuICAgICAgfSlcbiAgICB9IGZpbmFsbHkge1xuICAgICAgaWYgKHRoaXMuX3Byb2Nlc3MgIT09IG51bGwpIHtcbiAgICAgICAgdGhpcy5fcHJvY2Vzcy5yZW1vdmVMaXN0ZW5lcignZXJyb3InLCBvbkVycm9yKVxuICAgICAgICB0aGlzLl9wcm9jZXNzLnJlbW92ZUxpc3RlbmVyKCdleGl0Jywgb25FeGl0KVxuICAgICAgfVxuICAgIH1cbiAgfVxufVxuIl19