@vechain/connex.driver-nodejs
Version:
Connex framework driver implementation in NodeJS
198 lines • 16.4 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const promint_1 = require("./promint");
const cache_1 = require("./cache");
const blake2b_1 = require("thor-devkit/dist/cry/blake2b");
const common_1 = require("./common");
const options_1 = require("./options");
/** class implements Connex.Driver leaves out Vendor related methods */
class DriverNoVendor {
constructor(net, genesis, initialHead) {
this.net = net;
this.genesis = genesis;
this.headResolvers = [];
this.int = new promint_1.PromInt();
this.cache = new cache_1.Cache();
if (initialHead) {
this.head = initialHead;
}
else {
this.head = {
id: genesis.id,
number: genesis.number,
timestamp: genesis.timestamp,
parentID: genesis.parentID,
txsFeatures: genesis.txsFeatures
};
}
this.headTrackerLoop();
}
// close the driver to prevent mem leak
close() {
this.int.interrupt();
}
// implementations
pollHead() {
return this.int.wrap(new Promise(resolve => {
this.headResolvers.push(() => resolve(this.head));
}));
}
getBlock(revision) {
return this.cache.getBlock(revision, () => this.httpGet(`blocks/${revision}`));
}
getTransaction(id) {
return this.cache.getTx(id, () => this.httpGet(`transactions/${id}`, { head: this.head.id }));
}
getReceipt(id) {
return this.cache.getReceipt(id, () => this.httpGet(`transactions/${id}/receipt`, { head: this.head.id }));
}
getAccount(addr, revision) {
return this.cache.getAccount(addr, revision, () => this.httpGet(`accounts/${addr}`, { revision }));
}
getCode(addr, revision) {
return this.cache.getTied(`code-${addr}`, revision, () => this.httpGet(`accounts/${addr}/code`, { revision }));
}
getStorage(addr, key, revision) {
return this.cache.getTied(`storage-${addr}-${key}`, revision, () => this.httpGet(`accounts/${addr}/storage/${key}`, { revision }));
}
explain(arg, revision, cacheTies) {
const cacheKey = `explain-${blake2b_1.blake2b256(JSON.stringify(arg)).toString('hex')}`;
return this.cache.getTied(cacheKey, revision, () => this.httpPost('accounts/*', arg, { revision }), cacheTies);
}
filterEventLogs(arg) {
const cacheKey = `event-${blake2b_1.blake2b256(JSON.stringify(arg)).toString('hex')}`;
return this.cache.getTied(cacheKey, this.head.id, () => this.httpPost('logs/event', arg));
}
filterTransferLogs(arg) {
const cacheKey = `transfer-${blake2b_1.blake2b256(JSON.stringify(arg)).toString('hex')}`;
return this.cache.getTied(cacheKey, this.head.id, () => this.httpPost('logs/transfer', arg));
}
signTx(msg, option) {
throw new Error('not implemented');
}
signCert(msg, options) {
throw new Error(' not implemented');
}
isAddressOwned(addr) {
return Promise.resolve(false);
}
//////
httpGet(path, query) {
return this.net.http('GET', path, {
query,
validateResponseHeader: this.headerValidator
});
}
httpPost(path, body, query) {
return this.net.http('POST', path, {
query,
body,
validateResponseHeader: this.headerValidator
});
}
get headerValidator() {
return (headers) => {
const xgid = headers['x-genesis-id'];
if (xgid && xgid !== this.genesis.id) {
throw new Error(`responded 'x-genesis-id' not matched`);
}
};
}
emitNewHead() {
const resolvers = this.headResolvers;
this.headResolvers = [];
resolvers.forEach(r => r());
}
headTrackerLoop() {
return __awaiter(this, void 0, void 0, function* () {
let triggerWs = 0;
for (;;) {
try {
const best = yield this.int.wrap(this.httpGet('blocks/best'));
if (best.id !== this.head.id && best.number >= this.head.number) {
this.head = {
id: best.id,
number: best.number,
timestamp: best.timestamp,
parentID: best.parentID,
txsFeatures: best.txsFeatures
};
this.cache.handleNewBlock(this.head, undefined, best);
this.emitNewHead();
if (Date.now() - this.head.timestamp * 1000 < 60 * 1000) {
// nearly synced
triggerWs++;
}
}
}
catch (err) {
triggerWs = 0;
if (!options_1.options.disableErrorLog) {
// tslint:disable-next-line: no-console
console.warn('headTracker(http):', err);
}
if (err instanceof promint_1.InterruptedError) {
break;
}
}
if (triggerWs > 2) {
triggerWs = 0;
try {
yield this.trackWs();
}
catch (err) {
if (!options_1.options.disableErrorLog) {
// tslint:disable-next-line: no-console
console.warn('headTracker(ws):', err);
}
if (err instanceof promint_1.InterruptedError) {
break;
}
}
}
try {
yield this.int.wrap(common_1.sleep(8 * 1000));
}
catch (_a) {
break;
}
}
});
}
trackWs() {
return __awaiter(this, void 0, void 0, function* () {
const wsPath = `subscriptions/beat?pos=${this.head.parentID}`;
const wsr = this.net.openWebSocketReader(wsPath);
try {
for (;;) {
const data = yield this.int.wrap(wsr.read());
const beat = JSON.parse(data);
if (!beat.obsolete && beat.id !== this.head.id && beat.number >= this.head.number) {
this.head = {
id: beat.id,
number: beat.number,
timestamp: beat.timestamp,
parentID: beat.parentID,
txsFeatures: beat.txsFeatures
};
this.cache.handleNewBlock(this.head, { k: beat.k, bits: beat.bloom });
this.emitNewHead();
}
}
}
finally {
wsr.close();
}
});
}
}
exports.DriverNoVendor = DriverNoVendor;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJpdmVyLW5vLXZlbmRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9kcml2ZXItbm8tdmVuZG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFDQSx1Q0FBcUQ7QUFDckQsbUNBQStCO0FBQy9CLDBEQUF5RDtBQUN6RCxxQ0FBZ0M7QUFDaEMsdUNBQW1DO0FBRW5DLHVFQUF1RTtBQUN2RSxNQUFhLGNBQWM7SUFPdkIsWUFDcUIsR0FBUSxFQUNoQixPQUEwQixFQUNuQyxXQUF3QztRQUZ2QixRQUFHLEdBQUgsR0FBRyxDQUFLO1FBQ2hCLFlBQU8sR0FBUCxPQUFPLENBQW1CO1FBTi9CLGtCQUFhLEdBQUcsRUFBdUIsQ0FBQTtRQUM5QixRQUFHLEdBQUcsSUFBSSxpQkFBTyxFQUFFLENBQUE7UUFDbkIsVUFBSyxHQUFHLElBQUksYUFBSyxFQUFFLENBQUE7UUFPaEMsSUFBSSxXQUFXLEVBQUU7WUFDYixJQUFJLENBQUMsSUFBSSxHQUFHLFdBQVcsQ0FBQTtTQUMxQjthQUFNO1lBQ0gsSUFBSSxDQUFDLElBQUksR0FBRztnQkFDUixFQUFFLEVBQUUsT0FBTyxDQUFDLEVBQUU7Z0JBQ2QsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNO2dCQUN0QixTQUFTLEVBQUUsT0FBTyxDQUFDLFNBQVM7Z0JBQzVCLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUTtnQkFDMUIsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXO2FBQ25DLENBQUE7U0FDSjtRQUNELElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQTtJQUMxQixDQUFDO0lBRUQsdUNBQXVDO0lBQ2hDLEtBQUs7UUFDUixJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxDQUFBO0lBQ3hCLENBQUM7SUFFRCxrQkFBa0I7SUFDWCxRQUFRO1FBQ1gsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FDaEIsSUFBSSxPQUFPLENBQTZCLE9BQU8sQ0FBQyxFQUFFO1lBQzlDLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQTtRQUNyRCxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQ1gsQ0FBQztJQUVNLFFBQVEsQ0FBQyxRQUF5QjtRQUNyQyxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FDdEMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQTtJQUMzQyxDQUFDO0lBQ00sY0FBYyxDQUFDLEVBQVU7UUFDNUIsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQzdCLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFBO0lBQ25FLENBQUM7SUFDTSxVQUFVLENBQUMsRUFBVTtRQUN4QixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FDbEMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxVQUFVLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUE7SUFDM0UsQ0FBQztJQUNNLFVBQVUsQ0FBQyxJQUFZLEVBQUUsUUFBZ0I7UUFDNUMsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRSxDQUM5QyxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksSUFBSSxFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUE7SUFDdkQsQ0FBQztJQUNNLE9BQU8sQ0FBQyxJQUFZLEVBQUUsUUFBZ0I7UUFDekMsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLElBQUksRUFBRSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FDckQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLElBQUksT0FBTyxFQUFFLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFBO0lBQzVELENBQUM7SUFDTSxVQUFVLENBQUMsSUFBWSxFQUFFLEdBQVcsRUFBRSxRQUFnQjtRQUN6RCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFdBQVcsSUFBSSxJQUFJLEdBQUcsRUFBRSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FDL0QsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLElBQUksWUFBWSxHQUFHLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQTtJQUN0RSxDQUFDO0lBQ00sT0FBTyxDQUFDLEdBQTZCLEVBQUUsUUFBZ0IsRUFBRSxTQUFvQjtRQUNoRixNQUFNLFFBQVEsR0FBRyxXQUFXLG9CQUFVLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFBO1FBQzdFLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FDL0MsSUFBSSxDQUFDLFFBQVEsQ0FBQyxZQUFZLEVBQUUsR0FBRyxFQUFFLEVBQUUsUUFBUSxFQUFFLENBQUMsRUFBRSxTQUFTLENBQUMsQ0FBQTtJQUNsRSxDQUFDO0lBQ00sZUFBZSxDQUFDLEdBQXFDO1FBQ3hELE1BQU0sUUFBUSxHQUFHLFNBQVMsb0JBQVUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUE7UUFDM0UsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQ25ELElBQUksQ0FBQyxRQUFRLENBQUMsWUFBWSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUE7SUFDekMsQ0FBQztJQUNNLGtCQUFrQixDQUFDLEdBQXdDO1FBQzlELE1BQU0sUUFBUSxHQUFHLFlBQVksb0JBQVUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUE7UUFDOUUsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQ25ELElBQUksQ0FBQyxRQUFRLENBQUMsZUFBZSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUE7SUFDNUMsQ0FBQztJQUNNLE1BQU0sQ0FDVCxHQUE0QixFQUM1QixNQUFrQztRQUVsQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixDQUFDLENBQUE7SUFDdEMsQ0FBQztJQUNNLFFBQVEsQ0FDWCxHQUE4QixFQUM5QixPQUFxQztRQUVyQyxNQUFNLElBQUksS0FBSyxDQUFDLGtCQUFrQixDQUFDLENBQUE7SUFDdkMsQ0FBQztJQUNNLGNBQWMsQ0FBQyxJQUFZO1FBQzlCLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUNqQyxDQUFDO0lBQ0QsTUFBTTtJQUNJLE9BQU8sQ0FBQyxJQUFZLEVBQUUsS0FBOEI7UUFDMUQsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFO1lBQzlCLEtBQUs7WUFDTCxzQkFBc0IsRUFBRSxJQUFJLENBQUMsZUFBZTtTQUMvQyxDQUFDLENBQUE7SUFDTixDQUFDO0lBRVMsUUFBUSxDQUFDLElBQVksRUFBRSxJQUFTLEVBQUUsS0FBOEI7UUFDdEUsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFO1lBQy9CLEtBQUs7WUFDTCxJQUFJO1lBQ0osc0JBQXNCLEVBQUUsSUFBSSxDQUFDLGVBQWU7U0FDL0MsQ0FBQyxDQUFBO0lBQ04sQ0FBQztJQUVELElBQVksZUFBZTtRQUN2QixPQUFPLENBQUMsT0FBK0IsRUFBRSxFQUFFO1lBQ3ZDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBQTtZQUNwQyxJQUFJLElBQUksSUFBSSxJQUFJLEtBQUssSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUU7Z0JBQ2xDLE1BQU0sSUFBSSxLQUFLLENBQUMsc0NBQXNDLENBQUMsQ0FBQTthQUMxRDtRQUNMLENBQUMsQ0FBQTtJQUNMLENBQUM7SUFFTyxXQUFXO1FBQ2YsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQTtRQUNwQyxJQUFJLENBQUMsYUFBYSxHQUFHLEVBQUUsQ0FBQTtRQUN2QixTQUFTLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQTtJQUMvQixDQUFDO0lBRWEsZUFBZTs7WUFDekIsSUFBSSxTQUFTLEdBQUcsQ0FBQyxDQUFBO1lBQ2pCLFNBQVU7Z0JBQ04sSUFBSTtvQkFDQSxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFvQixJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUE7b0JBQ2hGLElBQUksSUFBSSxDQUFDLEVBQUUsS0FBSyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsSUFBSSxJQUFJLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFO3dCQUM3RCxJQUFJLENBQUMsSUFBSSxHQUFHOzRCQUNSLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTs0QkFDWCxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07NEJBQ25CLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUzs0QkFDekIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFROzRCQUN2QixXQUFXLEVBQUUsSUFBSSxDQUFDLFdBQVc7eUJBQ2hDLENBQUE7d0JBQ0QsSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLENBQUE7d0JBQ3JELElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQTt3QkFFbEIsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxHQUFHLEVBQUUsR0FBRyxJQUFJLEVBQUU7NEJBQ3JELGdCQUFnQjs0QkFDaEIsU0FBUyxFQUFFLENBQUE7eUJBQ2Q7cUJBQ0o7aUJBQ0o7Z0JBQUMsT0FBTyxHQUFHLEVBQUU7b0JBQ1YsU0FBUyxHQUFHLENBQUMsQ0FBQTtvQkFDYixJQUFJLENBQUMsaUJBQU8sQ0FBQyxlQUFlLEVBQUU7d0JBQzFCLHVDQUF1Qzt3QkFDdkMsT0FBTyxDQUFDLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxHQUFHLENBQUMsQ0FBQTtxQkFDMUM7b0JBQ0QsSUFBSSxHQUFHLFlBQVksMEJBQWdCLEVBQUU7d0JBQ2pDLE1BQUs7cUJBQ1I7aUJBQ0o7Z0JBRUQsSUFBSSxTQUFTLEdBQUcsQ0FBQyxFQUFFO29CQUNmLFNBQVMsR0FBRyxDQUFDLENBQUE7b0JBQ2IsSUFBSTt3QkFDQSxNQUFNLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQTtxQkFDdkI7b0JBQUMsT0FBTyxHQUFHLEVBQUU7d0JBQ1YsSUFBSSxDQUFDLGlCQUFPLENBQUMsZUFBZSxFQUFFOzRCQUMxQix1Q0FBdUM7NEJBQ3ZDLE9BQU8sQ0FBQyxJQUFJLENBQUMsa0JBQWtCLEVBQUUsR0FBRyxDQUFDLENBQUE7eUJBQ3hDO3dCQUNELElBQUksR0FBRyxZQUFZLDBCQUFnQixFQUFFOzRCQUNqQyxNQUFLO3lCQUNSO3FCQUNKO2lCQUNKO2dCQUNELElBQUk7b0JBQ0EsTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxjQUFLLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUE7aUJBQ3ZDO2dCQUFDLFdBQU07b0JBQ0osTUFBSztpQkFDUjthQUNKO1FBQ0wsQ0FBQztLQUFBO0lBRWEsT0FBTzs7WUFDakIsTUFBTSxNQUFNLEdBQ1IsMEJBQTBCLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUE7WUFFbEQsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQTtZQUVoRCxJQUFJO2dCQUNBLFNBQVU7b0JBQ04sTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQTtvQkFDNUMsTUFBTSxJQUFJLEdBQVMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQTtvQkFDbkMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLEVBQUUsS0FBSyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsSUFBSSxJQUFJLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFO3dCQUMvRSxJQUFJLENBQUMsSUFBSSxHQUFHOzRCQUNSLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTs0QkFDWCxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07NEJBQ25CLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUzs0QkFDekIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFROzRCQUN2QixXQUFXLEVBQUUsSUFBSSxDQUFDLFdBQVc7eUJBQ2hDLENBQUE7d0JBQ0QsSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQTt3QkFDckUsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFBO3FCQUNyQjtpQkFDSjthQUNKO29CQUFTO2dCQUNOLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQTthQUNkO1FBQ0wsQ0FBQztLQUFBO0NBQ0o7QUE1TUQsd0NBNE1DIn0=