@dashevo/dapi-client 0.24.0-dev.6 → 0.25.0-dev.2
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.
- package/README.md +7 -8
- package/lib/BlockHeadersProvider/BlockHeadersProvider.js +187 -50
- package/lib/BlockHeadersProvider/BlockHeadersReader.js +170 -88
- package/lib/BlockHeadersProvider/createBlockHeadersProviderFromOptions.js +31 -16
- package/lib/DAPIClient.js +0 -6
- package/lib/dapiAddressProvider/DAPIAddress.js +14 -40
- package/lib/dapiAddressProvider/ListDAPIAddressProvider.js +3 -4
- package/lib/dapiAddressProvider/SimplifiedMasternodeListDAPIAddressProvider.js +8 -1
- package/lib/index.js +5 -0
- package/lib/methods/platform/response/Metadata.js +21 -1
- package/lib/methods/platform/response/Proof.js +16 -5
- package/lib/test/bootstrap.js +8 -0
- package/lib/test/fixtures/getMNListDiffsFixture.js +85 -1066
- package/lib/test/fixtures/getMetadataFixture.js +9 -1
- package/lib/test/fixtures/getProofFixture.js +3 -2
- package/lib/test/mocks/BlockHeadersWithChainLocksStreamMock.js +54 -0
- package/lib/test/mocks/mockHeadersChain.js +55 -0
- package/lib/transport/GrpcTransport/GrpcTransport.js +5 -10
- package/lib/transport/GrpcTransport/createGrpcTransportError.js +11 -14
- package/lib/transport/JsonRpcTransport/JsonRpcTransport.js +1 -1
- package/lib/transport/JsonRpcTransport/requestJsonRpc.js +22 -25
- package/lib/transport/ReconnectableStream.js +284 -0
- package/package.json +15 -12
- package/polyfills/fetch-polyfill.js +10 -0
- /package/lib/{test/utils → utils}/wait.js +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const { EventEmitter } = require('events');
|
|
2
|
+
const { BlockHeader } = require('@dashevo/dashcore-lib');
|
|
3
|
+
const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes');
|
|
2
4
|
|
|
3
5
|
const EVENTS = {
|
|
4
6
|
BLOCK_HEADERS: 'BLOCK_HEADERS',
|
|
@@ -6,15 +8,8 @@ const EVENTS = {
|
|
|
6
8
|
ERROR: 'error',
|
|
7
9
|
};
|
|
8
10
|
|
|
9
|
-
const COMMANDS = {
|
|
10
|
-
HANDLE_FINISHED_STREAM: 'HANDLE_FINISHED_STREAM',
|
|
11
|
-
HANDLE_STREAM_RETRY: 'HANDLE_STREAM_RETRY',
|
|
12
|
-
HANDLE_STREAM_ERROR: 'HANDLE_STREAM_ERROR',
|
|
13
|
-
};
|
|
14
|
-
|
|
15
11
|
/**
|
|
16
12
|
* @typedef BlockHeadersReaderOptions
|
|
17
|
-
* @property {CoreMethodsFacade} [coreMethods]
|
|
18
13
|
* @property {number} [maxParallelStreams]
|
|
19
14
|
* @property {number} [targetBatchSize]
|
|
20
15
|
* @property {number} [maxRetries]
|
|
@@ -23,10 +18,13 @@ const COMMANDS = {
|
|
|
23
18
|
class BlockHeadersReader extends EventEmitter {
|
|
24
19
|
/**
|
|
25
20
|
* @param {BlockHeadersReaderOptions} options
|
|
21
|
+
* @param createHistoricalSyncStream
|
|
22
|
+
* @param createContinuousSyncStream
|
|
26
23
|
*/
|
|
27
|
-
constructor(options = {}) {
|
|
24
|
+
constructor(options = {}, createHistoricalSyncStream, createContinuousSyncStream) {
|
|
28
25
|
super();
|
|
29
|
-
this.
|
|
26
|
+
this.createHistoricalSyncStream = createHistoricalSyncStream;
|
|
27
|
+
this.createContinuousSyncStream = createContinuousSyncStream;
|
|
30
28
|
this.maxParallelStreams = options.maxParallelStreams;
|
|
31
29
|
this.targetBatchSize = options.targetBatchSize;
|
|
32
30
|
this.maxRetries = options.maxRetries;
|
|
@@ -34,9 +32,16 @@ class BlockHeadersReader extends EventEmitter {
|
|
|
34
32
|
/**
|
|
35
33
|
* Holds references to the historical streams
|
|
36
34
|
*
|
|
37
|
-
* @type {
|
|
35
|
+
* @type {Stream[]}
|
|
38
36
|
*/
|
|
39
37
|
this.historicalStreams = [];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Holds reference to the continuous sync stream
|
|
41
|
+
*
|
|
42
|
+
* @type {Stream}
|
|
43
|
+
*/
|
|
44
|
+
this.continuousSyncStream = null;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
/**
|
|
@@ -48,52 +53,30 @@ class BlockHeadersReader extends EventEmitter {
|
|
|
48
53
|
*/
|
|
49
54
|
async readHistorical(fromBlockHeight, toBlockHeight) {
|
|
50
55
|
if (this.historicalStreams.length) {
|
|
51
|
-
throw new Error('Historical streams are already running');
|
|
56
|
+
throw new Error('Historical streams are already running. Please stop them first.');
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return;
|
|
59
|
+
if (fromBlockHeight <= 0) {
|
|
60
|
+
throw new Error(`Invalid fromBlockHeight value: ${fromBlockHeight}`);
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
|
|
63
|
+
const totalAmount = toBlockHeight - fromBlockHeight + 1;
|
|
64
|
+
if (totalAmount <= 0) {
|
|
60
65
|
throw new Error(`Invalid total amount of headers to read: ${totalAmount}`);
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
// Resubscribe to the stream in case of error, and replace the stream in the array
|
|
64
|
-
this.on(COMMANDS.HANDLE_STREAM_RETRY, (oldStream, newStream) => {
|
|
65
|
-
const index = this.historicalStreams.indexOf(oldStream);
|
|
66
|
-
this.historicalStreams[index] = newStream;
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// Remove stream from the array in case of error
|
|
70
|
-
this.on(COMMANDS.HANDLE_STREAM_ERROR, (stream, e) => {
|
|
71
|
-
const index = this.historicalStreams.indexOf(stream);
|
|
72
|
-
this.historicalStreams.splice(index, 1);
|
|
73
|
-
this.emit(EVENTS.ERROR, e);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// Remove finished stream from the array and emit HISTORICAL_DATA_OBTAINED event
|
|
77
|
-
this.on(COMMANDS.HANDLE_FINISHED_STREAM, (stream) => {
|
|
78
|
-
const index = this.historicalStreams.indexOf(stream);
|
|
79
|
-
this.historicalStreams.splice(index, 1);
|
|
80
|
-
if (this.historicalStreams.length === 0) {
|
|
81
|
-
this.emit(EVENTS.HISTORICAL_DATA_OBTAINED);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
68
|
const numStreams = Math.min(
|
|
86
69
|
Math.max(Math.round(totalAmount / this.targetBatchSize), 1),
|
|
87
70
|
this.maxParallelStreams,
|
|
88
71
|
);
|
|
89
72
|
|
|
90
73
|
const actualBatchSize = Math.ceil(totalAmount / numStreams);
|
|
74
|
+
|
|
91
75
|
for (let batchIndex = 0; batchIndex < numStreams; batchIndex += 1) {
|
|
92
|
-
const startingHeight = (batchIndex * actualBatchSize) +
|
|
76
|
+
const startingHeight = (batchIndex * actualBatchSize) + fromBlockHeight;
|
|
93
77
|
const count = Math.min(actualBatchSize, toBlockHeight - startingHeight + 1);
|
|
94
78
|
|
|
95
|
-
const subscribeWithRetries = this.
|
|
96
|
-
|
|
79
|
+
const subscribeWithRetries = this.createSubscribeToHistoricalBatch(this.maxRetries);
|
|
97
80
|
// eslint-disable-next-line no-await-in-loop
|
|
98
81
|
const stream = await subscribeWithRetries(startingHeight, count);
|
|
99
82
|
this.historicalStreams.push(stream);
|
|
@@ -101,48 +84,106 @@ class BlockHeadersReader extends EventEmitter {
|
|
|
101
84
|
}
|
|
102
85
|
|
|
103
86
|
stopReadingHistorical() {
|
|
104
|
-
this.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
this.historicalStreams = [];
|
|
87
|
+
while (this.historicalStreams.length) {
|
|
88
|
+
const stream = this.historicalStreams.shift();
|
|
89
|
+
this.cancelStream(stream);
|
|
90
|
+
}
|
|
109
91
|
}
|
|
110
92
|
|
|
111
93
|
/**
|
|
112
94
|
* Subscribes to continuously arriving block headers
|
|
113
95
|
*
|
|
114
96
|
* @param {number} fromBlockHeight
|
|
115
|
-
* @returns {Promise<
|
|
97
|
+
* @returns {Promise<ReconnectableStream>}
|
|
116
98
|
*/
|
|
117
99
|
async subscribeToNew(fromBlockHeight) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
100
|
+
if (this.continuousSyncStream) {
|
|
101
|
+
throw new Error('Continuous sync has already been started');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (typeof fromBlockHeight !== 'number' || fromBlockHeight < 1) {
|
|
105
|
+
throw new Error(`Invalid fromBlockHeight: ${fromBlockHeight}`);
|
|
106
|
+
}
|
|
121
107
|
|
|
122
|
-
|
|
123
|
-
|
|
108
|
+
// We don't know yet whether we already have header at fromBlockHeight
|
|
109
|
+
let lastKnownChainHeight = fromBlockHeight - 1;
|
|
110
|
+
|
|
111
|
+
const stream = await this.createContinuousSyncStream(fromBlockHeight);
|
|
112
|
+
|
|
113
|
+
const errorHandler = (e) => {
|
|
114
|
+
this.continuousSyncStream = null;
|
|
115
|
+
if (e.code === GrpcErrorCodes.CANCELLED) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this.emit(EVENTS.ERROR, e);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const dataHandler = (data) => {
|
|
122
|
+
const blockHeadersResponse = data.getBlockHeaders();
|
|
123
|
+
|
|
124
|
+
if (blockHeadersResponse) {
|
|
125
|
+
const rawHeaders = blockHeadersResponse.getHeadersList();
|
|
126
|
+
|
|
127
|
+
const headers = rawHeaders.map((header) => new BlockHeader(Buffer.from(header)));
|
|
128
|
+
|
|
129
|
+
lastKnownChainHeight += headers.length;
|
|
130
|
+
const batchHeadHeight = lastKnownChainHeight - headers.length + 1;
|
|
124
131
|
|
|
125
|
-
if (blockHeaders) {
|
|
126
132
|
/**
|
|
127
133
|
* Kills stream in case of deliberate rejection from the outside
|
|
128
134
|
*
|
|
129
135
|
* @param e
|
|
130
136
|
*/
|
|
131
|
-
const rejectHeaders = (e) => {
|
|
132
|
-
|
|
137
|
+
const rejectHeaders = async (e) => {
|
|
138
|
+
// Don't use cancelStream there because it's going to unsubscribe from events
|
|
139
|
+
stream.cancel();
|
|
140
|
+
stream.retryOnError(e);
|
|
133
141
|
};
|
|
134
142
|
|
|
135
|
-
this.emit(EVENTS.BLOCK_HEADERS,
|
|
143
|
+
this.emit(EVENTS.BLOCK_HEADERS, {
|
|
144
|
+
headers,
|
|
145
|
+
headHeight: batchHeadHeight,
|
|
146
|
+
}, rejectHeaders);
|
|
136
147
|
}
|
|
137
|
-
}
|
|
148
|
+
};
|
|
138
149
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
150
|
+
const beforeReconnectHandler = (updateArguments) => {
|
|
151
|
+
updateArguments({
|
|
152
|
+
fromBlockHeight: lastKnownChainHeight,
|
|
153
|
+
count: 0,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
lastKnownChainHeight -= 1;
|
|
157
|
+
};
|
|
142
158
|
|
|
159
|
+
const endHandler = () => {
|
|
160
|
+
this.continuousSyncStream = null;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
stream.on('data', dataHandler);
|
|
164
|
+
stream.on('beforeReconnect', beforeReconnectHandler);
|
|
165
|
+
stream.on('error', errorHandler);
|
|
166
|
+
stream.on('end', endHandler);
|
|
167
|
+
|
|
168
|
+
stream.removeAllListeners = () => {
|
|
169
|
+
stream.removeListener('data', dataHandler);
|
|
170
|
+
stream.removeListener('end', endHandler);
|
|
171
|
+
stream.removeListener('beforeReconnect', beforeReconnectHandler);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
this.continuousSyncStream = stream;
|
|
143
175
|
return stream;
|
|
144
176
|
}
|
|
145
177
|
|
|
178
|
+
unsubscribeFromNew() {
|
|
179
|
+
if (this.continuousSyncStream) {
|
|
180
|
+
this.cancelStream(this.continuousSyncStream);
|
|
181
|
+
this.continuousSyncStream = null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// TODO: refactor whole thing with ReconnectableStream that supports
|
|
186
|
+
// retry on error logic
|
|
146
187
|
/**
|
|
147
188
|
* A HOF that returns a function to subscribe to historical block headers and chain locks
|
|
148
189
|
* and handles retry logic
|
|
@@ -151,7 +192,7 @@ class BlockHeadersReader extends EventEmitter {
|
|
|
151
192
|
* @param {number} [maxRetries=0] - maximum amount of retries
|
|
152
193
|
* @returns {function(*, *): Promise<Stream>}
|
|
153
194
|
*/
|
|
154
|
-
|
|
195
|
+
createSubscribeToHistoricalBatch(maxRetries = 0) {
|
|
155
196
|
let currentRetries = 0;
|
|
156
197
|
|
|
157
198
|
/**
|
|
@@ -164,16 +205,45 @@ class BlockHeadersReader extends EventEmitter {
|
|
|
164
205
|
const subscribeWithRetries = async (fromBlockHeight, count) => {
|
|
165
206
|
let headersObtained = 0;
|
|
166
207
|
|
|
167
|
-
const stream = await this.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
208
|
+
const stream = await this.createHistoricalSyncStream(fromBlockHeight, count);
|
|
209
|
+
|
|
210
|
+
const errorHandler = (streamError) => {
|
|
211
|
+
if (streamError.code === GrpcErrorCodes.CANCELLED) {
|
|
212
|
+
// TODO: consider reworking with COMMANDS instead
|
|
213
|
+
// of producing a side effect that alters class state
|
|
214
|
+
const index = this.historicalStreams.indexOf(stream);
|
|
215
|
+
if (index >= 0) {
|
|
216
|
+
this.historicalStreams.splice(index, 1);
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (currentRetries < maxRetries) {
|
|
222
|
+
const newFromBlockHeight = fromBlockHeight + headersObtained;
|
|
223
|
+
const newCount = count - headersObtained;
|
|
224
|
+
|
|
225
|
+
// TODO: do not retry in case newCount is zero
|
|
226
|
+
subscribeWithRetries(newFromBlockHeight, newCount)
|
|
227
|
+
.then((newStream) => {
|
|
228
|
+
const index = this.historicalStreams.indexOf(stream);
|
|
229
|
+
this.historicalStreams[index] = newStream;
|
|
230
|
+
currentRetries += 1;
|
|
231
|
+
}).catch((e) => {
|
|
232
|
+
this.stopReadingHistorical();
|
|
233
|
+
this.emit(EVENTS.ERROR, e);
|
|
234
|
+
});
|
|
235
|
+
} else {
|
|
236
|
+
this.stopReadingHistorical();
|
|
237
|
+
this.emit(EVENTS.ERROR, streamError);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
171
240
|
|
|
172
|
-
|
|
241
|
+
const dataHandler = (data) => {
|
|
173
242
|
const blockHeaders = data.getBlockHeaders();
|
|
174
243
|
|
|
175
244
|
if (blockHeaders) {
|
|
176
|
-
const headersList = blockHeaders.getHeadersList()
|
|
245
|
+
const headersList = blockHeaders.getHeadersList()
|
|
246
|
+
.map((header) => new BlockHeader(Buffer.from(header)));
|
|
177
247
|
|
|
178
248
|
let rejected = false;
|
|
179
249
|
|
|
@@ -184,46 +254,58 @@ class BlockHeadersReader extends EventEmitter {
|
|
|
184
254
|
*/
|
|
185
255
|
const rejectHeaders = (e) => {
|
|
186
256
|
rejected = true;
|
|
187
|
-
stream
|
|
257
|
+
// Cancel stream and unsubscribe from all data events
|
|
258
|
+
// because they are going th be re-created in errorHandler
|
|
259
|
+
this.cancelStream(stream);
|
|
260
|
+
errorHandler(e);
|
|
188
261
|
};
|
|
189
262
|
|
|
190
|
-
|
|
263
|
+
const batchHeadHeight = fromBlockHeight + headersObtained;
|
|
264
|
+
this.emit(EVENTS.BLOCK_HEADERS, {
|
|
265
|
+
headers: headersList,
|
|
266
|
+
headHeight: batchHeadHeight,
|
|
267
|
+
}, rejectHeaders);
|
|
191
268
|
|
|
192
269
|
if (!rejected) {
|
|
193
270
|
headersObtained += headersList.length;
|
|
194
271
|
}
|
|
195
272
|
}
|
|
196
|
-
}
|
|
273
|
+
};
|
|
197
274
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
subscribeWithRetries(newFromBlockHeight, newCount)
|
|
204
|
-
.then((newStream) => {
|
|
205
|
-
currentRetries += 1;
|
|
206
|
-
this.emit(COMMANDS.HANDLE_STREAM_RETRY, stream, newStream);
|
|
207
|
-
}).catch((e) => {
|
|
208
|
-
this.emit(COMMANDS.HANDLE_STREAM_ERROR, stream, e);
|
|
209
|
-
});
|
|
210
|
-
} else {
|
|
211
|
-
this.emit(COMMANDS.HANDLE_STREAM_ERROR, stream, streamError);
|
|
275
|
+
const endHandler = () => {
|
|
276
|
+
const index = this.historicalStreams.indexOf(stream);
|
|
277
|
+
this.historicalStreams.splice(index, 1);
|
|
278
|
+
if (this.historicalStreams.length === 0) {
|
|
279
|
+
this.emit(EVENTS.HISTORICAL_DATA_OBTAINED);
|
|
212
280
|
}
|
|
213
|
-
}
|
|
281
|
+
};
|
|
214
282
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
283
|
+
// TODO: consider reworking with "pasued" mode to
|
|
284
|
+
// control the data flow manually because SPV verification is
|
|
285
|
+
// resource intense and backpressure could be a problem
|
|
286
|
+
// also the whole thing severely affects UI responsiveness
|
|
287
|
+
stream.on('data', dataHandler);
|
|
288
|
+
stream.on('error', errorHandler);
|
|
289
|
+
stream.on('end', endHandler);
|
|
290
|
+
|
|
291
|
+
stream.removeAllListeners = () => {
|
|
292
|
+
stream.removeListener('data', dataHandler);
|
|
293
|
+
stream.removeListener('end', endHandler);
|
|
294
|
+
};
|
|
218
295
|
|
|
219
296
|
return stream;
|
|
220
297
|
};
|
|
221
298
|
|
|
222
299
|
return subscribeWithRetries;
|
|
223
300
|
}
|
|
301
|
+
|
|
302
|
+
// eslint-disable-next-line class-methods-use-this
|
|
303
|
+
cancelStream(stream) {
|
|
304
|
+
stream.removeAllListeners();
|
|
305
|
+
stream.cancel();
|
|
306
|
+
}
|
|
224
307
|
}
|
|
225
308
|
|
|
226
309
|
BlockHeadersReader.EVENTS = EVENTS;
|
|
227
|
-
BlockHeadersReader.COMMANDS = COMMANDS;
|
|
228
310
|
|
|
229
311
|
module.exports = BlockHeadersReader;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const networks = require('@dashevo/dashcore-lib/lib/networks');
|
|
2
1
|
const DAPIClientError = require('../errors/DAPIClientError');
|
|
3
2
|
const BlockHeadersProvider = require('./BlockHeadersProvider');
|
|
3
|
+
const ReconnectableStream = require('../transport/ReconnectableStream');
|
|
4
4
|
|
|
5
5
|
const validateNumber = (value, name, min = NaN, max = NaN) => {
|
|
6
6
|
if (typeof value !== 'number') {
|
|
@@ -11,7 +11,7 @@ const validateNumber = (value, name, min = NaN, max = NaN) => {
|
|
|
11
11
|
throw new DAPIClientError(`'${name}' can not be less than ${min}`);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
if (!Number.isNaN(max) && value >
|
|
14
|
+
if (!Number.isNaN(max) && value > max) {
|
|
15
15
|
throw new DAPIClientError(`'${name}' can not be more than ${max}`);
|
|
16
16
|
}
|
|
17
17
|
};
|
|
@@ -32,45 +32,60 @@ function createBlockHeadersProviderFromOptions(options, coreMethods) {
|
|
|
32
32
|
blockHeadersProvider = options.blockHeadersProvider;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
const createContinuousSyncStream = (fromBlockHeight) => ReconnectableStream
|
|
36
|
+
.create(
|
|
37
|
+
coreMethods.subscribeToBlockHeadersWithChainLocks,
|
|
38
|
+
{
|
|
39
|
+
maxRetriesOnError: -1,
|
|
40
|
+
},
|
|
41
|
+
)({
|
|
42
|
+
fromBlockHeight,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const createHistoricalSyncStream = (fromBlockHeight, count) => {
|
|
46
|
+
const { subscribeToBlockHeadersWithChainLocks } = coreMethods;
|
|
47
|
+
return subscribeToBlockHeadersWithChainLocks({
|
|
48
|
+
fromBlockHeight,
|
|
49
|
+
count,
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
35
53
|
if (options.blockHeadersProviderOptions) {
|
|
54
|
+
const { network } = options;
|
|
55
|
+
|
|
36
56
|
const blockHeadersProviderOptions = {
|
|
37
57
|
...BlockHeadersProvider.defaultOptions,
|
|
38
58
|
...options.blockHeadersProviderOptions,
|
|
59
|
+
network,
|
|
39
60
|
};
|
|
40
61
|
|
|
41
62
|
const {
|
|
42
|
-
network,
|
|
43
|
-
autoStart,
|
|
44
63
|
maxParallelStreams,
|
|
45
64
|
targetBatchSize,
|
|
46
65
|
fromBlockHeight,
|
|
47
66
|
maxRetries,
|
|
48
67
|
} = blockHeadersProviderOptions;
|
|
49
68
|
|
|
50
|
-
if (network && !networks.get(network)) {
|
|
51
|
-
throw new DAPIClientError(`Invalid network '${options.network}'`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (typeof autoStart !== 'boolean') {
|
|
55
|
-
throw new DAPIClientError('\'autoStart\' option must have boolean type');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
69
|
validateNumber(maxParallelStreams, 'maxParallelStreams', 1);
|
|
59
70
|
validateNumber(targetBatchSize, 'targetBatchSize', 1);
|
|
60
71
|
validateNumber(fromBlockHeight, 'fromBlockHeight', 1);
|
|
61
|
-
validateNumber(maxRetries, 'maxRetries', 0);
|
|
72
|
+
validateNumber(maxRetries, 'maxRetries', 0, 100);
|
|
62
73
|
|
|
63
74
|
blockHeadersProvider = new BlockHeadersProvider(
|
|
64
75
|
blockHeadersProviderOptions,
|
|
76
|
+
createHistoricalSyncStream,
|
|
77
|
+
createContinuousSyncStream,
|
|
65
78
|
);
|
|
66
79
|
}
|
|
67
80
|
|
|
68
81
|
if (!blockHeadersProvider) {
|
|
69
|
-
blockHeadersProvider = new BlockHeadersProvider(
|
|
82
|
+
blockHeadersProvider = new BlockHeadersProvider(
|
|
83
|
+
{},
|
|
84
|
+
createContinuousSyncStream,
|
|
85
|
+
createHistoricalSyncStream,
|
|
86
|
+
);
|
|
70
87
|
}
|
|
71
88
|
|
|
72
|
-
blockHeadersProvider.setCoreMethods(coreMethods);
|
|
73
|
-
|
|
74
89
|
return blockHeadersProvider;
|
|
75
90
|
}
|
|
76
91
|
|
package/lib/DAPIClient.js
CHANGED
|
@@ -65,12 +65,6 @@ class DAPIClient extends EventEmitter {
|
|
|
65
65
|
this.blockHeadersProvider.on(BlockHeadersProvider.EVENTS.ERROR, (e) => {
|
|
66
66
|
this.emit(EVENTS.ERROR, e);
|
|
67
67
|
});
|
|
68
|
-
|
|
69
|
-
if (this.options.blockHeadersProviderOptions.autoStart) {
|
|
70
|
-
this.blockHeadersProvider.start().catch((e) => {
|
|
71
|
-
this.emit(EVENTS.ERROR, e);
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
68
|
}
|
|
75
69
|
}
|
|
76
70
|
|
|
@@ -10,13 +10,12 @@ class DAPIAddress {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
if (typeof address === 'string') {
|
|
13
|
-
const [host,
|
|
13
|
+
const [host, port, ssl] = address.split(':');
|
|
14
14
|
|
|
15
15
|
// eslint-disable-next-line no-param-reassign
|
|
16
16
|
address = {
|
|
17
17
|
host,
|
|
18
|
-
|
|
19
|
-
grpcPort: grpcPort ? parseInt(grpcPort, 10) : DAPIAddress.DEFAULT_GRPC_PORT,
|
|
18
|
+
port: port ? parseInt(port, 10) : DAPIAddress.DEFAULT_PORT,
|
|
20
19
|
protocol: ssl === 'no-ssl' ? 'http' : DAPIAddress.DEFAULT_PROTOCOL,
|
|
21
20
|
allowSelfSignedCertificate: ssl === 'self-signed',
|
|
22
21
|
};
|
|
@@ -28,8 +27,7 @@ class DAPIAddress {
|
|
|
28
27
|
|
|
29
28
|
this.protocol = address.protocol || DAPIAddress.DEFAULT_PROTOCOL;
|
|
30
29
|
this.host = address.host;
|
|
31
|
-
this.
|
|
32
|
-
this.grpcPort = address.grpcPort || DAPIAddress.DEFAULT_GRPC_PORT;
|
|
30
|
+
this.port = address.port || DAPIAddress.DEFAULT_PORT;
|
|
33
31
|
this.proRegTxHash = address.proRegTxHash;
|
|
34
32
|
this.allowSelfSignedCertificate = address.allowSelfSignedCertificate || false;
|
|
35
33
|
|
|
@@ -68,43 +66,22 @@ class DAPIAddress {
|
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
/**
|
|
71
|
-
* Get
|
|
69
|
+
* Get port
|
|
72
70
|
*
|
|
73
71
|
* @returns {number}
|
|
74
72
|
*/
|
|
75
|
-
|
|
76
|
-
return this.
|
|
73
|
+
getPort() {
|
|
74
|
+
return this.port;
|
|
77
75
|
}
|
|
78
76
|
|
|
79
77
|
/**
|
|
80
|
-
* Set
|
|
78
|
+
* Set port
|
|
81
79
|
*
|
|
82
80
|
* @param {number} port
|
|
83
81
|
* @returns {DAPIAddress}
|
|
84
82
|
*/
|
|
85
|
-
|
|
86
|
-
this.
|
|
87
|
-
|
|
88
|
-
return this;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Get gRPC port
|
|
93
|
-
*
|
|
94
|
-
* @returns {number}
|
|
95
|
-
*/
|
|
96
|
-
getGrpcPort() {
|
|
97
|
-
return this.grpcPort;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Set gRPC port
|
|
102
|
-
*
|
|
103
|
-
* @param {number} port
|
|
104
|
-
* @returns {DAPIAddress}
|
|
105
|
-
*/
|
|
106
|
-
setGrpcPort(port) {
|
|
107
|
-
this.grpcPort = port;
|
|
83
|
+
setPort(port) {
|
|
84
|
+
this.port = port;
|
|
108
85
|
|
|
109
86
|
return this;
|
|
110
87
|
}
|
|
@@ -179,28 +156,25 @@ class DAPIAddress {
|
|
|
179
156
|
return {
|
|
180
157
|
protocol: this.getProtocol(),
|
|
181
158
|
host: this.getHost(),
|
|
182
|
-
|
|
183
|
-
grpcPort: this.getGrpcPort(),
|
|
159
|
+
port: this.getPort(),
|
|
184
160
|
proRegTxHash: this.getProRegTxHash(),
|
|
185
161
|
allowSelfSignedCertificate: this.isSelfSignedCertificateAllowed(),
|
|
186
162
|
};
|
|
187
163
|
}
|
|
188
164
|
|
|
189
165
|
toString() {
|
|
190
|
-
return `${this.getProtocol()}://${this.getHost()}:${this.
|
|
166
|
+
return `${this.getProtocol()}://${this.getHost()}:${this.getPort()}`;
|
|
191
167
|
}
|
|
192
168
|
}
|
|
193
169
|
|
|
194
|
-
DAPIAddress.
|
|
195
|
-
DAPIAddress.
|
|
196
|
-
DAPIAddress.DEFAULT_PROTOCOL = 'http';
|
|
170
|
+
DAPIAddress.DEFAULT_PORT = 443;
|
|
171
|
+
DAPIAddress.DEFAULT_PROTOCOL = 'https';
|
|
197
172
|
|
|
198
173
|
/**
|
|
199
174
|
* @typedef {object} RawDAPIAddress
|
|
200
175
|
* @property {string} protocol
|
|
201
176
|
* @property {string} host
|
|
202
|
-
* @property {number} [
|
|
203
|
-
* @property {number} [grpcPort=3010]
|
|
177
|
+
* @property {number} [port=443]
|
|
204
178
|
* @property {string} [proRegTxHash]
|
|
205
179
|
* @property {bool} [selfSigned]
|
|
206
180
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const sample = require('lodash
|
|
1
|
+
const sample = require('lodash/sample');
|
|
2
2
|
const networks = require('@dashevo/dashcore-lib/lib/networks');
|
|
3
3
|
|
|
4
4
|
class ListDAPIAddressProvider {
|
|
@@ -30,7 +30,7 @@ class ListDAPIAddressProvider {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// This is a temporary fix for a localhost masternode.
|
|
33
|
-
// On
|
|
33
|
+
// On macOS, internal docker IP is used to register masternode, and it's
|
|
34
34
|
// not really possible to bind to that address, so that workaround is introduced.
|
|
35
35
|
const network = networks.get(this.options.network);
|
|
36
36
|
if (network && network.regtestEnabled) {
|
|
@@ -39,8 +39,7 @@ class ListDAPIAddressProvider {
|
|
|
39
39
|
liveAddress.protocol = 'https';
|
|
40
40
|
liveAddress.host = '127.0.0.1';
|
|
41
41
|
liveAddress.allowSelfSignedCertificate = true;
|
|
42
|
-
liveAddress.
|
|
43
|
-
liveAddress.grpcPort = 3010 + randomNodeIndex * 100;
|
|
42
|
+
liveAddress.port = 2443 + randomNodeIndex * 100;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
return liveAddress;
|
|
@@ -19,10 +19,15 @@ class SimplifiedMasternodeListDAPIAddressProvider {
|
|
|
19
19
|
*/
|
|
20
20
|
async getLiveAddress() {
|
|
21
21
|
const sml = await this.smlProvider.getSimplifiedMNList();
|
|
22
|
-
const validMasternodeList = sml.getValidMasternodesList()
|
|
22
|
+
const validMasternodeList = sml.getValidMasternodesList()
|
|
23
|
+
// Keep only HP masternodes
|
|
24
|
+
.filter((smlEntry) => smlEntry.nType === 1);
|
|
23
25
|
|
|
24
26
|
const addressesByRegProTxHashes = {};
|
|
27
|
+
let allowSelfSignedCertificate;
|
|
25
28
|
this.listDAPIAddressProvider.getAllAddresses().forEach((address) => {
|
|
29
|
+
allowSelfSignedCertificate = address.isSelfSignedCertificateAllowed();
|
|
30
|
+
|
|
26
31
|
if (!address.getProRegTxHash()) {
|
|
27
32
|
return;
|
|
28
33
|
}
|
|
@@ -36,6 +41,8 @@ class SimplifiedMasternodeListDAPIAddressProvider {
|
|
|
36
41
|
if (!address) {
|
|
37
42
|
address = new DAPIAddress({
|
|
38
43
|
host: smlEntry.getIp(),
|
|
44
|
+
port: smlEntry.platformHTTPPort,
|
|
45
|
+
allowSelfSignedCertificate,
|
|
39
46
|
proRegTxHash: smlEntry.proRegTxHash,
|
|
40
47
|
});
|
|
41
48
|
} else {
|
package/lib/index.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
require('../polyfills/fetch-polyfill');
|
|
2
|
+
|
|
1
3
|
const DAPIClient = require('./DAPIClient');
|
|
2
4
|
|
|
3
5
|
const NotFoundError = require('./transport/GrpcTransport/errors/NotFoundError');
|
|
6
|
+
const BlockHeadersProvider = require('./BlockHeadersProvider/BlockHeadersProvider');
|
|
4
7
|
|
|
5
8
|
DAPIClient.Errors = {
|
|
6
9
|
NotFoundError,
|
|
7
10
|
};
|
|
8
11
|
|
|
12
|
+
DAPIClient.BlockHeadersProvider = BlockHeadersProvider;
|
|
13
|
+
|
|
9
14
|
module.exports = DAPIClient;
|