1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | /// <reference types="@reactivex/ix-es2015-cjs" />
|
4 | const client_common_1 = require("@neo-one/client-common");
|
5 | const utils_1 = require("@neo-one/utils");
|
6 | const asynciterablex_1 = require("@reactivex/ix-es2015-cjs/asynciterable/asynciterablex");
|
7 | const scan_1 = require("@reactivex/ix-es2015-cjs/asynciterable/pipe/scan");
|
8 | const operators_1 = require("rxjs/operators");
|
9 | const common_1 = require("./common");
|
10 | const ConsensusContext_1 = require("./ConsensusContext");
|
11 | const ConsensusQueue_1 = require("./ConsensusQueue");
|
12 | const handleConsensusPayload_1 = require("./handleConsensusPayload");
|
13 | const handlePersistBlock_1 = require("./handlePersistBlock");
|
14 | const handleTransactionReceived_1 = require("./handleTransactionReceived");
|
15 | const runConsensus_1 = require("./runConsensus");
|
16 | const MS_IN_SECOND = 1000;
|
17 | class Consensus {
|
18 | constructor({ options$, node, monitor, }) {
|
19 | this.mutableQueue = new ConsensusQueue_1.ConsensusQueue();
|
20 | this.options$ = options$.pipe(operators_1.map((options) => {
|
21 | const privateKey = client_common_1.common.stringToPrivateKey(options.privateKey);
|
22 | const publicKey = client_common_1.crypto.privateKeyToPublicKey(privateKey);
|
23 | const feeAddress = client_common_1.crypto.publicKeyToScriptHash(publicKey);
|
24 | return {
|
25 | privateKey,
|
26 | publicKey,
|
27 | feeAddress,
|
28 | privateNet: options.privateNet,
|
29 | };
|
30 | }));
|
31 | this.node = node;
|
32 | this.mutableConsensusContext = new ConsensusContext_1.ConsensusContext();
|
33 | this.monitor = monitor.at('node_consensus');
|
34 | }
|
35 | start$() {
|
36 | return this.options$.pipe(utils_1.mergeScanLatest(async (_, options) => {
|
37 | await this.pause();
|
38 | this.doStart(options);
|
39 | }), utils_1.finalize(async () => {
|
40 | await this.pause();
|
41 | }));
|
42 | }
|
43 | onPersistBlock() {
|
44 | this.mutableQueue.write({ type: 'handlePersistBlock' });
|
45 | }
|
46 | onConsensusPayloadReceived(payload) {
|
47 | this.mutableQueue.write({
|
48 | type: 'handleConsensusPayload',
|
49 | payload,
|
50 | });
|
51 | }
|
52 | onTransactionReceived(transaction) {
|
53 | this.mutableQueue.write({
|
54 | type: 'handleTransactionReceived',
|
55 | transaction,
|
56 | });
|
57 | }
|
58 | async runConsensusNow() {
|
59 | const options = await this.options$.pipe(operators_1.take(1)).toPromise();
|
60 | if (options.privateNet) {
|
61 | this.mutableQueue.write({ type: 'timer' });
|
62 | }
|
63 | else {
|
64 | throw new Error('Can only force consensus on a private network.');
|
65 | }
|
66 | }
|
67 | nowSeconds() {
|
68 | return this.mutableConsensusContext.nowSeconds();
|
69 | }
|
70 | async fastForwardOffset(seconds) {
|
71 | const options = await this.options$.pipe(operators_1.take(1)).toPromise();
|
72 | if (options.privateNet) {
|
73 | this.mutableConsensusContext.fastForwardOffset(seconds);
|
74 | }
|
75 | else {
|
76 | throw new Error('Can only fast forward on a private network.');
|
77 | }
|
78 | }
|
79 | async fastForwardToTime(seconds) {
|
80 | const options = await this.options$.pipe(operators_1.take(1)).toPromise();
|
81 | if (options.privateNet) {
|
82 | this.mutableConsensusContext.fastForwardToTime(seconds);
|
83 | }
|
84 | else {
|
85 | throw new Error('Can only fast forward on a private network.');
|
86 | }
|
87 | }
|
88 | async pause() {
|
89 | this.clearTimer();
|
90 | this.mutableQueue.done();
|
91 | this.mutableQueue = new ConsensusQueue_1.ConsensusQueue();
|
92 | if (this.mutableStartPromise !== undefined) {
|
93 | await this.mutableStartPromise;
|
94 | }
|
95 | }
|
96 | async reset() {
|
97 | this.mutableConsensusContext = new ConsensusContext_1.ConsensusContext();
|
98 | }
|
99 | async resume() {
|
100 | const options = await this.options$.pipe(operators_1.take(1)).toPromise();
|
101 | // tslint:disable-next-line no-floating-promises
|
102 | this.doStart(options);
|
103 | }
|
104 | doStart(options) {
|
105 | let completed = false;
|
106 | const mutableStartPromise = this.start(options).then(() => {
|
107 | completed = true;
|
108 | this.mutableStartPromise = undefined;
|
109 | });
|
110 | if (!completed) {
|
111 | this.mutableStartPromise = mutableStartPromise;
|
112 | }
|
113 | }
|
114 | async start(options) {
|
115 | this.monitor.log({
|
116 | name: 'neo_consensus_start',
|
117 | message: 'Consensus started.',
|
118 | level: 'verbose',
|
119 | });
|
120 | const initialResult = await common_1.initializeNewConsensus({
|
121 | blockchain: this.node.blockchain,
|
122 | publicKey: options.publicKey,
|
123 | consensusContext: this.mutableConsensusContext,
|
124 | });
|
125 | await asynciterablex_1.AsyncIterableX.from(this.mutableQueue)
|
126 | .pipe(scan_1.scan(async (context, event) => {
|
127 | let result;
|
128 | switch (event.type) {
|
129 | case 'handlePersistBlock':
|
130 | result = await handlePersistBlock_1.handlePersistBlock({
|
131 | blockchain: this.node.blockchain,
|
132 | publicKey: options.publicKey,
|
133 | consensusContext: this.mutableConsensusContext,
|
134 | });
|
135 | break;
|
136 | case 'handleConsensusPayload':
|
137 | result = await handleConsensusPayload_1.handleConsensusPayload({
|
138 | context,
|
139 | node: this.node,
|
140 | privateKey: options.privateKey,
|
141 | payload: event.payload,
|
142 | consensusContext: this.mutableConsensusContext,
|
143 | });
|
144 | break;
|
145 | case 'handleTransactionReceived':
|
146 | result = await handleTransactionReceived_1.handleTransactionReceived({
|
147 | context,
|
148 | node: this.node,
|
149 | privateKey: options.privateKey,
|
150 | transaction: event.transaction,
|
151 | consensusContext: this.mutableConsensusContext,
|
152 | });
|
153 | break;
|
154 | case 'timer':
|
155 | result = await runConsensus_1.runConsensus({
|
156 | context,
|
157 | node: this.node,
|
158 | options,
|
159 | consensusContext: this.mutableConsensusContext,
|
160 | });
|
161 | break;
|
162 | default:
|
163 | utils_1.utils.assertNever(event);
|
164 | throw new Error('For TS');
|
165 | }
|
166 | return this.handleResult(result);
|
167 | }, this.handleResult(initialResult)))
|
168 | .forEach(() => {
|
169 | // do nothing
|
170 | });
|
171 | this.monitor.log({
|
172 | name: 'neo_consensus_stop',
|
173 | message: 'Consensus stopped.',
|
174 | level: 'verbose',
|
175 | });
|
176 | }
|
177 | handleResult(result) {
|
178 | if (result.timerSeconds !== undefined) {
|
179 | this.handleTimer(result.timerSeconds);
|
180 | }
|
181 | return result.context;
|
182 | }
|
183 | handleTimer(mutableTimerSeconds) {
|
184 | this.clearTimer();
|
185 | this.mutableTimer = setTimeout(() => this.mutableQueue.write({ type: 'timer' }), mutableTimerSeconds * MS_IN_SECOND);
|
186 | }
|
187 | clearTimer() {
|
188 | if (this.mutableTimer !== undefined) {
|
189 | clearTimeout(this.mutableTimer);
|
190 | this.mutableTimer = undefined;
|
191 | }
|
192 | }
|
193 | }
|
194 | exports.Consensus = Consensus;
|
195 |
|
196 | //# sourceMappingURL=data:application/json;charset=utf8;base64,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QUcsU0FBUyxDQUFDO1NBQy9CO0lBQ0gsQ0FBQztDQUNGO0FBbk9ELDhCQW1PQyIsImZpbGUiOiJuZW8tb25lLW5vZGUtY29uc2Vuc3VzL3NyYy9Db25zZW5zdXMuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvLy8gPHJlZmVyZW5jZSB0eXBlcz1cIkByZWFjdGl2ZXgvaXgtZXMyMDE1LWNqc1wiIC8+XG5pbXBvcnQgeyBjb21tb24sIGNyeXB0bywgRUNQb2ludCwgUHJpdmF0ZUtleSwgVUludDE2MCB9IGZyb20gJ0BuZW8tb25lL2NsaWVudC1jb21tb24nO1xuaW1wb3J0IHsgTW9uaXRvciB9IGZyb20gJ0BuZW8tb25lL21vbml0b3InO1xuaW1wb3J0IHsgQ29uc2Vuc3VzUGF5bG9hZCwgTm9kZSwgVHJhbnNhY3Rpb24gfSBmcm9tICdAbmVvLW9uZS9ub2RlLWNvcmUnO1xuaW1wb3J0IHsgZmluYWxpemUsIG1lcmdlU2NhbkxhdGVzdCwgdXRpbHMgYXMgY29tbW9uVXRpbHMgfSBmcm9tICdAbmVvLW9uZS91dGlscyc7XG5pbXBvcnQgeyBBc3luY0l0ZXJhYmxlWCB9IGZyb20gJ0ByZWFjdGl2ZXgvaXgtZXMyMDE1LWNqcy9hc3luY2l0ZXJhYmxlL2FzeW5jaXRlcmFibGV4JztcbmltcG9ydCB7IHNjYW4gfSBmcm9tICdAcmVhY3RpdmV4L2l4LWVzMjAxNS1janMvYXN5bmNpdGVyYWJsZS9waXBlL3NjYW4nO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgbWFwLCB0YWtlIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgaW5pdGlhbGl6ZU5ld0NvbnNlbnN1cyB9IGZyb20gJy4vY29tbW9uJztcbmltcG9ydCB7IENvbnNlbnN1c0NvbnRleHQgfSBmcm9tICcuL0NvbnNlbnN1c0NvbnRleHQnO1xuaW1wb3J0IHsgQ29uc2Vuc3VzUXVldWUgfSBmcm9tICcuL0NvbnNlbnN1c1F1ZXVlJztcbmltcG9ydCB7IENvbnRleHQgfSBmcm9tICcuL2NvbnRleHQnO1xuaW1wb3J0IHsgaGFuZGxlQ29uc2Vuc3VzUGF5bG9hZCB9IGZyb20gJy4vaGFuZGxlQ29uc2Vuc3VzUGF5bG9hZCc7XG5pbXBvcnQgeyBoYW5kbGVQZXJzaXN0QmxvY2sgfSBmcm9tICcuL2hhbmRsZVBlcnNpc3RCbG9jayc7XG5pbXBvcnQgeyBoYW5kbGVUcmFuc2FjdGlvblJlY2VpdmVkIH0gZnJvbSAnLi9oYW5kbGVUcmFuc2FjdGlvblJlY2VpdmVkJztcbmltcG9ydCB7IHJ1bkNvbnNlbnN1cyB9IGZyb20gJy4vcnVuQ29uc2Vuc3VzJztcbmltcG9ydCB7IEV2ZW50LCBSZXN1bHQgfSBmcm9tICcuL3R5cGVzJztcblxuZXhwb3J0IGludGVyZmFjZSBPcHRpb25zIHtcbiAgcmVhZG9ubHkgcHJpdmF0ZUtleTogc3RyaW5nO1xuICByZWFkb25seSBwcml2YXRlTmV0OiBib29sZWFuO1xufVxuZXhwb3J0IGludGVyZmFjZSBJbnRlcm5hbE9wdGlvbnMge1xuICByZWFkb25seSBwcml2YXRlS2V5OiBQcml2YXRlS2V5O1xuICByZWFkb25seSBwdWJsaWNLZXk6IEVDUG9pbnQ7XG4gIHJlYWRvbmx5IGZlZUFkZHJlc3M6IFVJbnQxNjA7XG4gIHJlYWRvbmx5IHByaXZhdGVOZXQ6IGJvb2xlYW47XG59XG5cbmNvbnN0IE1TX0lOX1NFQ09ORCA9IDEwMDA7XG5cbmV4cG9ydCBjbGFzcyBDb25zZW5zdXMge1xuICBwcml2YXRlIG11dGFibGVRdWV1ZTogQ29uc2Vuc3VzUXVldWU7XG4gIHByaXZhdGUgbXV0YWJsZVRpbWVyOiBudW1iZXIgfCB1bmRlZmluZWQ7XG4gIHByaXZhdGUgcmVhZG9ubHkgb3B0aW9ucyQ6IE9ic2VydmFibGU8SW50ZXJuYWxPcHRpb25zPjtcbiAgcHJpdmF0ZSByZWFkb25seSBub2RlOiBOb2RlO1xuICBwcml2YXRlIG11dGFibGVDb25zZW5zdXNDb250ZXh0OiBDb25zZW5zdXNDb250ZXh0O1xuICBwcml2YXRlIHJlYWRvbmx5IG1vbml0b3I6IE1vbml0b3I7XG4gIHByaXZhdGUgbXV0YWJsZVN0YXJ0UHJvbWlzZTogUHJvbWlzZTx2b2lkPiB8IHVuZGVmaW5lZDtcblxuICBwdWJsaWMgY29uc3RydWN0b3Ioe1xuICAgIG9wdGlvbnMkLFxuICAgIG5vZGUsXG4gICAgbW9uaXRvcixcbiAgfToge1xuICAgIHJlYWRvbmx5IG9wdGlvbnMkOiBPYnNlcnZhYmxlPE9wdGlvbnM+O1xuICAgIHJlYWRvbmx5IG5vZGU6IE5vZGU7XG4gICAgcmVhZG9ubHkgbW9uaXRvcjogTW9uaXRvcjtcbiAgfSkge1xuICAgIHRoaXMubXV0YWJsZVF1ZXVlID0gbmV3IENvbnNlbnN1c1F1ZXVlKCk7XG4gICAgdGhpcy5vcHRpb25zJCA9IG9wdGlvbnMkLnBpcGUoXG4gICAgICBtYXAoKG9wdGlvbnMpID0+IHtcbiAgICAgICAgY29uc3QgcHJpdmF0ZUtleSA9IGNvbW1vbi5zdHJpbmdUb1ByaXZhdGVLZXkob3B0aW9ucy5wcml2YXRlS2V5KTtcbiAgICAgICAgY29uc3QgcHVibGljS2V5ID0gY3J5cHRvLnByaXZhdGVLZXlUb1B1YmxpY0tleShwcml2YXRlS2V5KTtcbiAgICAgICAgY29uc3QgZmVlQWRkcmVzcyA9IGNyeXB0by5wdWJsaWNLZXlUb1NjcmlwdEhhc2gocHVibGljS2V5KTtcblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHByaXZhdGVLZXksXG4gICAgICAgICAgcHVibGljS2V5LFxuICAgICAgICAgIGZlZUFkZHJlc3MsXG4gICAgICAgICAgcHJpdmF0ZU5ldDogb3B0aW9ucy5wcml2YXRlTmV0LFxuICAgICAgICB9O1xuICAgICAgfSksXG4gICAgKTtcblxuICAgIHRoaXMubm9kZSA9IG5vZGU7XG4gICAgdGhpcy5tdXRhYmxlQ29uc2Vuc3VzQ29udGV4dCA9IG5ldyBDb25zZW5zdXNDb250ZXh0KCk7XG4gICAgdGhpcy5tb25pdG9yID0gbW9uaXRvci5hdCgnbm9kZV9jb25zZW5zdXMnKTtcbiAgfVxuXG4gIHB1YmxpYyBzdGFydCQoKTogT2JzZXJ2YWJsZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMub3B0aW9ucyQucGlwZShcbiAgICAgIG1lcmdlU2NhbkxhdGVzdDxJbnRlcm5hbE9wdGlvbnMsIHZvaWQ+KGFzeW5jIChfLCBvcHRpb25zKSA9PiB7XG4gICAgICAgIGF3YWl0IHRoaXMucGF1c2UoKTtcbiAgICAgICAgdGhpcy5kb1N0YXJ0KG9wdGlvbnMpO1xuICAgICAgfSksXG4gICAgICBmaW5hbGl6ZShhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IHRoaXMucGF1c2UoKTtcbiAgICAgIH0pLFxuICAgICk7XG4gIH1cblxuICBwdWJsaWMgb25QZXJzaXN0QmxvY2soKTogdm9pZCB7XG4gICAgdGhpcy5tdXRhYmxlUXVldWUud3JpdGUoeyB0eXBlOiAnaGFuZGxlUGVyc2lzdEJsb2NrJyB9KTtcbiAgfVxuXG4gIHB1YmxpYyBvbkNvbnNlbnN1c1BheWxvYWRSZWNlaXZlZChwYXlsb2FkOiBDb25zZW5zdXNQYXlsb2FkKTogdm9pZCB7XG4gICAgdGhpcy5tdXRhYmxlUXVldWUud3JpdGUoe1xuICAgICAgdHlwZTogJ2hhbmRsZUNvbnNlbnN1c1BheWxvYWQnLFxuICAgICAgcGF5bG9hZCxcbiAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBvblRyYW5zYWN0aW9uUmVjZWl2ZWQodHJhbnNhY3Rpb246IFRyYW5zYWN0aW9uKTogdm9pZCB7XG4gICAgdGhpcy5tdXRhYmxlUXVldWUud3JpdGUoe1xuICAgICAgdHlwZTogJ2hhbmRsZVRyYW5zYWN0aW9uUmVjZWl2ZWQnLFxuICAgICAgdHJhbnNhY3Rpb24sXG4gICAgfSk7XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgcnVuQ29uc2Vuc3VzTm93KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IG9wdGlvbnMgPSBhd2FpdCB0aGlzLm9wdGlvbnMkLnBpcGUodGFrZSgxKSkudG9Qcm9taXNlKCk7XG4gICAgaWYgKG9wdGlvbnMucHJpdmF0ZU5ldCkge1xuICAgICAgdGhpcy5tdXRhYmxlUXVldWUud3JpdGUoeyB0eXBlOiAndGltZXInIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0NhbiBvbmx5IGZvcmNlIGNvbnNlbnN1cyBvbiBhIHByaXZhdGUgbmV0d29yay4nKTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgbm93U2Vjb25kcygpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLm11dGFibGVDb25zZW5zdXNDb250ZXh0Lm5vd1NlY29uZHMoKTtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBmYXN0Rm9yd2FyZE9mZnNldChzZWNvbmRzOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBvcHRpb25zID0gYXdhaXQgdGhpcy5vcHRpb25zJC5waXBlKHRha2UoMSkpLnRvUHJvbWlzZSgpO1xuICAgIGlmIChvcHRpb25zLnByaXZhdGVOZXQpIHtcbiAgICAgIHRoaXMubXV0YWJsZUNvbnNlbnN1c0NvbnRleHQuZmFzdEZvcndhcmRPZmZzZXQoc2Vjb25kcyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2FuIG9ubHkgZmFzdCBmb3J3YXJkIG9uIGEgcHJpdmF0ZSBuZXR3b3JrLicpO1xuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBmYXN0Rm9yd2FyZFRvVGltZShzZWNvbmRzOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBvcHRpb25zID0gYXdhaXQgdGhpcy5vcHRpb25zJC5waXBlKHRha2UoMSkpLnRvUHJvbWlzZSgpO1xuICAgIGlmIChvcHRpb25zLnByaXZhdGVOZXQpIHtcbiAgICAgIHRoaXMubXV0YWJsZUNvbnNlbnN1c0NvbnRleHQuZmFzdEZvcndhcmRUb1RpbWUoc2Vjb25kcyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2FuIG9ubHkgZmFzdCBmb3J3YXJkIG9uIGEgcHJpdmF0ZSBuZXR3b3JrLicpO1xuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBwYXVzZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmNsZWFyVGltZXIoKTtcbiAgICB0aGlzLm11dGFibGVRdWV1ZS5kb25lKCk7XG4gICAgdGhpcy5tdXRhYmxlUXVldWUgPSBuZXcgQ29uc2Vuc3VzUXVldWUoKTtcbiAgICBpZiAodGhpcy5tdXRhYmxlU3RhcnRQcm9taXNlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGF3YWl0IHRoaXMubXV0YWJsZVN0YXJ0UHJvbWlzZTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgcmVzZXQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5tdXRhYmxlQ29uc2Vuc3VzQ29udGV4dCA9IG5ldyBDb25zZW5zdXNDb250ZXh0KCk7XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgcmVzdW1lKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IG9wdGlvbnMgPSBhd2FpdCB0aGlzLm9wdGlvbnMkLnBpcGUodGFrZSgxKSkudG9Qcm9taXNlKCk7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lIG5vLWZsb2F0aW5nLXByb21pc2VzXG4gICAgdGhpcy5kb1N0YXJ0KG9wdGlvbnMpO1xuICB9XG5cbiAgcHJpdmF0ZSBkb1N0YXJ0KG9wdGlvbnM6IEludGVybmFsT3B0aW9ucyk6IHZvaWQge1xuICAgIGxldCBjb21wbGV0ZWQgPSBmYWxzZTtcbiAgICBjb25zdCBtdXRhYmxlU3RhcnRQcm9taXNlID0gdGhpcy5zdGFydChvcHRpb25zKS50aGVuKCgpID0+IHtcbiAgICAgIGNvbXBsZXRlZCA9IHRydWU7XG4gICAgICB0aGlzLm11dGFibGVTdGFydFByb21pc2UgPSB1bmRlZmluZWQ7XG4gICAgfSk7XG4gICAgaWYgKCFjb21wbGV0ZWQpIHtcbiAgICAgIHRoaXMubXV0YWJsZVN0YXJ0UHJvbWlzZSA9IG11dGFibGVTdGFydFByb21pc2U7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBzdGFydChvcHRpb25zOiBJbnRlcm5hbE9wdGlvbnMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLm1vbml0b3IubG9nKHtcbiAgICAgIG5hbWU6ICduZW9fY29uc2Vuc3VzX3N0YXJ0JyxcbiAgICAgIG1lc3NhZ2U6ICdDb25zZW5zdXMgc3RhcnRlZC4nLFxuICAgICAgbGV2ZWw6ICd2ZXJib3NlJyxcbiAgICB9KTtcblxuICAgIGNvbnN0IGluaXRpYWxSZXN1bHQgPSBhd2FpdCBpbml0aWFsaXplTmV3Q29uc2Vuc3VzKHtcbiAgICAgIGJsb2NrY2hhaW46IHRoaXMubm9kZS5ibG9ja2NoYWluLFxuICAgICAgcHVibGljS2V5OiBvcHRpb25zLnB1YmxpY0tleSxcbiAgICAgIGNvbnNlbnN1c0NvbnRleHQ6IHRoaXMubXV0YWJsZUNvbnNlbnN1c0NvbnRleHQsXG4gICAgfSk7XG5cbiAgICBhd2FpdCBBc3luY0l0ZXJhYmxlWC5mcm9tKHRoaXMubXV0YWJsZVF1ZXVlKVxuICAgICAgLnBpcGUoXG4gICAgICAgIHNjYW4oYXN5bmMgKGNvbnRleHQ6IENvbnRleHQsIGV2ZW50OiBFdmVudCkgPT4ge1xuICAgICAgICAgIGxldCByZXN1bHQ7XG4gICAgICAgICAgc3dpdGNoIChldmVudC50eXBlKSB7XG4gICAgICAgICAgICBjYXNlICdoYW5kbGVQZXJzaXN0QmxvY2snOlxuICAgICAgICAgICAgICByZXN1bHQgPSBhd2FpdCBoYW5kbGVQZXJzaXN0QmxvY2soe1xuICAgICAgICAgICAgICAgIGJsb2NrY2hhaW46IHRoaXMubm9kZS5ibG9ja2NoYWluLFxuICAgICAgICAgICAgICAgIHB1YmxpY0tleTogb3B0aW9ucy5wdWJsaWNLZXksXG4gICAgICAgICAgICAgICAgY29uc2Vuc3VzQ29udGV4dDogdGhpcy5tdXRhYmxlQ29uc2Vuc3VzQ29udGV4dCxcbiAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdoYW5kbGVDb25zZW5zdXNQYXlsb2FkJzpcbiAgICAgICAgICAgICAgcmVzdWx0ID0gYXdhaXQgaGFuZGxlQ29uc2Vuc3VzUGF5bG9hZCh7XG4gICAgICAgICAgICAgICAgY29udGV4dCxcbiAgICAgICAgICAgICAgICBub2RlOiB0aGlzLm5vZGUsXG4gICAgICAgICAgICAgICAgcHJpdmF0ZUtleTogb3B0aW9ucy5wcml2YXRlS2V5LFxuICAgICAgICAgICAgICAgIHBheWxvYWQ6IGV2ZW50LnBheWxvYWQsXG4gICAgICAgICAgICAgICAgY29uc2Vuc3VzQ29udGV4dDogdGhpcy5tdXRhYmxlQ29uc2Vuc3VzQ29udGV4dCxcbiAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdoYW5kbGVUcmFuc2FjdGlvblJlY2VpdmVkJzpcbiAgICAgICAgICAgICAgcmVzdWx0ID0gYXdhaXQgaGFuZGxlVHJhbnNhY3Rpb25SZWNlaXZlZCh7XG4gICAgICAgICAgICAgICAgY29udGV4dCxcbiAgICAgICAgICAgICAgICBub2RlOiB0aGlzLm5vZGUsXG4gICAgICAgICAgICAgICAgcHJpdmF0ZUtleTogb3B0aW9ucy5wcml2YXRlS2V5LFxuICAgICAgICAgICAgICAgIHRyYW5zYWN0aW9uOiBldmVudC50cmFuc2FjdGlvbixcbiAgICAgICAgICAgICAgICBjb25zZW5zdXNDb250ZXh0OiB0aGlzLm11dGFibGVDb25zZW5zdXNDb250ZXh0LFxuICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ3RpbWVyJzpcbiAgICAgICAgICAgICAgcmVzdWx0ID0gYXdhaXQgcnVuQ29uc2Vuc3VzKHtcbiAgICAgICAgICAgICAgICBjb250ZXh0LFxuICAgICAgICAgICAgICAgIG5vZGU6IHRoaXMubm9kZSxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIGNvbnNlbnN1c0NvbnRleHQ6IHRoaXMubXV0YWJsZUNvbnNlbnN1c0NvbnRleHQsXG4gICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgY29tbW9uVXRpbHMuYXNzZXJ0TmV2ZXIoZXZlbnQpO1xuICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZvciBUUycpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiB0aGlzLmhhbmRsZVJlc3VsdChyZXN1bHQpO1xuICAgICAgICB9LCB0aGlzLmhhbmRsZVJlc3VsdChpbml0aWFsUmVzdWx0KSksXG4gICAgICApXG4gICAgICAuZm9yRWFjaCgoKSA9PiB7XG4gICAgICAgIC8vIGRvIG5vdGhpbmdcbiAgICAgIH0pO1xuXG4gICAgdGhpcy5tb25pdG9yLmxvZyh7XG4gICAgICBuYW1lOiAnbmVvX2NvbnNlbnN1c19zdG9wJyxcbiAgICAgIG1lc3NhZ2U6ICdDb25zZW5zdXMgc3RvcHBlZC4nLFxuICAgICAgbGV2ZWw6ICd2ZXJib3NlJyxcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgaGFuZGxlUmVzdWx0KHJlc3VsdDogUmVzdWx0PENvbnRleHQ+KTogQ29udGV4dCB7XG4gICAgaWYgKHJlc3VsdC50aW1lclNlY29uZHMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhpcy5oYW5kbGVUaW1lcihyZXN1bHQudGltZXJTZWNvbmRzKTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0LmNvbnRleHQ7XG4gIH1cblxuICBwcml2YXRlIGhhbmRsZVRpbWVyKG11dGFibGVUaW1lclNlY29uZHM6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMuY2xlYXJUaW1lcigpO1xuICAgIHRoaXMubXV0YWJsZVRpbWVyID0gc2V0VGltZW91dChcbiAgICAgICgpID0+IHRoaXMubXV0YWJsZVF1ZXVlLndyaXRlKHsgdHlwZTogJ3RpbWVyJyB9KSxcbiAgICAgIG11dGFibGVUaW1lclNlY29uZHMgKiBNU19JTl9TRUNPTkQsXG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmUgbm8tYW55XG4gICAgKSBhcyBhbnk7XG4gIH1cblxuICBwcml2YXRlIGNsZWFyVGltZXIoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMubXV0YWJsZVRpbWVyICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aGlzLm11dGFibGVUaW1lcik7XG4gICAgICB0aGlzLm11dGFibGVUaW1lciA9IHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cbn1cbiJdfQ==
|