@dashevo/dapi-client 0.22.0-dev.5 → 0.22.0-dev.7.alpha.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/lib/BlockHeadersProvider/BlockHeadersProvider.js +121 -0
- package/lib/BlockHeadersProvider/BlockHeadersReader.js +229 -0
- package/lib/BlockHeadersProvider/createBlockHeadersProviderFromOptions.js +77 -0
- package/lib/BlockHeadersProvider/interfaces/BlockHeadersReaderInterface.js +25 -0
- package/lib/DAPIClient.js +36 -1
- package/lib/methods/core/CoreMethodsFacade.js +4 -0
- package/lib/methods/core/subscribeToBlockHeadersWithChainLocksFactory.js +69 -0
- package/lib/methods/platform/response/Proof.js +6 -31
- package/lib/test/fixtures/getHeadersFixture.js +215 -0
- package/package.json +6 -5
|
@@ -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
|
-
|
|
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,16 +1,12 @@
|
|
|
1
|
-
const StoreTreeProofs = require('./StoreTreeProofs');
|
|
2
|
-
|
|
3
1
|
class Proof {
|
|
4
2
|
/**
|
|
5
3
|
* @param {object} properties
|
|
6
|
-
* @param {Buffer} properties.
|
|
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.
|
|
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
|
-
|
|
22
|
-
return this.
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.22.0-dev.7.alpha.2",
|
|
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.
|
|
30
|
-
"@dashevo/
|
|
31
|
-
"@dashevo/
|
|
32
|
-
"@dashevo/
|
|
29
|
+
"@dashevo/dapi-grpc": "~0.22.0-dev.7.alpha.2",
|
|
30
|
+
"@dashevo/dash-spv": "github:dashevo/dash-spv",
|
|
31
|
+
"@dashevo/dashcore-lib": "~0.19.30",
|
|
32
|
+
"@dashevo/dpp": "~0.22.0-dev.7.alpha.2",
|
|
33
|
+
"@dashevo/grpc-common": "~0.22.0-dev.7.alpha.2",
|
|
33
34
|
"@grpc/grpc-js": "^1.3.7",
|
|
34
35
|
"axios": "^0.21.1",
|
|
35
36
|
"bs58": "^4.0.1",
|