@dashevo/dapi-client 0.22.0-dev.4 → 0.22.0-dev.7.alpha.1

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 CHANGED
@@ -12,7 +12,7 @@ Platform via the decentralized API ([DAPI](https://github.com/dashevo/dapi))
12
12
  hosted on Dash masternodes.
13
13
 
14
14
  - `DAPI-Client` provides automatic server (masternode) discovery using either a default seed node or a user-supplied one
15
- - `DAPI-Client` maps to DAPI's [RPC](https://github.com/dashevo/dapi/tree/master/lib/rpcServer/commands) and [gRPC](https://github.com/dashevo/dapi/tree/master/lib/grpcServer/handlers) endpoints
15
+ - `DAPI-Client` maps to DAPI's [RPC](https://github.com/dashevo/platform/tree/master/packages/dapi/lib/rpcServer/commands) and [gRPC](https://github.com/dashevo/platform/tree/master/packages/dapi/lib/grpcServer/handlers) endpoints
16
16
 
17
17
  ## Table of Contents
18
18
  - [Install](#install)
@@ -101,12 +101,12 @@ client.core.getBestBlockHash(options).then((r) => {
101
101
 
102
102
  ## Documentation
103
103
 
104
- More extensive documentation available at https://dashevo.github.io/js-dapi-client/.
104
+ More extensive documentation available at https://dashevo.github.io/platform/DAPI-Client/.
105
105
 
106
106
 
107
107
  ## Contributing
108
108
 
109
- Feel free to dive in! [Open an issue](https://github.com/dashevo/js-dapi-client/issues/new/choose) or submit PRs.
109
+ Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs.
110
110
 
111
111
  ## License
112
112
 
@@ -0,0 +1,121 @@
1
+ const EventEmitter = require('events');
2
+ const { SpvChain } = require('@dashevo/dash-spv');
3
+
4
+ const BlockHeadersReader = require('./BlockHeadersReader');
5
+
6
+ /**
7
+ * @typedef {BlockHeadersProviderOptions} BlockHeadersProviderOptions
8
+ * @property {string} [network=testnet]
9
+ * @property {number} [maxParallelStreams=5] max parallel streams to read historical block headers
10
+ * @property {number} [targetBatchSize=100000] a target batch size per stream
11
+ * @property {number} [maxRetries=10] max amount of retries per stream connection
12
+ * @property {number} [autoStart=false] auto start fetching verifying block headers
13
+ */
14
+ const defaultOptions = {
15
+ network: 'testnet',
16
+ maxParallelStreams: 5,
17
+ targetBatchSize: 100000,
18
+ fromBlockHeight: 1,
19
+ maxRetries: 10,
20
+ autoStart: false,
21
+ };
22
+
23
+ const EVENTS = {
24
+ ERROR: 'error',
25
+ };
26
+
27
+ class BlockHeadersProvider extends EventEmitter {
28
+ /**
29
+ * @param {BlockHeadersProviderOptions} options
30
+ */
31
+ constructor(options = {}) {
32
+ super();
33
+ this.options = {
34
+ ...defaultOptions,
35
+ ...options,
36
+ };
37
+
38
+ this.spvChain = new SpvChain(this.options.network);
39
+ this.started = false;
40
+ }
41
+
42
+ /**
43
+ * @param {CoreMethodsFacade} coreMethods
44
+ */
45
+ setCoreMethods(coreMethods) {
46
+ this.coreMethods = coreMethods;
47
+ }
48
+
49
+ /**
50
+ * @param {BlockHeadersReader} blockHeadersReader
51
+ */
52
+ setBlockHeadersReader(blockHeadersReader) {
53
+ this.blockHeadersReader = blockHeadersReader;
54
+ }
55
+
56
+ /**
57
+ *
58
+ * @param spvChain
59
+ */
60
+ setSpvChain(spvChain) {
61
+ this.spvChain = spvChain;
62
+ }
63
+
64
+ async start() {
65
+ if (!this.coreMethods) {
66
+ throw new Error('Core methods have not been provided. Please use "setCoreMethods"');
67
+ }
68
+
69
+ if (this.started) {
70
+ throw new Error('BlockHeaderProvider has already been started');
71
+ }
72
+
73
+ const { chain: { blocksCount: bestBlockHeight } } = await this.coreMethods.getStatus();
74
+
75
+ if (!this.blockHeadersReader) {
76
+ this.blockHeadersReader = new BlockHeadersReader(
77
+ {
78
+ coreMethods: this.coreMethods,
79
+ maxParallelStreams: this.options.maxParallelStreams,
80
+ targetBatchSize: this.options.targetBatchSize,
81
+ maxRetries: this.options.maxRetries,
82
+ },
83
+ );
84
+ }
85
+
86
+ this.blockHeadersReader.on(BlockHeadersReader.EVENTS.ERROR, (e) => {
87
+ this.emit(EVENTS.ERROR, e);
88
+ });
89
+
90
+ this.blockHeadersReader.on(BlockHeadersReader.EVENTS.HISTORICAL_DATA_OBTAINED, () => {
91
+ this.blockHeadersReader.subscribeToNew(bestBlockHeight)
92
+ .catch((e) => {
93
+ this.emit(EVENTS.ERROR, e);
94
+ });
95
+ });
96
+
97
+ this.blockHeadersReader.on(BlockHeadersReader.EVENTS.BLOCK_HEADERS, (headers, reject) => {
98
+ try {
99
+ this.spvChain.addHeaders(headers.map((header) => Buffer.from(header)));
100
+ } catch (e) {
101
+ if (e.message === 'Some headers are invalid') {
102
+ reject(e);
103
+ } else {
104
+ this.emit(EVENTS.ERROR, e);
105
+ }
106
+ }
107
+ });
108
+
109
+ await this.blockHeadersReader.readHistorical(
110
+ this.options.fromBlockHeight,
111
+ bestBlockHeight - 1,
112
+ );
113
+
114
+ this.started = true;
115
+ }
116
+ }
117
+
118
+ BlockHeadersProvider.EVENTS = EVENTS;
119
+ BlockHeadersProvider.defaultOptions = defaultOptions;
120
+
121
+ module.exports = BlockHeadersProvider;
@@ -0,0 +1,229 @@
1
+ const { EventEmitter } = require('events');
2
+
3
+ const EVENTS = {
4
+ BLOCK_HEADERS: 'BLOCK_HEADERS',
5
+ HISTORICAL_DATA_OBTAINED: 'HISTORICAL_DATA_OBTAINED',
6
+ ERROR: 'error',
7
+ };
8
+
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
+ /**
16
+ * @typedef BlockHeadersReaderOptions
17
+ * @property {CoreMethodsFacade} [coreMethods]
18
+ * @property {number} [maxParallelStreams]
19
+ * @property {number} [targetBatchSize]
20
+ * @property {number} [maxRetries]
21
+ */
22
+
23
+ class BlockHeadersReader extends EventEmitter {
24
+ /**
25
+ * @param {BlockHeadersReaderOptions} options
26
+ */
27
+ constructor(options = {}) {
28
+ super();
29
+ this.coreMethods = options.coreMethods;
30
+ this.maxParallelStreams = options.maxParallelStreams;
31
+ this.targetBatchSize = options.targetBatchSize;
32
+ this.maxRetries = options.maxRetries;
33
+
34
+ /**
35
+ * Holds references to the historical streams
36
+ *
37
+ * @type {*[]}
38
+ */
39
+ this.historicalStreams = [];
40
+ }
41
+
42
+ /**
43
+ * Reads historical block heights using multiple streams
44
+ *
45
+ * @param {number} fromBlockHeight
46
+ * @param {number} toBlockHeight
47
+ * @returns {Promise<void>}
48
+ */
49
+ async readHistorical(fromBlockHeight, toBlockHeight) {
50
+ if (this.historicalStreams.length) {
51
+ throw new Error('Historical streams are already running');
52
+ }
53
+
54
+ const totalAmount = toBlockHeight - fromBlockHeight + 1;
55
+ if (totalAmount === 0) {
56
+ return;
57
+ }
58
+
59
+ if (totalAmount < 0) {
60
+ throw new Error(`Invalid total amount of headers to read: ${totalAmount}`);
61
+ }
62
+
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
+ const numStreams = Math.min(
86
+ Math.max(Math.round(totalAmount / this.targetBatchSize), 1),
87
+ this.maxParallelStreams,
88
+ );
89
+
90
+ const actualBatchSize = Math.ceil(totalAmount / numStreams);
91
+ for (let batchIndex = 0; batchIndex < numStreams; batchIndex += 1) {
92
+ const startingHeight = (batchIndex * actualBatchSize) + 1;
93
+ const count = Math.min(actualBatchSize, toBlockHeight - startingHeight + 1);
94
+
95
+ const subscribeWithRetries = this.subscribeToHistoricalBatch(this.maxRetries);
96
+
97
+ // eslint-disable-next-line no-await-in-loop
98
+ const stream = await subscribeWithRetries(startingHeight, count);
99
+ this.historicalStreams.push(stream);
100
+ }
101
+ }
102
+
103
+ stopReadingHistorical() {
104
+ this.removeAllListeners(COMMANDS.HANDLE_STREAM_RETRY);
105
+ this.removeAllListeners(COMMANDS.HANDLE_STREAM_ERROR);
106
+ this.removeAllListeners(COMMANDS.HANDLE_FINISHED_STREAM);
107
+ this.historicalStreams.forEach((stream) => stream.destroy());
108
+ this.historicalStreams = [];
109
+ }
110
+
111
+ /**
112
+ * Subscribes to continuously arriving block headers
113
+ *
114
+ * @param {number} fromBlockHeight
115
+ * @returns {Promise<Stream>}
116
+ */
117
+ async subscribeToNew(fromBlockHeight) {
118
+ const stream = await this.coreMethods.subscribeToBlockHeadersWithChainLocks({
119
+ fromBlockHeight,
120
+ });
121
+
122
+ stream.on('data', (data) => {
123
+ const blockHeaders = data.getBlockHeaders();
124
+
125
+ if (blockHeaders) {
126
+ /**
127
+ * Kills stream in case of deliberate rejection from the outside
128
+ *
129
+ * @param e
130
+ */
131
+ const rejectHeaders = (e) => {
132
+ stream.destroy(e);
133
+ };
134
+
135
+ this.emit(EVENTS.BLOCK_HEADERS, blockHeaders.getHeadersList(), rejectHeaders);
136
+ }
137
+ });
138
+
139
+ stream.on('error', (e) => {
140
+ this.emit(EVENTS.ERROR, e);
141
+ });
142
+
143
+ return stream;
144
+ }
145
+
146
+ /**
147
+ * A HOF that returns a function to subscribe to historical block headers and chain locks
148
+ * and handles retry logic
149
+ *
150
+ * @private
151
+ * @param {number} [maxRetries=0] - maximum amount of retries
152
+ * @returns {function(*, *): Promise<Stream>}
153
+ */
154
+ subscribeToHistoricalBatch(maxRetries = 0) {
155
+ let currentRetries = 0;
156
+
157
+ /**
158
+ * Subscribes to the stream of historical data and handles retry logic
159
+ *
160
+ * @param {number} fromBlockHeight
161
+ * @param {number} count
162
+ * @returns {Promise<Stream>}
163
+ */
164
+ const subscribeWithRetries = async (fromBlockHeight, count) => {
165
+ let headersObtained = 0;
166
+
167
+ const stream = await this.coreMethods.subscribeToBlockHeadersWithChainLocks({
168
+ fromBlockHeight,
169
+ count,
170
+ });
171
+
172
+ stream.on('data', (data) => {
173
+ const blockHeaders = data.getBlockHeaders();
174
+
175
+ if (blockHeaders) {
176
+ const headersList = blockHeaders.getHeadersList();
177
+
178
+ let rejected = false;
179
+
180
+ /**
181
+ * Kills stream in case of deliberate rejection from the outside
182
+ *
183
+ * @param e
184
+ */
185
+ const rejectHeaders = (e) => {
186
+ rejected = true;
187
+ stream.destroy(e);
188
+ };
189
+
190
+ this.emit(EVENTS.BLOCK_HEADERS, headersList, rejectHeaders);
191
+
192
+ if (!rejected) {
193
+ headersObtained += headersList.length;
194
+ }
195
+ }
196
+ });
197
+
198
+ stream.on('error', (streamError) => {
199
+ if (currentRetries < maxRetries) {
200
+ const newFromBlockHeight = fromBlockHeight + headersObtained;
201
+ const newCount = count - headersObtained;
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);
212
+ }
213
+ });
214
+
215
+ stream.on('end', () => {
216
+ this.emit(COMMANDS.HANDLE_FINISHED_STREAM, stream);
217
+ });
218
+
219
+ return stream;
220
+ };
221
+
222
+ return subscribeWithRetries;
223
+ }
224
+ }
225
+
226
+ BlockHeadersReader.EVENTS = EVENTS;
227
+ BlockHeadersReader.COMMANDS = COMMANDS;
228
+
229
+ module.exports = BlockHeadersReader;
@@ -0,0 +1,77 @@
1
+ const networks = require('@dashevo/dashcore-lib/lib/networks');
2
+ const DAPIClientError = require('../errors/DAPIClientError');
3
+ const BlockHeadersProvider = require('./BlockHeadersProvider');
4
+
5
+ const validateNumber = (value, name, min = NaN, max = NaN) => {
6
+ if (typeof value !== 'number') {
7
+ throw new DAPIClientError(`'${name}' is not a number`);
8
+ }
9
+
10
+ if (!Number.isNaN(min) && value < min) {
11
+ throw new DAPIClientError(`'${name}' can not be less than ${min}`);
12
+ }
13
+
14
+ if (!Number.isNaN(max) && value > min) {
15
+ throw new DAPIClientError(`'${name}' can not be more than ${max}`);
16
+ }
17
+ };
18
+
19
+ /**
20
+ * @typedef {createBlockHeadersProviderFromOptions}
21
+ * @param {DAPIClientOptions} options
22
+ * @param {CoreMethodsFacade} coreMethods
23
+ * @returns {BlockHeadersProvider}
24
+ */
25
+ function createBlockHeadersProviderFromOptions(options, coreMethods) {
26
+ let blockHeadersProvider;
27
+ if (options.blockHeadersProvider) {
28
+ if (options.blockHeadersProviderOptions) {
29
+ throw new DAPIClientError("Can't use 'blockHeadersProviderOptions' with 'blockHeadersProvider' option");
30
+ }
31
+
32
+ blockHeadersProvider = options.blockHeadersProvider;
33
+ }
34
+
35
+ if (options.blockHeadersProviderOptions) {
36
+ const blockHeadersProviderOptions = {
37
+ ...BlockHeadersProvider.defaultOptions,
38
+ ...options.blockHeadersProviderOptions,
39
+ };
40
+
41
+ const {
42
+ network,
43
+ autoStart,
44
+ maxParallelStreams,
45
+ targetBatchSize,
46
+ fromBlockHeight,
47
+ maxRetries,
48
+ } = blockHeadersProviderOptions;
49
+
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
+ validateNumber(maxParallelStreams, 'maxParallelStreams', 1);
59
+ validateNumber(targetBatchSize, 'targetBatchSize', 1);
60
+ validateNumber(fromBlockHeight, 'fromBlockHeight', 1);
61
+ validateNumber(maxRetries, 'maxRetries', 0);
62
+
63
+ blockHeadersProvider = new BlockHeadersProvider(
64
+ blockHeadersProviderOptions,
65
+ );
66
+ }
67
+
68
+ if (!blockHeadersProvider) {
69
+ blockHeadersProvider = new BlockHeadersProvider();
70
+ }
71
+
72
+ blockHeadersProvider.setCoreMethods(coreMethods);
73
+
74
+ return blockHeadersProvider;
75
+ }
76
+
77
+ module.exports = createBlockHeadersProviderFromOptions;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @interface BlockHeadersReader
3
+ */
4
+
5
+ /**
6
+ * @private
7
+ * @function
8
+ * @async
9
+ * @name BlockHeadersReader#createBatchFetcher
10
+ * @returns {(function(*, *): Promise<void>)|*}
11
+ */
12
+
13
+ /**
14
+ * @function
15
+ * @async
16
+ * @name BlockHeadersReader#subscribeToNew
17
+ * @returns {Promise<Stream>}
18
+ */
19
+
20
+ /**
21
+ * @function
22
+ * @async
23
+ * @name BlockHeadersReader#readHistorical
24
+ * @returns {Promise<void>}
25
+ */
package/lib/DAPIClient.js CHANGED
@@ -1,3 +1,5 @@
1
+ const EventEmitter = require('events');
2
+
1
3
  const GrpcTransport = require('./transport/GrpcTransport/GrpcTransport');
2
4
  const JsonRpcTransport = require('./transport/JsonRpcTransport/JsonRpcTransport');
3
5
 
@@ -9,15 +11,25 @@ const requestJsonRpc = require('./transport/JsonRpcTransport/requestJsonRpc');
9
11
  const createGrpcTransportError = require('./transport/GrpcTransport/createGrpcTransportError');
10
12
  const createJsonTransportError = require('./transport/JsonRpcTransport/createJsonTransportError');
11
13
 
12
- class DAPIClient {
14
+ const BlockHeadersProvider = require('./BlockHeadersProvider/BlockHeadersProvider');
15
+ const createBlockHeadersProviderFromOptions = require('./BlockHeadersProvider/createBlockHeadersProviderFromOptions');
16
+
17
+ const EVENTS = {
18
+ ERROR: 'error',
19
+ };
20
+
21
+ class DAPIClient extends EventEmitter {
13
22
  /**
14
23
  * @param {DAPIClientOptions} [options]
15
24
  */
16
25
  constructor(options = {}) {
26
+ super();
27
+
17
28
  this.options = {
18
29
  network: 'testnet',
19
30
  timeout: 10000,
20
31
  retries: 5,
32
+ blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions,
21
33
  ...options,
22
34
  };
23
35
 
@@ -40,9 +52,30 @@ class DAPIClient {
40
52
 
41
53
  this.core = new CoreMethodsFacade(jsonRpcTransport, grpcTransport);
42
54
  this.platform = new PlatformMethodsFacade(grpcTransport);
55
+
56
+ this.initBlockHeadersProvider();
57
+ }
58
+
59
+ /**
60
+ * @private
61
+ */
62
+ initBlockHeadersProvider() {
63
+ this.blockHeadersProvider = createBlockHeadersProviderFromOptions(this.options, this.core);
64
+
65
+ this.blockHeadersProvider.on(BlockHeadersProvider.EVENTS.ERROR, (e) => {
66
+ this.emit(EVENTS.ERROR, e);
67
+ });
68
+
69
+ if (this.options.blockHeadersProviderOptions.autoStart) {
70
+ this.blockHeadersProvider.start().catch((e) => {
71
+ this.emit(EVENTS.ERROR, e);
72
+ });
73
+ }
43
74
  }
44
75
  }
45
76
 
77
+ DAPIClient.EVENTS = EVENTS;
78
+
46
79
  /**
47
80
  * @typedef {DAPIClientOptions} DAPIClientOptions
48
81
  * @property {DAPIAddressProvider} [dapiAddressProvider]
@@ -54,6 +87,8 @@ class DAPIClient {
54
87
  * @property {number} [retries=3]
55
88
  * @property {number} [baseBanTime=60000]
56
89
  * @property {boolean} [throwDeadlineExceeded]
90
+ * @property {BlockHeadersProvider} [blockHeadersProvider]
91
+ * @property {BlockHeadersProviderOptions} [blockHeadersProviderOptions]
57
92
  */
58
93
 
59
94
  module.exports = DAPIClient;
@@ -8,6 +8,7 @@ const getMnListDiffFactory = require('./getMnListDiffFactory');
8
8
  const getStatusFactory = require('./getStatusFactory');
9
9
  const getTransactionFactory = require('./getTransaction/getTransactionFactory');
10
10
  const subscribeToTransactionsWithProofsFactory = require('./subscribeToTransactionsWithProofsFactory');
11
+ const subscribeToBlockHeadersWithChainLocksFactory = require('./subscribeToBlockHeadersWithChainLocksFactory');
11
12
 
12
13
  class CoreMethodsFacade {
13
14
  /**
@@ -27,6 +28,9 @@ class CoreMethodsFacade {
27
28
  this.subscribeToTransactionsWithProofs = subscribeToTransactionsWithProofsFactory(
28
29
  grpcTransport,
29
30
  );
31
+ this.subscribeToBlockHeadersWithChainLocks = subscribeToBlockHeadersWithChainLocksFactory(
32
+ grpcTransport,
33
+ );
30
34
  }
31
35
  }
32
36
 
@@ -0,0 +1,69 @@
1
+ const {
2
+ v0: {
3
+ BlockHeadersWithChainLocksRequest,
4
+ CorePromiseClient,
5
+ },
6
+ } = require('@dashevo/dapi-grpc');
7
+
8
+ const DAPIClientError = require('../../errors/DAPIClientError');
9
+
10
+ /**
11
+ * @param {GrpcTransport} grpcTransport
12
+ * @returns {subscribeToBlockHeadersWithChainLocks}
13
+ */
14
+ function subscribeToBlockHeadersWithChainLocksFactory(grpcTransport) {
15
+ /**
16
+ * @typedef {subscribeToBlockHeadersWithChainLocks}
17
+ * @param {DAPIClientOptions & subscribeToBlockHeadersWithChainLocksOptions} [options]
18
+ * @returns {
19
+ * EventEmitter|!grpc.web.ClientReadableStream<!BlockHeadersWithChainLocksResponse>
20
+ * }
21
+ */
22
+ async function subscribeToBlockHeadersWithChainLocks(options = { }) {
23
+ // eslint-disable-next-line no-param-reassign
24
+ options = {
25
+ count: 0,
26
+ // Override global timeout option
27
+ // and timeout for this method by default
28
+ timeout: undefined,
29
+ ...options,
30
+ };
31
+
32
+ if (options.fromBlockHeight === 0) {
33
+ throw new DAPIClientError('Invalid argument: minimum value for `fromBlockHeight` is 1');
34
+ }
35
+
36
+ const request = new BlockHeadersWithChainLocksRequest();
37
+
38
+ if (options.fromBlockHeight !== undefined) {
39
+ request.setFromBlockHeight(options.fromBlockHeight);
40
+ }
41
+
42
+ if (options.fromBlockHash) {
43
+ request.setFromBlockHash(
44
+ Buffer.from(options.fromBlockHash, 'hex'),
45
+ );
46
+ }
47
+
48
+ request.setCount(options.count);
49
+
50
+ return grpcTransport.request(
51
+ CorePromiseClient,
52
+ 'subscribeToBlockHeadersWithChainLocks',
53
+ request,
54
+ options,
55
+ );
56
+ }
57
+
58
+ return subscribeToBlockHeadersWithChainLocks;
59
+ }
60
+
61
+ /**
62
+ * @typedef {object} subscribeToBlockHeadersWithChainLocksOptions
63
+ * @property {string} [fromBlockHash] - Specifies block hash to start syncing from
64
+ * @property {number} [fromBlockHeight] - Specifies block height to start syncing from
65
+ * @property {number} [count=0] - Number of blocks to sync,
66
+ * if set to 0 syncing is continuously sends new data as well
67
+ */
68
+
69
+ module.exports = subscribeToBlockHeadersWithChainLocksFactory;
@@ -1,3 +1,5 @@
1
+ const cbor = require('cbor');
2
+
1
3
  const AbstractResponse = require('../response/AbstractResponse');
2
4
 
3
5
  class GetIdentitiesByPublicKeyHashesResponse extends AbstractResponse {
@@ -13,7 +15,7 @@ class GetIdentitiesByPublicKeyHashesResponse extends AbstractResponse {
13
15
  }
14
16
 
15
17
  /**
16
- * @returns {Buffer[]}
18
+ * @returns {Array<Buffer[]>}
17
19
  */
18
20
  getIdentities() {
19
21
  return this.identities;
@@ -27,8 +29,13 @@ class GetIdentitiesByPublicKeyHashesResponse extends AbstractResponse {
27
29
  const { metadata, proof } = AbstractResponse.createMetadataAndProofFromProto(proto);
28
30
 
29
31
  return new GetIdentitiesByPublicKeyHashesResponse(
30
- proto.getIdentitiesList()
31
- .map((identity) => (identity.length > 0 ? Buffer.from(identity) : null)),
32
+ proto.getIdentitiesList().map((identitiesSerialized) => {
33
+ const identities = cbor.decode(identitiesSerialized);
34
+
35
+ return identities.map((identity) => (
36
+ (identity.length > 0 ? Buffer.from(identity) : null)
37
+ ));
38
+ }),
32
39
  metadata,
33
40
  proof,
34
41
  );
@@ -1,3 +1,5 @@
1
+ const cbor = require('cbor');
2
+
1
3
  const AbstractResponse = require('../response/AbstractResponse');
2
4
 
3
5
  class GetIdentityIdsByPublicKeyHashesResponse extends AbstractResponse {
@@ -13,7 +15,7 @@ class GetIdentityIdsByPublicKeyHashesResponse extends AbstractResponse {
13
15
  }
14
16
 
15
17
  /**
16
- * @returns {Buffer[]}
18
+ * @returns {Array<Buffer[]>}
17
19
  */
18
20
  getIdentityIds() {
19
21
  return this.identityIds;
@@ -27,8 +29,13 @@ class GetIdentityIdsByPublicKeyHashesResponse extends AbstractResponse {
27
29
  const { metadata, proof } = AbstractResponse.createMetadataAndProofFromProto(proto);
28
30
 
29
31
  return new GetIdentityIdsByPublicKeyHashesResponse(
30
- proto.getIdentityIdsList()
31
- .map((identityId) => (identityId.length > 0 ? Buffer.from(identityId) : null)),
32
+ proto.getIdentityIdsList().map((identityIdsSerialized) => {
33
+ const identityIds = cbor.decode(identityIdsSerialized);
34
+
35
+ return identityIds.map((identityId) => (
36
+ (identityId.length > 0 ? Buffer.from(identityId) : null)
37
+ ));
38
+ }),
32
39
  metadata,
33
40
  proof,
34
41
  );
@@ -1,16 +1,12 @@
1
- const StoreTreeProofs = require('./StoreTreeProofs');
2
-
3
1
  class Proof {
4
2
  /**
5
3
  * @param {object} properties
6
- * @param {Buffer} properties.rootTreeProof
7
- * @param {StoreTreeProofs} properties.storeTreeProofs
4
+ * @param {Buffer} properties.merkleProof
8
5
  * @param {Buffer} properties.signatureLLMQHash
9
6
  * @param {Buffer} properties.signature
10
7
  */
11
8
  constructor(properties) {
12
- this.rootTreeProof = properties.rootTreeProof;
13
- this.storeTreeProofs = properties.storeTreeProofs;
9
+ this.merkleProof = properties.merkleProof;
14
10
  this.signatureLLMQHash = properties.signatureLLMQHash;
15
11
  this.signature = properties.signature;
16
12
  }
@@ -18,15 +14,8 @@ class Proof {
18
14
  /**
19
15
  * @returns {Buffer}
20
16
  */
21
- getRootTreeProof() {
22
- return this.rootTreeProof;
23
- }
24
-
25
- /**
26
- * @returns {StoreTreeProofs}
27
- */
28
- getStoreTreeProofs() {
29
- return this.storeTreeProofs;
17
+ getMerkleProof() {
18
+ return this.merkleProof;
30
19
  }
31
20
 
32
21
  /**
@@ -44,27 +33,13 @@ class Proof {
44
33
  }
45
34
 
46
35
  /**
47
- * @param proofProto
36
+ * @param {Object} proofProto
48
37
  *
49
38
  * @returns {Proof}
50
39
  */
51
40
  static createFromProto(proofProto) {
52
- const rawStoreProofs = proofProto.getStoreTreeProofs();
53
-
54
- const storeTreeProofs = new StoreTreeProofs({
55
- dataContractsProof: rawStoreProofs.getDataContractsProof()
56
- ? Buffer.from(rawStoreProofs.getDataContractsProof()) : null,
57
- publicKeyHashesToIdentityIdsProof: rawStoreProofs.getPublicKeyHashesToIdentityIdsProof()
58
- ? Buffer.from(rawStoreProofs.getPublicKeyHashesToIdentityIdsProof()) : null,
59
- identitiesProof: rawStoreProofs.getIdentitiesProof()
60
- ? Buffer.from(rawStoreProofs.getIdentitiesProof()) : null,
61
- documentsProof: rawStoreProofs.getDocumentsProof()
62
- ? Buffer.from(rawStoreProofs.getDocumentsProof()) : null,
63
- });
64
-
65
41
  return new Proof({
66
- rootTreeProof: Buffer.from(proofProto.getRootTreeProof()),
67
- storeTreeProofs,
42
+ merleProof: Buffer.from(proofProto.getMerkleProof()),
68
43
  signatureLLMQHash: Buffer.from(proofProto.getSignatureLlmqHash()),
69
44
  signature: Buffer.from(proofProto.getSignature()),
70
45
  });
@@ -0,0 +1,215 @@
1
+ const { BlockHeader } = require('@dashevo/dashcore-lib');
2
+
3
+ const headers = [
4
+ {
5
+ version: 2,
6
+ prevHash: '00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c',
7
+ merkleRoot: 'b4fd581bc4bfe51a5a66d8b823bd6ee2b492f0ddc44cf7e820550714cedc117f',
8
+ time: 1398712771,
9
+ bits: '1e0fffff',
10
+ nonce: 31475,
11
+ },
12
+ {
13
+ version: 2,
14
+ prevHash: '0000047d24635e347be3aaaeb66c26be94901a2f962feccd4f95090191f208c1',
15
+ merkleRoot: '0d6d332e68eb8ecc66a5baaa95dc4b10c0b32841aed57dc99a5ae0b2f9e4294d',
16
+ time: 1398712772,
17
+ nonce: 6523,
18
+ bits: '1e0ffff0',
19
+ },
20
+ {
21
+ version: 2,
22
+ prevHash: '00000c6264fab4ba2d23990396f42a76aa4822f03cbc7634b79f4dfea36fccc2',
23
+ merkleRoot: '1cc711129405a328c58d1948e748c3b8f3d610e66d9901db88c42c5247829658',
24
+ time: 1398712774,
25
+ nonce: 53194,
26
+ bits: '1e0ffff0',
27
+ },
28
+ {
29
+ version: 2,
30
+ prevHash: '0000057d5c945acbe476bc17bbbaeb2fc1c1b18673e7582c48ac04af61f4d811',
31
+ merkleRoot: '7e6b1b1457308bf6ccb1e325c64607ba7dfac05e26c08887cd28f97d4d4ab3e2',
32
+ time: 1398712782,
33
+ nonce: 193159,
34
+ bits: '1e0ffff0',
35
+ },
36
+ {
37
+ version: 2,
38
+ prevHash: '000002258bd58bf4cdcde282abc030437c103dbb12d2a7dbc978d07bcf386b42',
39
+ merkleRoot: '4cf4f3b788e8dc847a9e0ff3b279340207c555a6bc0736f93e95dbdc2e3c2f16',
40
+ time: 1398712784,
41
+ nonce: 41103,
42
+ bits: '1e0ffff0',
43
+ },
44
+ {
45
+ version: 2,
46
+ prevHash: '00000b1406007f9148c4c173e2314437a315b79b5b79e73ed534a2e8b18a43fc',
47
+ merkleRoot: '99c25e1373c0a5277157b730624d110d04322ffaac49ac0c98cc2d01c80f4c4c',
48
+ time: 1398712787,
49
+ nonce: 62295,
50
+ bits: '1e0ffff0',
51
+ },
52
+ {
53
+ version: 2,
54
+ prevHash: '00000d93de4b31ec550d324876ee7c502a073911972d26c2c990b94d9d5b5a44',
55
+ merkleRoot: '769fc473dc8e5073e4fe3f69f0603972d3d72fbeb62a64de826c64e44260b3f3',
56
+ time: 1398712793,
57
+ nonce: 127755,
58
+ bits: '1e0ffff0',
59
+ },
60
+ {
61
+ version: 2,
62
+ prevHash: '00000154b50ded9d6298dc965e541cef117103c3376256185d0d70e92850853d',
63
+ merkleRoot: '8243e732675d2936bb27917844e714ff5e2012726e3f76e374e7f6236e78669e',
64
+ time: 1398712796,
65
+ nonce: 62099,
66
+ bits: '1e0ffff0',
67
+ },
68
+ {
69
+ version: 2,
70
+ prevHash: '0000089e53a33e48061fa4dbc0a52c40736c2eedddaf563ec25cfe406dd8a541',
71
+ merkleRoot: '243786d6706421ba9ebe9a0d153a5e79d9f781825dbbb0ed26ccc647f10984ee',
72
+ time: 1398712802,
73
+ nonce: 128139,
74
+ bits: '1e0ffff0',
75
+ },
76
+ {
77
+ version: 2,
78
+ prevHash: '00000e98a2b0b1505e3ed00dba28988cd81440de4ec48d817412c2178d332985',
79
+ merkleRoot: 'b158385f441d9299732ec264442d28b6009b8d24388ee7e23a6ce7e6051bfa31',
80
+ time: 1398712803,
81
+ nonce: 23451,
82
+ bits: '1e0ffff0',
83
+ },
84
+ {
85
+ version: 2,
86
+ prevHash: '000001128124cab4756edc387dce2b8762514916d953a4993971a40d78240978',
87
+ merkleRoot: '1f966cdf6ca90f211308554f90f33935cd8e25ab329ffedfa1066b00a581d5d5',
88
+ time: 1398712807,
89
+ nonce: 58648,
90
+ bits: '1e0ffff0',
91
+ },
92
+ {
93
+ version: 2,
94
+ prevHash: '00000ce430de949c85a145b02e33ebbaed3772dc8f3d668f66edc6852c24d002',
95
+ merkleRoot: '663360403b5fba9cd8744c3706f9660c7d3fee4e5a9ee98ce0ad5e5ad7824c1d',
96
+ time: 1398712821,
97
+ nonce: 312363,
98
+ bits: '1e0ffff0',
99
+ },
100
+ {
101
+ version: 2,
102
+ prevHash: '00000ac3a0c9df709260e41290d6902e5a4a073099f11fe8c1ce80aadc4bb331',
103
+ merkleRoot: 'c2ed22a3e6712b842359dfbb6f0a133ae122ffb601e4cf60e30b8c99f9438f4f',
104
+ time: 1398712821,
105
+ nonce: 8325,
106
+ bits: '1e0ffff0',
107
+ },
108
+ {
109
+ version: 2,
110
+ prevHash: '00000b526b34e733532d706c1f4cef93eefe707b87c2c3cb2978e1a84b97c501',
111
+ merkleRoot: '8994e231d4986bbea391d08f67ff9b6da758c86aa2fc9e9816f105462d18cf1b',
112
+ time: 1398712824,
113
+ nonce: 60659,
114
+ bits: '1e0ffff0',
115
+ },
116
+ {
117
+ version: 2,
118
+ prevHash: '00000799cc6963498a215e04402737fc1a08e696a91540756b0697f1ac7b23a8',
119
+ merkleRoot: 'f57a78dd55cb4530df55a8b7ba5aed632b1d3819950e78f90d1acdba5ac595d9',
120
+ time: 1398712825,
121
+ nonce: 18064,
122
+ bits: '1e0ffff0',
123
+ },
124
+ {
125
+ version: 2,
126
+ prevHash: '00000170cc708e9c3b491625dd8dc2d737730a738ebd349f2a770fb26944c04f',
127
+ merkleRoot: '5494a90a580a4f16ff80d2b0c7507d7a565415091d88abeaa752b517da92e06d',
128
+ time: 1398886700,
129
+ nonce: 1555411,
130
+ bits: '1e0fffff',
131
+ },
132
+ {
133
+ version: 2,
134
+ prevHash: '000000ab6ae22728c701e15a13dfa022c88f17a4e0fd71f073b0a720229de22b',
135
+ merkleRoot: '0c03f4a03f47046d1295207ca99cc9db6757a6e4ff313a359d1182cfb8431a84',
136
+ time: 1405697551,
137
+ nonce: 1491503,
138
+ bits: '1e0fffff',
139
+ },
140
+ {
141
+ version: 2,
142
+ confirmations: 86916,
143
+ prevHash: '00000541dfefddc46d4a7ef3873375db4a6f5b1c06a84b67346fb88e07f024d0',
144
+ merkleRoot: '2d6ad1b165c8b7dd4f038d8003d0229259db52e7e828d0d25af38c2f2f7e1990',
145
+ time: 1405697569,
146
+ nonce: 966245,
147
+ bits: '1e0ffff0',
148
+ },
149
+ {
150
+ version: 2,
151
+ prevHash: '000000d275d8dc93e7bf8c358463c97e12be9b2b17d4e64770aca7af8dcb93d8',
152
+ merkleRoot: 'e3552304f4c424ebe5bfc0911b1a3a6fcec15fe788acaf910f45238f16d522f7',
153
+ time: 1405697598,
154
+ nonce: 1661888,
155
+ bits: '1e0ffff0',
156
+ },
157
+ {
158
+ version: 2,
159
+ prevHash: '0000073a769b38812ca2e3a6d4958cab3f21ed3cf4cc80590be3db4b9cb17c8a',
160
+ merkleRoot: 'dd0e01125ffba80e7e7508d57da2725ccc648ed3ea3bac60ee19f81efcf8b934',
161
+ time: 1405697613,
162
+ nonce: 828461,
163
+ bits: '1e0ffff0',
164
+ },
165
+ {
166
+ version: 2,
167
+ prevHash: '00000816a46bd5ea15161c7864b76a263f890d60d4aad6b53cceb8c39ee6b153',
168
+ merkleRoot: '2847d5749c81f8c0b6b91a87a833532611feebb39a833d0e092ec7c8e141467e',
169
+ time: 1405697617,
170
+ nonce: 50599,
171
+ bits: '1e0ffff0',
172
+ },
173
+ {
174
+ version: 2,
175
+ prevHash: '00000976f999bcb32be3d5b7c2e684e8000d8566f22df2612f58b8574536addc',
176
+ merkleRoot: '78d7b2982419345c34391df68ff2d10c0475d5513a6b52f8caaae4f350af1eee',
177
+ time: 1405697628,
178
+ nonce: 613213,
179
+ bits: '1e0ffff0',
180
+ },
181
+ {
182
+ version: 2,
183
+ prevHash: '00000a4f6e00f23dde0b9197d5633b1637c0c22748adadda2d1a799b26115d0d',
184
+ merkleRoot: 'bc8af9a84bc94da13dd144c4e1bff2d8580d33f7613d2e4c9c504b4df5c6d9b4',
185
+ time: 1405697647,
186
+ nonce: 1050462,
187
+ bits: '1e0ffff0',
188
+ },
189
+ {
190
+ version: 2,
191
+ prevHash: '00000ad04d60ac3b1567b499c359b2f901be9826e358e071e5ffcb014d7e60ef',
192
+ merkleRoot: '3cbf4973f43b967773b06169246fbbc80258631280ea84165a1c269b3601943f',
193
+ time: 1405697671,
194
+ nonce: 1381148,
195
+ bits: '1e0ffff0',
196
+ },
197
+ {
198
+ version: 2,
199
+ prevHash: '00000df1e666a530efa28ac37c8cf8b80970c65aa69497fc4fbb3252d2e5f661',
200
+ merkleRoot: '4050c8893b1d882da44d15e481b30e145ed6fe29ffa51ac99dc0e206696aa6dc',
201
+ time: 1405697678,
202
+ nonce: 352597,
203
+ bits: '1e0ffff0',
204
+ },
205
+ ];
206
+
207
+ /**
208
+ * @returns {BlockHeader[]}
209
+ */
210
+ function getHeadersFixture() {
211
+ return headers
212
+ .map((header) => new BlockHeader({ ...header, bits: parseInt(header.bits, 16) }));
213
+ }
214
+
215
+ module.exports = getHeadersFixture;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dashevo/dapi-client",
3
- "version": "0.22.0-dev.4",
3
+ "version": "0.22.0-dev.7.alpha.1",
4
4
  "description": "Client library used to access Dash DAPI endpoints",
5
5
  "main": "lib/DAPIClient.js",
6
6
  "contributors": [
@@ -26,10 +26,11 @@
26
26
  }
27
27
  ],
28
28
  "dependencies": {
29
- "@dashevo/dapi-grpc": "~0.22.0-dev.4",
30
- "@dashevo/dashcore-lib": "~0.19.28",
31
- "@dashevo/dpp": "~0.22.0-dev.4",
32
- "@dashevo/grpc-common": "~0.22.0-dev.4",
29
+ "@dashevo/dapi-grpc": "~0.22.0-dev.7.alpha.1",
30
+ "@dashevo/dash-spv": "github:dashevo/dash-spv",
31
+ "@dashevo/dashcore-lib": "~0.19.30",
32
+ "@dashevo/dpp": "~0.22.0-dev.7.alpha.1",
33
+ "@dashevo/grpc-common": "~0.22.0-dev.7.alpha.1",
33
34
  "@grpc/grpc-js": "^1.3.7",
34
35
  "axios": "^0.21.1",
35
36
  "bs58": "^4.0.1",