@my-packages-changeit/cardano-scanner 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,51 @@
1
+ import { ChainSynchronizationClient } from '@cardano-ogmios/client/dist/ChainSynchronization';
2
+ import { Point, Transaction } from '@cardano-ogmios/schema';
3
+ import { AbstractLogger } from '@rosen-bridge/abstract-logger';
4
+ import { WebSocketScanner } from '@rosen-bridge/abstract-scanner';
5
+ import { CardanoOgmiosConfig } from '../interfaces';
6
+ declare class CardanoOgmiosScanner extends WebSocketScanner<Transaction> {
7
+ client: ChainSynchronizationClient;
8
+ initPoint: Point;
9
+ host: string;
10
+ port: number;
11
+ useTls: boolean;
12
+ private connected;
13
+ private stopped;
14
+ private reconnectionConfig;
15
+ constructor(config: CardanoOgmiosConfig, logger?: AbstractLogger);
16
+ /**
17
+ * process block when a block forked library call this function
18
+ * @param response: forked block
19
+ * @param requestNext: a function to tell node to send next block
20
+ */
21
+ private rollBackward;
22
+ /**
23
+ * Step forward when new block arrived ogmios-client call this function
24
+ * @param response: new block
25
+ * @param requestNext: tell library that this block proceeds. pass next block when available
26
+ */
27
+ private rollForward;
28
+ /**
29
+ * find intersect between stored blocks and blockchain.
30
+ * @param context: blockchain context
31
+ */
32
+ private findIntersection;
33
+ /**
34
+ * Handles ogmios connection closure
35
+ */
36
+ private connectionCloseHandler;
37
+ /**
38
+ * start scanner. first find fork point. fork all blocks and request updates from node.
39
+ */
40
+ start: () => Promise<void>;
41
+ /**
42
+ * stop ws connection to node.
43
+ */
44
+ stop: () => Promise<void>;
45
+ /**
46
+ * @returns The connection status of ogmios scanner
47
+ */
48
+ getConnectionStatus: () => boolean;
49
+ }
50
+ export { CardanoOgmiosScanner };
51
+ //# sourceMappingURL=ogmios.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ogmios.d.ts","sourceRoot":"","sources":["../../lib/scanner/ogmios.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,0BAA0B,EAE3B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAGL,KAAK,EAGL,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAEL,gBAAgB,EACjB,MAAM,gCAAgC,CAAC;AAQxC,OAAO,EAAE,mBAAmB,EAA4B,MAAM,eAAe,CAAC;AAY9E,cAAM,oBAAqB,SAAQ,gBAAgB,CAAC,WAAW,CAAC;IAC9D,MAAM,EAAE,0BAA0B,CAAC;IACnC,SAAS,EAAE,KAAK,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,kBAAkB,CAA2B;gBAEzC,MAAM,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,cAAc;IAoBhE;;;;OAIG;IACH,OAAO,CAAC,YAAY,CAsBlB;IAEF;;;;OAIG;IACH,OAAO,CAAC,WAAW,CAuBjB;IAEF;;;OAGG;IACH,OAAO,CAAC,gBAAgB,CAkCtB;IAEF;;OAEG;IACH,OAAO,CAAC,sBAAsB,CA6B5B;IAEF;;OAEG;IACH,KAAK,QAAa,OAAO,CAAC,IAAI,CAAC,CAwB7B;IAEF;;OAEG;IACH,IAAI,QAAa,OAAO,CAAC,IAAI,CAAC,CAG5B;IAEF;;OAEG;IACH,mBAAmB,gBAAwB;CAC5C;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
@@ -0,0 +1,173 @@
1
+ import { createChainSynchronizationClient, } from '@cardano-ogmios/client';
2
+ import { createInteractionContext } from '@cardano-ogmios/client';
3
+ import { findIntersection, } from '@cardano-ogmios/client/dist/ChainSynchronization';
4
+ import { ExponentialBackoff, handleWhen, retry } from 'cockatiel';
5
+ import { BlockDbAction, WebSocketScanner, } from '@rosen-bridge/abstract-scanner';
6
+ import { RECONNECTION_INITIAL_DELAY, RECONNECTION_MAX_DELAY, SLOT_SHELLY_NUMBER, RECONNECTION_MAX_ATTEMPTS, } from '../constants';
7
+ class CardanoOgmiosScanner extends WebSocketScanner {
8
+ client;
9
+ initPoint;
10
+ host;
11
+ port;
12
+ useTls;
13
+ connected;
14
+ stopped;
15
+ reconnectionConfig;
16
+ constructor(config, logger) {
17
+ super('cardano', logger, config.maxTryBlock, config.suffix);
18
+ this.action = new BlockDbAction(config.dataSource, this.name(), logger);
19
+ this.host = config.nodeHostOrIp;
20
+ this.port = config.nodePort;
21
+ this.useTls = config.useTls ?? false;
22
+ this.connected = false;
23
+ this.initPoint = {
24
+ id: config.initialHash,
25
+ slot: config.initialSlot,
26
+ };
27
+ this.reconnectionConfig = {
28
+ initialDelay: config.reconnectionConfig?.initialDelay ?? RECONNECTION_INITIAL_DELAY,
29
+ maxDelay: config.reconnectionConfig?.maxDelay ?? RECONNECTION_MAX_DELAY,
30
+ maxAttempts: config.reconnectionConfig?.maxAttempts ?? RECONNECTION_MAX_ATTEMPTS,
31
+ };
32
+ }
33
+ /**
34
+ * process block when a block forked library call this function
35
+ * @param response: forked block
36
+ * @param requestNext: a function to tell node to send next block
37
+ */
38
+ rollBackward = async (response, requestNext) => {
39
+ const hash = response.point.id;
40
+ const savedBlock = await this.action.getBlockWithHash(hash);
41
+ this.logger.debug(`Rolling backward to height ${savedBlock?.height} in scanner ${this.name()}`);
42
+ if (savedBlock) {
43
+ const block = {
44
+ hash: savedBlock.hash,
45
+ height: savedBlock.height,
46
+ parentHash: savedBlock.parentHash,
47
+ extra: savedBlock.extra,
48
+ timestamp: savedBlock.timestamp,
49
+ };
50
+ await this.stepBackward(block);
51
+ }
52
+ requestNext();
53
+ };
54
+ /**
55
+ * Step forward when new block arrived ogmios-client call this function
56
+ * @param response: new block
57
+ * @param requestNext: tell library that this block proceeds. pass next block when available
58
+ */
59
+ rollForward = async (response, requestNext) => {
60
+ if (response.block.type === 'praos') {
61
+ const praosBlock = response.block;
62
+ this.logger.debug(`Queueing new block at height ${praosBlock.height} in scanner ${this.name()}`);
63
+ const block = {
64
+ hash: praosBlock.id,
65
+ height: praosBlock.height,
66
+ parentHash: praosBlock.ancestor,
67
+ extra: `${praosBlock.slot}`,
68
+ // Caution: In case of a hard fork and change in slot duration, this must change!
69
+ timestamp: praosBlock.slot + SLOT_SHELLY_NUMBER,
70
+ };
71
+ if (praosBlock.transactions)
72
+ await this.stepForward(block, praosBlock.transactions);
73
+ }
74
+ requestNext();
75
+ };
76
+ /**
77
+ * find intersect between stored blocks and blockchain.
78
+ * @param context: blockchain context
79
+ */
80
+ findIntersection = async (context) => {
81
+ let count = 1;
82
+ let skip = 0;
83
+ while (count !== 0) {
84
+ try {
85
+ const release = await this.mutex.acquire();
86
+ const blocks = await this.action.getLastSavedBlocks(skip, count);
87
+ release();
88
+ if (blocks.length === 0)
89
+ count = 0;
90
+ const points = blocks.length > 0
91
+ ? blocks.map((item) => ({
92
+ slot: item.extra ? parseInt(item.extra) : 0,
93
+ id: item.hash,
94
+ }))
95
+ : [this.initPoint];
96
+ const intersect = await findIntersection(context, points);
97
+ const intersectPoint = intersect.intersection;
98
+ let height = 0;
99
+ if (blocks.length) {
100
+ const foundedBlock = blocks.find((item) => (item.hash = intersectPoint.id));
101
+ if (foundedBlock) {
102
+ height = foundedBlock.height;
103
+ }
104
+ }
105
+ return { point: intersect.intersection, height: height };
106
+ }
107
+ catch {
108
+ skip += count;
109
+ count *= 2;
110
+ }
111
+ }
112
+ return undefined;
113
+ };
114
+ /**
115
+ * Handles ogmios connection closure
116
+ */
117
+ connectionCloseHandler = async () => {
118
+ this.connected = false;
119
+ this.logger.warn('Ogmios connection closed');
120
+ if (this.stopped)
121
+ return;
122
+ const retryPolicy = retry(handleWhen((error) => {
123
+ this.logger.warn(`An error occurred while reconnecting to ogmios client: ${error}`);
124
+ return true;
125
+ }), {
126
+ maxAttempts: this.reconnectionConfig.maxAttempts,
127
+ backoff: new ExponentialBackoff({
128
+ maxDelay: this.reconnectionConfig.maxDelay,
129
+ initialDelay: this.reconnectionConfig.initialDelay,
130
+ }),
131
+ });
132
+ let trial = 0;
133
+ retryPolicy.execute(async () => {
134
+ trial++;
135
+ this.logger.debug(`Retrying to connect to ogmios client in trial step #${trial}`);
136
+ return await this.start();
137
+ });
138
+ };
139
+ /**
140
+ * start scanner. first find fork point. fork all blocks and request updates from node.
141
+ */
142
+ start = async () => {
143
+ this.stopped = false;
144
+ const context = await createInteractionContext((err) => this.logger.error(`${err}`), this.connectionCloseHandler, { connection: { port: this.port, host: this.host, tls: this.useTls } });
145
+ const intersect = await this.findIntersection(context);
146
+ if (intersect) {
147
+ // find intersect then start from that point
148
+ this.client = await createChainSynchronizationClient(context, {
149
+ rollBackward: this.rollBackward,
150
+ rollForward: this.rollForward,
151
+ }, { sequential: true });
152
+ this.connected = true;
153
+ await this.forkBlock(intersect.height + 1);
154
+ await this.client.resume([intersect.point], 2);
155
+ }
156
+ else {
157
+ throw Error('Can not start scanner. initial block is invalid');
158
+ }
159
+ };
160
+ /**
161
+ * stop ws connection to node.
162
+ */
163
+ stop = async () => {
164
+ this.stopped = true;
165
+ await this.client.shutdown();
166
+ };
167
+ /**
168
+ * @returns The connection status of ogmios scanner
169
+ */
170
+ getConnectionStatus = () => this.connected;
171
+ }
172
+ export { CardanoOgmiosScanner };
173
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2dtaW9zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vbGliL3NjYW5uZXIvb2dtaW9zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCxnQ0FBZ0MsR0FFakMsTUFBTSx3QkFBd0IsQ0FBQztBQUNoQyxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNsRSxPQUFPLEVBRUwsZ0JBQWdCLEdBQ2pCLE1BQU0sa0RBQWtELENBQUM7QUFTMUQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFHbEUsT0FBTyxFQUNMLGFBQWEsRUFDYixnQkFBZ0IsR0FDakIsTUFBTSxnQ0FBZ0MsQ0FBQztBQUV4QyxPQUFPLEVBQ0wsMEJBQTBCLEVBQzFCLHNCQUFzQixFQUN0QixrQkFBa0IsRUFDbEIseUJBQXlCLEdBQzFCLE1BQU0sY0FBYyxDQUFDO0FBYXRCLE1BQU0sb0JBQXFCLFNBQVEsZ0JBQTZCO0lBQzlELE1BQU0sQ0FBNkI7SUFDbkMsU0FBUyxDQUFRO0lBQ2pCLElBQUksQ0FBUztJQUNiLElBQUksQ0FBUztJQUNiLE1BQU0sQ0FBVTtJQUNSLFNBQVMsQ0FBVTtJQUNuQixPQUFPLENBQVU7SUFDakIsa0JBQWtCLENBQTJCO0lBRXJELFlBQVksTUFBMkIsRUFBRSxNQUF1QjtRQUM5RCxLQUFLLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQUMsV0FBVyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM1RCxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksYUFBYSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3hFLElBQUksQ0FBQyxJQUFJLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQztRQUNoQyxJQUFJLENBQUMsSUFBSSxHQUFHLE1BQU0sQ0FB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QUM7WUFDTixNQUFNLEtBQUssQ0FBQyxpREFBaUQsQ0FBQyxDQUFDO1FBQ2pFLENBQUM7SUFDSCxDQUFDLENBQUM7SUFFRjs7T0FFRztJQUNILElBQUksR0FBRyxLQUFLLElBQW1CLEVBQUU7UUFDL0IsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7UUFDcEIsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQy9CLENBQUMsQ0FBQztJQUVGOztPQUVHO0lBQ0gsbUJBQW1CLEdBQUcsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQztDQUM1QztBQUVELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgY3JlYXRlQ2hhaW5TeW5jaHJvbml6YXRpb25DbGllbnQsXG4gIEludGVyYWN0aW9uQ29udGV4dCxcbn0gZnJvbSAnQGNhcmRhbm8tb2dtaW9zL2NsaWVudCc7XG5pbXBvcnQgeyBjcmVhdGVJbnRlcmFjdGlvbkNvbnRleHQgfSBmcm9tICdAY2FyZGFuby1vZ21pb3MvY2xpZW50JztcbmltcG9ydCB7XG4gIENoYWluU3luY2hyb25pemF0aW9uQ2xpZW50LFxuICBmaW5kSW50ZXJzZWN0aW9uLFxufSBmcm9tICdAY2FyZGFuby1vZ21pb3MvY2xpZW50L2Rpc3QvQ2hhaW5TeW5jaHJvbml6YXRpb24nO1xuaW1wb3J0IHtcbiAgQmxvY2ssXG4gIEJsb2NrUHJhb3MsXG4gIFBvaW50LFxuICBQb2ludE9yT3JpZ2luLFxuICBUaXBPck9yaWdpbixcbiAgVHJhbnNhY3Rpb24sXG59IGZyb20gJ0BjYXJkYW5vLW9nbWlvcy9zY2hlbWEnO1xuaW1wb3J0IHsgRXhwb25lbnRpYWxCYWNrb2ZmLCBoYW5kbGVXaGVuLCByZXRyeSB9IGZyb20gJ2NvY2thdGllbCc7XG5cbmltcG9ydCB7IEFic3RyYWN0TG9nZ2VyIH0gZnJvbSAnQHJvc2VuLWJyaWRnZS9hYnN0cmFjdC1sb2dnZXInO1xuaW1wb3J0IHtcbiAgQmxvY2tEYkFjdGlvbixcbiAgV2ViU29ja2V0U2Nhbm5lcixcbn0gZnJvbSAnQHJvc2VuLWJyaWRnZS9hYnN0cmFjdC1zY2FubmVyJztcblxuaW1wb3J0IHtcbiAgUkVDT05ORUNUSU9OX0lOSVRJQUxfREVMQVksXG4gIFJFQ09OTkVDVElPTl9NQVhfREVMQVksXG4gIFNMT1RfU0hFTExZX05VTUJFUixcbiAgUkVDT05ORUNUSU9OX01BWF9BVFRFTVBUUyxcbn0gZnJvbSAnLi4vY29uc3RhbnRzJztcbmltcG9ydCB7IENhcmRhbm9PZ21pb3NDb25maWcsIE9nbWlvc1JlY29ubmVjdGlvbkNvbmZpZyB9IGZyb20gJy4uL2ludGVyZmFjZXMnO1xuXG5pbnRlcmZhY2UgQmFja3dhcmRSZXNwb25zZSB7XG4gIHBvaW50OiBQb2ludE9yT3JpZ2luO1xuICB0aXA6IFRpcE9yT3JpZ2luO1xufVxuXG5pbnRlcmZhY2UgRm9yd2FyZFJlc3BvbnNlIHtcbiAgYmxvY2s6IEJsb2NrO1xuICB0aXA6IFRpcE9yT3JpZ2luO1xufVxuXG5jbGFzcyBDYXJkYW5vT2dtaW9zU2Nhbm5lciBleHRlbmRzIFdlYlNvY2tldFNjYW5uZXI8VHJhbnNhY3Rpb24+IHtcbiAgY2xpZW50OiBDaGFpblN5bmNocm9uaXphdGlvbkNsaWVudDtcbiAgaW5pdFBvaW50OiBQb2ludDtcbiAgaG9zdDogc3RyaW5nO1xuICBwb3J0OiBudW1iZXI7XG4gIHVzZVRsczogYm9vbGVhbjtcbiAgcHJpdmF0ZSBjb25uZWN0ZWQ6IGJvb2xlYW47XG4gIHByaXZhdGUgc3RvcHBlZDogYm9vbGVhbjtcbiAgcHJpdmF0ZSByZWNvbm5lY3Rpb25Db25maWc6IE9nbWlvc1JlY29ubmVjdGlvbkNvbmZpZztcblxuICBjb25zdHJ1Y3Rvcihjb25maWc6IENhcmRhbm9PZ21pb3NDb25maWcsIGxvZ2dlcj86IEFic3RyYWN0TG9nZ2VyKSB7XG4gICAgc3VwZXIoJ2NhcmRhbm8nLCBsb2dnZXIsIGNvbmZpZy5tYXhUcnlCbG9jaywgY29uZmlnLnN1ZmZpeCk7XG4gICAgdGhpcy5hY3Rpb24gPSBuZXcgQmxvY2tEYkFjdGlvbihjb25maWcuZGF0YVNvdXJjZSwgdGhpcy5uYW1lKCksIGxvZ2dlcik7XG4gICAgdGhpcy5ob3N0ID0gY29uZmlnLm5vZGVIb3N0T3JJcDtcbiAgICB0aGlzLnBvcnQgPSBjb25maWcubm9kZVBvcnQ7XG4gICAgdGhpcy51c2VUbHMgPSBjb25maWcudXNlVGxzID8/IGZhbHNlO1xuICAgIHRoaXMuY29ubmVjdGVkID0gZmFsc2U7XG4gICAgdGhpcy5pbml0UG9pbnQgPSB7XG4gICAgICBpZDogY29uZmlnLmluaXRpYWxIYXNoLFxuICAgICAgc2xvdDogY29uZmlnLmluaXRpYWxTbG90LFxuICAgIH07XG4gICAgdGhpcy5yZWNvbm5lY3Rpb25Db25maWcgPSB7XG4gICAgICBpbml0aWFsRGVsYXk6XG4gICAgICAgIGNvbmZpZy5yZWNvbm5lY3Rpb25Db25maWc/LmluaXRpYWxEZWxheSA/PyBSRUNPTk5FQ1RJT05fSU5JVElBTF9ERUxBWSxcbiAgICAgIG1heERlbGF5OiBjb25maWcucmVjb25uZWN0aW9uQ29uZmlnPy5tYXhEZWxheSA/PyBSRUNPTk5FQ1RJT05fTUFYX0RFTEFZLFxuICAgICAgbWF4QXR0ZW1wdHM6XG4gICAgICAgIGNvbmZpZy5yZWNvbm5lY3Rpb25Db25maWc/Lm1heEF0dGVtcHRzID8/IFJFQ09OTkVDVElPTl9NQVhfQVRURU1QVFMsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBwcm9jZXNzIGJsb2NrIHdoZW4gYSBibG9jayBmb3JrZWQgbGlicmFyeSBjYWxsIHRoaXMgZnVuY3Rpb25cbiAgICogQHBhcmFtIHJlc3BvbnNlOiBmb3JrZWQgYmxvY2tcbiAgICogQHBhcmFtIHJlcXVlc3ROZXh0OiBhIGZ1bmN0aW9uIHRvIHRlbGwgbm9kZSB0byBzZW5kIG5leHQgYmxvY2tcbiAgICovXG4gIHByaXZhdGUgcm9sbEJhY2t3YXJkID0gYXN5bmMgKFxuICAgIHJlc3BvbnNlOiBCYWNrd2FyZFJlc3BvbnNlLFxuICAgIHJlcXVlc3ROZXh0OiAoKSA9PiB2b2lkLFxuICApID0+IHtcbiAgICBjb25zdCBoYXNoID0gKHJlc3BvbnNlLnBvaW50IGFzIFBvaW50KS5pZDtcbiAgICBjb25zdCBzYXZlZEJsb2NrID0gYXdhaXQgdGhpcy5hY3Rpb24uZ2V0QmxvY2tXaXRoSGFzaChoYXNoKTtcbiAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhcbiAgICAgIGBSb2xsaW5nIGJhY2t3YXJkIHRvIGhlaWdodCAke1xuICAgICAgICBzYXZlZEJsb2NrPy5oZWlnaHRcbiAgICAgIH0gaW4gc2Nhbm5lciAke3RoaXMubmFtZSgpfWAsXG4gICAgKTtcbiAgICBpZiAoc2F2ZWRCbG9jaykge1xuICAgICAgY29uc3QgYmxvY2sgPSB7XG4gICAgICAgIGhhc2g6IHNhdmVkQmxvY2suaGFzaCxcbiAgICAgICAgaGVpZ2h0OiBzYXZlZEJsb2NrLmhlaWdodCxcbiAgICAgICAgcGFyZW50SGFzaDogc2F2ZWRCbG9jay5wYXJlbnRIYXNoLFxuICAgICAgICBleHRyYTogc2F2ZWRCbG9jay5leHRyYSxcbiAgICAgICAgdGltZXN0YW1wOiBzYXZlZEJsb2NrLnRpbWVzdGFtcCxcbiAgICAgIH07XG4gICAgICBhd2FpdCB0aGlzLnN0ZXBCYWNrd2FyZChibG9jayk7XG4gICAgfVxuICAgIHJlcXVlc3ROZXh0KCk7XG4gIH07XG5cbiAgLyoqXG4gICAqIFN0ZXAgZm9yd2FyZCB3aGVuIG5ldyBibG9jayBhcnJpdmVkIG9nbWlvcy1jbGllbnQgY2FsbCB0aGlzIGZ1bmN0aW9uXG4gICAqIEBwYXJhbSByZXNwb25zZTogbmV3IGJsb2NrXG4gICAqIEBwYXJhbSByZXF1ZXN0TmV4dDogdGVsbCBsaWJyYXJ5IHRoYXQgdGhpcyBibG9jayBwcm9jZWVkcy4gcGFzcyBuZXh0IGJsb2NrIHdoZW4gYXZhaWxhYmxlXG4gICAqL1xuICBwcml2YXRlIHJvbGxGb3J3YXJkID0gYXN5bmMgKFxuICAgIHJlc3BvbnNlOiBGb3J3YXJkUmVzcG9uc2UsXG4gICAgcmVxdWVzdE5leHQ6ICgpID0+IHZvaWQsXG4gICkgPT4ge1xuICAgIGlmIChyZXNwb25zZS5ibG9jay50eXBlID09PSAncHJhb3MnKSB7XG4gICAgICBjb25zdCBwcmFvc0Jsb2NrID0gcmVzcG9uc2UuYmxvY2sgYXMgQmxvY2tQcmFvcztcbiAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKFxuICAgICAgICBgUXVldWVpbmcgbmV3IGJsb2NrIGF0IGhlaWdodCAke1xuICAgICAgICAgIHByYW9zQmxvY2suaGVpZ2h0XG4gICAgICAgIH0gaW4gc2Nhbm5lciAke3RoaXMubmFtZSgpfWAsXG4gICAgICApO1xuICAgICAgY29uc3QgYmxvY2sgPSB7XG4gICAgICAgIGhhc2g6IHByYW9zQmxvY2suaWQsXG4gICAgICAgIGhlaWdodDogcHJhb3NCbG9jay5oZWlnaHQsXG4gICAgICAgIHBhcmVudEhhc2g6IHByYW9zQmxvY2suYW5jZXN0b3IsXG4gICAgICAgIGV4dHJhOiBgJHtwcmFvc0Jsb2NrLnNsb3R9YCxcbiAgICAgICAgLy8gQ2F1dGlvbjogSW4gY2FzZSBvZiBhIGhhcmQgZm9yayBhbmQgY2hhbmdlIGluIHNsb3QgZHVyYXRpb24sIHRoaXMgbXVzdCBjaGFuZ2UhXG4gICAgICAgIHRpbWVzdGFtcDogcHJhb3NCbG9jay5zbG90ICsgU0xPVF9TSEVMTFlfTlVNQkVSLFxuICAgICAgfTtcbiAgICAgIGlmIChwcmFvc0Jsb2NrLnRyYW5zYWN0aW9ucylcbiAgICAgICAgYXdhaXQgdGhpcy5zdGVwRm9yd2FyZChibG9jaywgcHJhb3NCbG9jay50cmFuc2FjdGlvbnMpO1xuICAgIH1cbiAgICByZXF1ZXN0TmV4dCgpO1xuICB9O1xuXG4gIC8qKlxuICAgKiBmaW5kIGludGVyc2VjdCBiZXR3ZWVuIHN0b3JlZCBibG9ja3MgYW5kIGJsb2NrY2hhaW4uXG4gICAqIEBwYXJhbSBjb250ZXh0OiBibG9ja2NoYWluIGNvbnRleHRcbiAgICovXG4gIHByaXZhdGUgZmluZEludGVyc2VjdGlvbiA9IGFzeW5jIChjb250ZXh0OiBJbnRlcmFjdGlvbkNvbnRleHQpID0+IHtcbiAgICBsZXQgY291bnQgPSAxO1xuICAgIGxldCBza2lwID0gMDtcbiAgICB3aGlsZSAoY291bnQgIT09IDApIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlbGVhc2UgPSBhd2FpdCB0aGlzLm11dGV4LmFjcXVpcmUoKTtcbiAgICAgICAgY29uc3QgYmxvY2tzID0gYXdhaXQgdGhpcy5hY3Rpb24uZ2V0TGFzdFNhdmVkQmxvY2tzKHNraXAsIGNvdW50KTtcbiAgICAgICAgcmVsZWFzZSgpO1xuICAgICAgICBpZiAoYmxvY2tzLmxlbmd0aCA9PT0gMCkgY291bnQgPSAwO1xuICAgICAgICBjb25zdCBwb2ludHMgPVxuICAgICAgICAgIGJsb2Nrcy5sZW5ndGggPiAwXG4gICAgICAgICAgICA/IGJsb2Nrcy5tYXAoKGl0ZW0pID0+ICh7XG4gICAgICAgICAgICAgICAgc2xvdDogaXRlbS5leHRyYSA/IHBhcnNlSW50KGl0ZW0uZXh0cmEpIDogMCxcbiAgICAgICAgICAgICAgICBpZDogaXRlbS5oYXNoLFxuICAgICAgICAgICAgICB9KSlcbiAgICAgICAgICAgIDogW3RoaXMuaW5pdFBvaW50XTtcbiAgICAgICAgY29uc3QgaW50ZXJzZWN0ID0gYXdhaXQgZmluZEludGVyc2VjdGlvbihjb250ZXh0LCBwb2ludHMpO1xuICAgICAgICBjb25zdCBpbnRlcnNlY3RQb2ludCA9IGludGVyc2VjdC5pbnRlcnNlY3Rpb24gYXMgUG9pbnQ7XG4gICAgICAgIGxldCBoZWlnaHQgPSAwO1xuICAgICAgICBpZiAoYmxvY2tzLmxlbmd0aCkge1xuICAgICAgICAgIGNvbnN0IGZvdW5kZWRCbG9jayA9IGJsb2Nrcy5maW5kKFxuICAgICAgICAgICAgKGl0ZW0pID0+IChpdGVtLmhhc2ggPSBpbnRlcnNlY3RQb2ludC5pZCksXG4gICAgICAgICAgKTtcbiAgICAgICAgICBpZiAoZm91bmRlZEJsb2NrKSB7XG4gICAgICAgICAgICBoZWlnaHQgPSBmb3VuZGVkQmxvY2suaGVpZ2h0O1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4geyBwb2ludDogaW50ZXJzZWN0LmludGVyc2VjdGlvbiBhcyBQb2ludCwgaGVpZ2h0OiBoZWlnaHQgfTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICBza2lwICs9IGNvdW50O1xuICAgICAgICBjb3VudCAqPSAyO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9O1xuXG4gIC8qKlxuICAgKiBIYW5kbGVzIG9nbWlvcyBjb25uZWN0aW9uIGNsb3N1cmVcbiAgICovXG4gIHByaXZhdGUgY29ubmVjdGlvbkNsb3NlSGFuZGxlciA9IGFzeW5jICgpID0+IHtcbiAgICB0aGlzLmNvbm5lY3RlZCA9IGZhbHNlO1xuICAgIHRoaXMubG9nZ2VyLndhcm4oJ09nbWlvcyBjb25uZWN0aW9uIGNsb3NlZCcpO1xuICAgIGlmICh0aGlzLnN0b3BwZWQpIHJldHVybjtcblxuICAgIGNvbnN0IHJldHJ5UG9saWN5ID0gcmV0cnkoXG4gICAgICBoYW5kbGVXaGVuKChlcnJvcjogRXJyb3IpID0+IHtcbiAgICAgICAgdGhpcy5sb2dnZXIud2FybihcbiAgICAgICAgICBgQW4gZXJyb3Igb2NjdXJyZWQgd2hpbGUgcmVjb25uZWN0aW5nIHRvIG9nbWlvcyBjbGllbnQ6ICR7ZXJyb3J9YCxcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9KSxcbiAgICAgIHtcbiAgICAgICAgbWF4QXR0ZW1wdHM6IHRoaXMucmVjb25uZWN0aW9uQ29uZmlnLm1heEF0dGVtcHRzLFxuICAgICAgICBiYWNrb2ZmOiBuZXcgRXhwb25lbnRpYWxCYWNrb2ZmKHtcbiAgICAgICAgICBtYXhEZWxheTogdGhpcy5yZWNvbm5lY3Rpb25Db25maWcubWF4RGVsYXksXG4gICAgICAgICAgaW5pdGlhbERlbGF5OiB0aGlzLnJlY29ubmVjdGlvbkNvbmZpZy5pbml0aWFsRGVsYXksXG4gICAgICAgIH0pLFxuICAgICAgfSxcbiAgICApO1xuXG4gICAgbGV0IHRyaWFsID0gMDtcbiAgICByZXRyeVBvbGljeS5leGVjdXRlKGFzeW5jICgpID0+IHtcbiAgICAgIHRyaWFsKys7XG4gICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhcbiAgICAgICAgYFJldHJ5aW5nIHRvIGNvbm5lY3QgdG8gb2dtaW9zIGNsaWVudCBpbiB0cmlhbCBzdGVwICMke3RyaWFsfWAsXG4gICAgICApO1xuICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuc3RhcnQoKTtcbiAgICB9KTtcbiAgfTtcblxuICAvKipcbiAgICogc3RhcnQgc2Nhbm5lci4gZmlyc3QgZmluZCBmb3JrIHBvaW50LiBmb3JrIGFsbCBibG9ja3MgYW5kIHJlcXVlc3QgdXBkYXRlcyBmcm9tIG5vZGUuXG4gICAqL1xuICBzdGFydCA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICB0aGlzLnN0b3BwZWQgPSBmYWxzZTtcbiAgICBjb25zdCBjb250ZXh0OiBJbnRlcmFjdGlvbkNvbnRleHQgPSBhd2FpdCBjcmVhdGVJbnRlcmFjdGlvbkNvbnRleHQoXG4gICAgICAoZXJyKSA9PiB0aGlzLmxvZ2dlci5lcnJvcihgJHtlcnJ9YCksXG4gICAgICB0aGlzLmNvbm5lY3Rpb25DbG9zZUhhbmRsZXIsXG4gICAgICB7IGNvbm5lY3Rpb246IHsgcG9ydDogdGhpcy5wb3J0LCBob3N0OiB0aGlzLmhvc3QsIHRsczogdGhpcy51c2VUbHMgfSB9LFxuICAgICk7XG4gICAgY29uc3QgaW50ZXJzZWN0ID0gYXdhaXQgdGhpcy5maW5kSW50ZXJzZWN0aW9uKGNvbnRleHQpO1xuICAgIGlmIChpbnRlcnNlY3QpIHtcbiAgICAgIC8vIGZpbmQgaW50ZXJzZWN0IHRoZW4gc3RhcnQgZnJvbSB0aGF0IHBvaW50XG4gICAgICB0aGlzLmNsaWVudCA9IGF3YWl0IGNyZWF0ZUNoYWluU3luY2hyb25pemF0aW9uQ2xpZW50KFxuICAgICAgICBjb250ZXh0LFxuICAgICAgICB7XG4gICAgICAgICAgcm9sbEJhY2t3YXJkOiB0aGlzLnJvbGxCYWNrd2FyZCxcbiAgICAgICAgICByb2xsRm9yd2FyZDogdGhpcy5yb2xsRm9yd2FyZCxcbiAgICAgICAgfSxcbiAgICAgICAgeyBzZXF1ZW50aWFsOiB0cnVlIH0sXG4gICAgICApO1xuICAgICAgdGhpcy5jb25uZWN0ZWQgPSB0cnVlO1xuICAgICAgYXdhaXQgdGhpcy5mb3JrQmxvY2soaW50ZXJzZWN0LmhlaWdodCArIDEpO1xuICAgICAgYXdhaXQgdGhpcy5jbGllbnQucmVzdW1lKFtpbnRlcnNlY3QucG9pbnRdLCAyKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgRXJyb3IoJ0NhbiBub3Qgc3RhcnQgc2Nhbm5lci4gaW5pdGlhbCBibG9jayBpcyBpbnZhbGlkJyk7XG4gICAgfVxuICB9O1xuXG4gIC8qKlxuICAgKiBzdG9wIHdzIGNvbm5lY3Rpb24gdG8gbm9kZS5cbiAgICovXG4gIHN0b3AgPSBhc3luYyAoKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgdGhpcy5zdG9wcGVkID0gdHJ1ZTtcbiAgICBhd2FpdCB0aGlzLmNsaWVudC5zaHV0ZG93bigpO1xuICB9O1xuXG4gIC8qKlxuICAgKiBAcmV0dXJucyBUaGUgY29ubmVjdGlvbiBzdGF0dXMgb2Ygb2dtaW9zIHNjYW5uZXJcbiAgICovXG4gIGdldENvbm5lY3Rpb25TdGF0dXMgPSAoKSA9PiB0aGlzLmNvbm5lY3RlZDtcbn1cblxuZXhwb3J0IHsgQ2FyZGFub09nbWlvc1NjYW5uZXIgfTtcbiJdfQ==