@dashevo/dash-spv 0.24.11 → 0.24.12

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/.mocharc.yml ADDED
@@ -0,0 +1,4 @@
1
+ exit: true
2
+ timeout: 3000
3
+ file:
4
+ - ./lib/test/bootstrap.js
package/.ultra.cache.json CHANGED
@@ -1 +1 @@
1
- {"files":{".eslintignore":"5772aab4a649e8c99c5b74bd685792cd6a69f9ac",".eslintrc.json":"41aa1d3c1477f203b588dcb5c0015454edd94481",".gitignore":"095d365a56f423d4fab2345c95c137e7c6ea45c0","LICENSE":"95bf3c7cb642fb61c1bbbb319fd3c83e47a5af96","README.md":"3659fd1bfb44b5e6916679ae586ccbff18683e59","config/config.js":"267c3536b5fe54c88080a56241c34d7393a54ac2","index.js":"4413accedb71ffb73ea7ce4ad45a2035c2ce92ca","lib/blockstore.js":"1ed82a8c1cfb4bc3aa96f0669a27c94d2e76b164","lib/consensus.js":"73201c7fec9c62f5f87dba7b2d0d54c08fa4fa6f","lib/errors/SPVError.js":"08b7af972c146bcdf29ffaa06eb90ca2c5660914","lib/merkleproofs.js":"dca09952c352109795af64f9490209a96e483e2f","lib/spvchain.js":"dfb38386a8c0eb1fee0c64e1b1a950125811f6db","lib/utils.js":"c2119b88a67698053e783f09c038a04ae208f232","package.json":"b16d6e333ecb69fd9b6393adc2f050ca6e016006","test/data/headers.js":"eb9cae85ee11027338fbf1f69e373d22388286d6","test/data/merkleproofs.js":"49ea4c29fb1f0cf6378ed7ee46b0a1db3bb555c1","test/data/rawHeaders.js":"03fed6ca2f8c2d1b63cfa13743431a2d551c7ad9","test/index.js":"e937184e5ee21ba882e09fcd5e5f5228d9b7ef68","test/spvchain.js":"7e6c93b2e623a3ddb93e9f250133e83f28c882e2"},"deps":{}}
1
+ {"files":{".eslintignore":"5772aab4a649e8c99c5b74bd685792cd6a69f9ac",".eslintrc.json":"41aa1d3c1477f203b588dcb5c0015454edd94481",".gitignore":"095d365a56f423d4fab2345c95c137e7c6ea45c0",".mocharc.yml":"4b2c6eac5413c431a990afeec68f34c8398891ad","LICENSE":"95bf3c7cb642fb61c1bbbb319fd3c83e47a5af96","README.md":"3659fd1bfb44b5e6916679ae586ccbff18683e59","config/config.js":"267c3536b5fe54c88080a56241c34d7393a54ac2","index.js":"4413accedb71ffb73ea7ce4ad45a2035c2ce92ca","lib/blockstore.js":"1ed82a8c1cfb4bc3aa96f0669a27c94d2e76b164","lib/consensus.js":"73201c7fec9c62f5f87dba7b2d0d54c08fa4fa6f","lib/errors/SPVError.js":"08b7af972c146bcdf29ffaa06eb90ca2c5660914","lib/merkleproofs.js":"dca09952c352109795af64f9490209a96e483e2f","lib/spvchain.js":"167c1fecd83df5ae597c7af87471ec972c6b782e","lib/test/.eslintrc":"4c2b11fe8177c0c2fd0667acb599fc44c4c9741a","lib/test/bootstrap.js":"53142f9ec29cd1e75714de8a0be0080cf3bbda04","lib/utils.js":"c2119b88a67698053e783f09c038a04ae208f232","lib/x11.js":"d85e555bad579a8b3e2e92447331933ce9b0c4b1","package.json":"5157625ed2e10b6b204aff6591bf2957bd693786","test/data/headers.js":"eb9cae85ee11027338fbf1f69e373d22388286d6","test/data/merkleproofs.js":"49ea4c29fb1f0cf6378ed7ee46b0a1db3bb555c1","test/data/rawHeaders.js":"03fed6ca2f8c2d1b63cfa13743431a2d551c7ad9","test/index.js":"4b67c838fadf0baac0207b13ddd99b0bc8d228c0","test/spvchain.js":"58a7716c91830e0a23bd27ab857cbba70b8bee93"},"deps":{}}
package/lib/spvchain.js CHANGED
@@ -1,26 +1,50 @@
1
+ const X11 = require('./x11');
2
+
1
3
  const config = require('../config/config');
2
4
  const Consensus = require('./consensus');
3
5
  const utils = require('./utils');
4
6
  const SPVError = require('./errors/SPVError');
5
7
 
6
- const SpvChain = class {
7
- // TODO: move chainType and confirms to `options`
8
- constructor(chainType, confirms = 100, startBlock, startBlockHeight) {
9
- this.confirmsBeforeFinal = confirms;
8
+ const CHAIN_TYPE_NETWORKS = {
9
+ testnet: 'testnet',
10
+ devnet: 'devnet',
11
+ local: 'regtest',
12
+ regtest: 'regtest',
13
+ lowdiff: 'regtest',
14
+ livenet: 'mainnet',
15
+ mainnet: 'mainnet',
16
+ };
10
17
 
11
- this.reset(startBlockHeight);
12
- this.init(chainType, startBlock);
18
+ const createGenesis = (chainType) => {
19
+ switch (chainType) {
20
+ case 'testnet': return config.getTestnetGenesis();
21
+ case 'devnet': return config.getDevnetGenesis();
22
+ case 'local':
23
+ case 'regtest': return config.getRegtestGenesis();
24
+ case 'livenet':
25
+ case 'mainnet': return config.getLivenetGenesis();
26
+ case 'lowdiff': return config.getLowDiffGenesis();
27
+ default: throw new SPVError(`Unsupported chain type ${chainType}`);
28
+ }
29
+ };
13
30
 
14
- this.hashesByHeight = new Map([[this.startBlockHeight, this.root.hash]]);
31
+ const SpvChain = class {
32
+ constructor(chainType, confirms = 100) {
33
+ this.confirmsBeforeFinal = confirms;
34
+ this.network = CHAIN_TYPE_NETWORKS[chainType];
35
+ if (!this.network) {
36
+ throw new SPVError(`Unsupported chain type "${chainType}"`);
37
+ }
38
+ this.genesis = createGenesis(chainType);
15
39
 
16
- this.heightByHash = new Map([[this.root.hash, this.startBlockHeight]]);
40
+ this.reset();
41
+ }
17
42
 
18
- // TODO: legacy - remove this
19
- this.orphanBlocks = [];
43
+ static async wasmX11Ready() {
44
+ return X11.load();
20
45
  }
21
46
 
22
- reset(fromBlockHeight = 0) {
23
- this.root = null;
47
+ reset() {
24
48
  this.allBranches = [[]];
25
49
  this.orphanChunks = [];
26
50
  this.prunedHeaders = [];
@@ -28,71 +52,52 @@ const SpvChain = class {
28
52
  this.hashesByHeight = new Map();
29
53
  this.heightByHash = new Map();
30
54
  this.orphansHashes = new Set();
31
- this.startBlockHeight = fromBlockHeight < 0 ? 0 : fromBlockHeight;
55
+ // Height of the first header in the chain.
56
+ // 0 - if we are synchronizing from genesis
57
+ // > 0 if part of the chain is pruned and we are synchronizing from checkpoint where
58
+ this.startBlockHeight = null;
59
+
60
+ // Head of pending first header.
61
+ // Used in situations where we don't know root in advance
62
+ this.pendingStartBlockHeight = null;
32
63
  }
33
64
 
34
- init(chainType, startBlock) {
35
- switch (chainType) {
36
- case 'testnet':
37
- this.network = 'testnet';
38
- if (startBlock) {
39
- this.root = startBlock;
40
- break;
41
- }
42
- this.root = config.getTestnetGenesis();
43
- break;
44
- case 'devnet':
45
- this.network = 'devnet';
46
- if (startBlock) {
47
- this.root = startBlock;
48
- break;
49
- }
50
- this.root = config.getDevnetGenesis();
51
- break;
52
- case 'local':
53
- case 'regtest':
54
- this.network = 'regtest';
55
- if (startBlock) {
56
- this.root = startBlock;
57
- break;
58
- }
59
- this.root = config.getRegtestGenesis();
60
- break;
61
- case 'livenet':
62
- this.network = 'mainnet';
63
- if (startBlock) {
64
- this.root = startBlock;
65
- break;
66
- }
67
- this.root = config.getLivenetGenesis();
68
- break;
69
- case 'mainnet':
70
- this.network = 'mainnet';
71
- if (startBlock) {
72
- this.root = startBlock;
73
- break;
74
- }
75
- this.root = config.getLivenetGenesis();
76
- break;
77
- case 'lowdiff':
78
- this.network = 'regtest';
79
- if (startBlock) {
80
- this.root = startBlock;
81
- break;
82
- }
83
- this.root = config.getLowDiffGenesis();
84
- break;
85
- default:
86
- if (startBlock) {
87
- this.root = startBlock;
88
- this.network = 'mainnet';
89
- } else {
90
- throw new SPVError('Unhandled chaintype or startBlock not provided');
91
- }
92
- break;
65
+ /**
66
+ * Initializes chain with a start block and height
67
+ * - When no arguments provided, chain is initialized from genesis block
68
+ * - If startBlock is provided, height must be provided as well
69
+ * @param {BlockHeader} [startBlock]
70
+ * @param {number} [height]
71
+ */
72
+ initialize(startBlock, height) {
73
+ if (!X11.ready()) {
74
+ throw new SPVError('X11 wasm not ready, call wasmX11Ready() first');
75
+ }
76
+
77
+ if (this.startBlockHeight !== null) {
78
+ throw new SPVError('Chain already initialized');
79
+ }
80
+
81
+ if (startBlock && typeof height === 'number') {
82
+ // eslint-disable-next-line
83
+ startBlock = utils.normalizeHeader(startBlock);
84
+ } else if (!startBlock && typeof height !== 'number') {
85
+ // eslint-disable-next-line
86
+ startBlock = this.genesis;
87
+ // eslint-disable-next-line
88
+ height = 0;
89
+ } else {
90
+ throw new SPVError('Initialization error, please provide both startBlock and height');
93
91
  }
94
- // this.root.children = [];
95
- this.setAllBranches();
92
+
93
+ this.startBlockHeight = height;
94
+ this.hashesByHeight = new Map([[height, startBlock.hash]]);
95
+ this.heightByHash = new Map([[startBlock.hash, height]]);
96
+ this.setAllBranches(startBlock);
97
+ }
98
+
99
+ initialized() {
100
+ return this.startBlockHeight !== null || this.pendingStartBlockHeight !== null;
96
101
  }
97
102
 
98
103
  validate() {
@@ -146,30 +151,15 @@ const SpvChain = class {
146
151
  }
147
152
 
148
153
  /** @private */
149
- findConnection(newHeader) {
150
- const stack = [this.root];
151
- while (stack.length > 0) {
152
- const node = stack.pop();
153
- if (node.hash === utils.getCorrectedHash(newHeader.prevHash)) {
154
- return node;
155
- }
156
- // node.children.forEach((c) => { stack.push(c); });
154
+ setAllBranches(node, branch = []) {
155
+ if (!node) {
156
+ throw new SPVError('Root node for a branch is not defined');
157
157
  }
158
- return null;
159
- }
160
158
 
161
- /** @private */
162
- setAllBranches(node = this.root, branch = []) {
163
159
  this.allBranches = [];
164
160
  branch.push(node);
165
161
 
166
- // node.children.forEach((c) => {
167
- // this.setAllBranches(c, Array.from(branch));
168
- // });
169
-
170
- // if (node.children.length === 0) {
171
162
  this.allBranches.push(branch);
172
- // }
173
163
  }
174
164
 
175
165
  /** @private */
@@ -200,17 +190,6 @@ const SpvChain = class {
200
190
  return this.heightByHash.has(hash) || this.orphansHashes.has(hash);
201
191
  }
202
192
 
203
- /** @private */
204
- // orphanReconnect() {
205
- // for (let i = 0; i < this.orphanBlocks.length; i += 1) {
206
- // const connectionTip = this.findConnection(this.orphanBlocks[i]);
207
- // if (connectionTip) {
208
- // connectionTip.children.push(this.orphanBlocks[i]);
209
- // this.orphanBlocks.splice(i, 1);
210
- // }
211
- // }
212
- // }
213
-
214
193
  /** @private */
215
194
  orphanChunksReconnect() {
216
195
  // TODO: consider optimizing with map of { [chunkHeadHash]: chunkIndex }
@@ -233,28 +212,11 @@ const SpvChain = class {
233
212
  }
234
213
  }
235
214
 
236
- /** @private */
237
- // TODO: remove
238
- // getOrphans() {
239
- // return this.orphanBlocks;
240
- // }
241
-
242
215
  /** @private */
243
216
  getOrphanChunks() {
244
217
  return this.orphanChunks;
245
218
  }
246
219
 
247
- /** @private */
248
- // processValidHeader(header) {
249
- // const connection = this.findConnection(header);
250
- // if (connection) {
251
- // // connection.children.push(header);
252
- // this.orphanReconnect();
253
- // } else {
254
- // this.orphanBlocks.push(header);
255
- // }
256
- // }
257
-
258
220
  /** @private
259
221
  * validates a dashcore.BlockHeader object
260
222
  *
@@ -281,16 +243,9 @@ const SpvChain = class {
281
243
  if (utils.getCorrectedHash(header.prevHash) !== previousHeader.hash) {
282
244
  return false;
283
245
  }
284
- // if (!header.children) {
285
- // header.children = [];
286
- // }
287
- // if (!previousHeader.children) {
288
- // previousHeader.children = [];
289
- // }
290
- // previousHeader.children.push(header);
246
+
291
247
  return true;
292
248
  }
293
- /* eslint-enable no-param-reassign */
294
249
 
295
250
  /**
296
251
  * Returns the longest chain of headers
@@ -337,44 +292,6 @@ const SpvChain = class {
337
292
  return this.getLongestChain().filter((h) => h.hash === hash)[0];
338
293
  }
339
294
 
340
- /**
341
- * Gets specified amount of headers in the confirmed chain
342
- * @param n
343
- * @return Object[]
344
- */
345
- getLastHeaders(n) {
346
- const longestChain = this.getLongestChain();
347
- let headers = longestChain.slice(-n);
348
-
349
- if (headers.length < n) {
350
- const remaining = n - headers.length;
351
- headers = [...this.prunedHeaders.slice(-remaining), ...headers];
352
- }
353
-
354
- return headers;
355
- }
356
-
357
- /**
358
- * adds a valid header to the tip of the longest spv chain.
359
- * If it cannot be connected to the tip it gets temporarily
360
- * added to an orphan array for possible later reconnection
361
- *
362
- * @param {Object[]|string[]|buffer[]} header
363
- * @return {boolean}
364
- */
365
- // addHeader(header) {
366
- // const headerNormalised = utils.normalizeHeader(header);
367
- //
368
- // if (this.isValid(headerNormalised, this.getLongestChain())) {
369
- // // headerNormalised.children = [];
370
- // this.processValidHeader(headerNormalised);
371
- // this.setAllBranches();
372
- // this.checkPruneBlocks();
373
- // return true;
374
- // }
375
- // return false;
376
- // }
377
-
378
295
  /**
379
296
  * adds an array of valid headers to the longest spv chain.
380
297
  * If they cannot be connected to last tip they get temporarily
@@ -385,39 +302,45 @@ const SpvChain = class {
385
302
  * @return {BlockHeader[]}
386
303
  */
387
304
  addHeaders(headers, batchHeadHeight = 0) {
388
- let headHeight = batchHeadHeight;
389
- const normalizedHeaders = headers.map((h) => utils.normalizeHeader(h));
390
-
391
- const tip = this.getTipHeader();
392
- // Handle 1 block intersection of batches
393
- if (tip && tip.hash === normalizedHeaders[0].hash) {
394
- normalizedHeaders.splice(0, 1);
395
- // Patch head height value after splice
396
- headHeight += 1;
305
+ if (!X11.ready()) {
306
+ throw new SPVError('X11 wasm not ready, call wasmX11Ready() first');
397
307
  }
398
308
 
399
- if (normalizedHeaders.length === 0) {
400
- // The batch already in the chain, do nothing
401
- return [];
309
+ if (!this.initialized()) {
310
+ throw new SPVError('Chain not initialized, either call initialize() or set pendingStartBlockHeight');
402
311
  }
403
312
 
404
- const firstHeader = normalizedHeaders[0];
405
- const connectsToTip = tip && SpvChain.isParentChild(firstHeader, tip);
313
+ const normalizedHeaders = headers.map((h) => utils.normalizeHeader(h));
406
314
 
407
- //
408
- // Reorg detection
409
- // Get prev header hash
410
- const prevHash = utils.getCorrectedHash(firstHeader.prevHash);
411
- const prevHeaderHeight = this.heightByHash.get(prevHash);
315
+ let isOrphan = false;
412
316
 
413
- // Test on initial wallet load
414
- if (prevHeaderHeight && prevHeaderHeight > 0 && prevHeaderHeight !== headHeight - 1) {
415
- console.log('SPVCHAIN: Reorg detected.');
416
- console.log(`------->: Batch head ${firstHeader.hash} at height ${headHeight} has parent at height ${prevHeaderHeight}`);
417
- }
317
+ // Chain is initialized, root block, and it's height are known
318
+ if (this.pendingStartBlockHeight === null) {
319
+ const tip = this.getTipHeader();
320
+ // Handle 1 block intersection of batches
321
+ if (tip.hash === normalizedHeaders[0].hash) {
322
+ normalizedHeaders.splice(0, 1);
323
+ }
418
324
 
419
- const isOrphan = tip ? !connectsToTip
420
- : headHeight !== this.startBlockHeight;
325
+ if (normalizedHeaders.length === 0) {
326
+ // The batch already in the chain, do nothing
327
+ return [];
328
+ }
329
+
330
+ const firstHeader = normalizedHeaders[0];
331
+
332
+ isOrphan = !SpvChain.isParentChild(firstHeader, tip);
333
+ } else if (batchHeadHeight === this.pendingStartBlockHeight) {
334
+ // Header at pendingStartBlockHeight is found, initialize chain
335
+ this.startBlockHeight = this.pendingStartBlockHeight;
336
+ this.pendingStartBlockHeight = null;
337
+ isOrphan = false;
338
+ } else if (batchHeadHeight > this.pendingStartBlockHeight) {
339
+ // Orphan chunk has arrived
340
+ isOrphan = true;
341
+ } else {
342
+ throw new SPVError(`Batch at invalid height arrived: ${batchHeadHeight}, expected > ${this.pendingStartBlockHeight}`);
343
+ }
421
344
 
422
345
  const allValid = normalizedHeaders.reduce(
423
346
  (acc, header, index, array) => {
@@ -0,0 +1,9 @@
1
+ {
2
+ "env": {
3
+ "node": true,
4
+ "mocha": true
5
+ },
6
+ "rules": {
7
+ "import/no-extraneous-dependencies": "off"
8
+ }
9
+ }
@@ -0,0 +1,19 @@
1
+ const sinon = require('sinon');
2
+
3
+ beforeEach(function beforeEach() {
4
+ if (!this.sinon) {
5
+ this.sinon = sinon.createSandbox();
6
+ } else {
7
+ this.sinon.restore();
8
+ }
9
+ });
10
+
11
+ before(function before() {
12
+ if (!this.sinon) {
13
+ this.sinon = sinon.createSandbox();
14
+ }
15
+ });
16
+
17
+ afterEach(function afterEach() {
18
+ this.sinon.restore();
19
+ });
package/lib/x11.js ADDED
@@ -0,0 +1,27 @@
1
+ const { configure: configureDashcore } = require('@dashevo/dashcore-lib');
2
+ const X11 = require('wasm-x11-hash');
3
+
4
+ let x11Promise = null;
5
+ let x11Ready = false;
6
+
7
+ const load = async () => {
8
+ if (!x11Promise) {
9
+ x11Promise = X11().then(
10
+ (x11Hash) => {
11
+ configureDashcore({
12
+ x11hash: x11Hash,
13
+ });
14
+ x11Ready = true;
15
+ },
16
+ );
17
+ }
18
+
19
+ return x11Promise;
20
+ };
21
+
22
+ const ready = () => x11Ready;
23
+
24
+ module.exports = {
25
+ ready,
26
+ load,
27
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dashevo/dash-spv",
3
- "version": "0.24.11",
3
+ "version": "0.24.12",
4
4
  "description": "Repository containing SPV functions used by @dashevo",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -16,7 +16,8 @@
16
16
  "@dashevo/dash-util": "^2.0.3",
17
17
  "@dashevo/dashcore-lib": "~0.20.9",
18
18
  "levelup": "^4.4.0",
19
- "memdown": "^5.1.0"
19
+ "memdown": "^5.1.0",
20
+ "wasm-x11-hash": "~0.0.2"
20
21
  },
21
22
  "devDependencies": {
22
23
  "chai": "^4.3.4",
@@ -24,6 +25,7 @@
24
25
  "eslint-config-airbnb-base": "^14.2.1",
25
26
  "eslint-plugin-import": "^2.24.2",
26
27
  "mocha": "^9.1.2",
27
- "should": "^13.2.3"
28
+ "should": "^13.2.3",
29
+ "sinon": "^11.1.2"
28
30
  }
29
31
  }
package/test/index.js CHANGED
@@ -16,8 +16,10 @@ let merkleBlock = null;
16
16
  let merkleBlock2 = null;
17
17
 
18
18
  describe('SPV-DASH (forks & re-orgs) deserialized headers', () => {
19
- before(() => {
19
+ before(async () => {
20
20
  chain = new Blockchain('testnet');
21
+ await Blockchain.wasmX11Ready();
22
+ chain.initialize();
21
23
  });
22
24
 
23
25
  it('should get 26 testnet headers', () => {
@@ -83,8 +85,9 @@ describe('SPV-DASH (forks & re-orgs) deserialized headers', () => {
83
85
  });
84
86
 
85
87
  describe('SPV-DASH (forks & re-orgs) serialized raw headers for mainnet', () => {
86
- before(() => {
87
- chain = new Blockchain('mainnet', 10000, utils.normalizeHeader(mainnet[0]));
88
+ before(async () => {
89
+ chain = new Blockchain('mainnet', 10000);
90
+ chain.initialize(utils.normalizeHeader(mainnet[0]), 1);
88
91
  });
89
92
 
90
93
  it('should get 2000 mainnet headers', () => {
@@ -127,8 +130,9 @@ describe('SPV-DASH (forks & re-orgs) serialized raw headers for mainnet', () =>
127
130
  });
128
131
 
129
132
  describe('SPV-DASH (addHeaders) add many headers for testnet', () => {
130
- before(() => {
131
- chain = new Blockchain('testnet', 10000, utils.normalizeHeader(testnet[0]));
133
+ before(async () => {
134
+ chain = new Blockchain('testnet', 10000);
135
+ chain.initialize(utils.normalizeHeader(testnet[0]), 1);
132
136
  });
133
137
 
134
138
  it('should add the 1st 250 testnet headers', () => {
@@ -146,7 +150,8 @@ describe('SPV-DASH (addHeaders) add many headers for testnet', () => {
146
150
  });
147
151
 
148
152
  it('should add the 1st 250 testnet2 headers', () => {
149
- chain = new Blockchain('testnet', 10000, utils.normalizeHeader(testnet2[0]));
153
+ chain = new Blockchain('testnet', 10000);
154
+ chain.initialize(utils.normalizeHeader(testnet2[0]), 1);
150
155
  chain.addHeaders(testnet2.slice(1, 250));
151
156
  chain.getOrphanChunks().length.should.equal(0);
152
157
  chain.getAllBranches().length.should.equal(1);
@@ -161,7 +166,8 @@ describe('SPV-DASH (addHeaders) add many headers for testnet', () => {
161
166
  });
162
167
 
163
168
  it('should add the 1st 250 testnet3 headers', () => {
164
- chain = new Blockchain('testnet', 10000, utils.normalizeHeader(testnet3[0]));
169
+ chain = new Blockchain('testnet', 10000);
170
+ chain.initialize(utils.normalizeHeader(testnet3[0]), 1);
165
171
  chain.addHeaders(testnet3.slice(1, 250));
166
172
  chain.getOrphanChunks().length.should.equal(0);
167
173
  chain.getAllBranches().length.should.equal(1);
@@ -199,7 +205,8 @@ describe('SPV-DASH (addHeaders) add many headers for testnet', () => {
199
205
 
200
206
  describe('SPV-DASH (addHeaders) add testnet headers out of order', () => {
201
207
  before(() => {
202
- chain = new Blockchain('testnet', 10000, utils.normalizeHeader(testnet[0]));
208
+ chain = new Blockchain('testnet', 10000);
209
+ chain.initialize(utils.normalizeHeader(testnet[0]), 1);
203
210
  });
204
211
 
205
212
  it('should add the 1st 100 testnet headers', () => {
@@ -242,7 +249,8 @@ describe('SPV-DASH (addHeaders) add testnet headers out of order', () => {
242
249
 
243
250
  describe('SPV-DASH (addHeaders) add many headers for mainnet', () => {
244
251
  before(() => {
245
- chain = new Blockchain('mainnet', 10000, utils.normalizeHeader(mainnet[0]));
252
+ chain = new Blockchain('mainnet', 10000);
253
+ chain.initialize(utils.normalizeHeader(mainnet[0]), 1);
246
254
  });
247
255
 
248
256
  it('should add the 1st 500 mainnet headers', () => {
@@ -290,7 +298,8 @@ describe('SPV-DASH (addHeaders) add many headers for mainnet', () => {
290
298
 
291
299
  describe('SPV-DASH (addHeaders) add mainnet headers out of order', () => {
292
300
  before(() => {
293
- chain = new Blockchain('mainnet', 10000, utils.normalizeHeader(mainnet[0]));
301
+ chain = new Blockchain('mainnet', 10000);
302
+ chain.initialize(utils.normalizeHeader(mainnet[0]), 1);
294
303
  });
295
304
 
296
305
  it('should add the 1st 100 mainnet headers', () => {
@@ -377,7 +386,8 @@ describe('MerkleProofs', () => {
377
386
 
378
387
  describe('Transaction validation', () => {
379
388
  before(() => {
380
- chain = new Blockchain('testnet', 10000, utils.normalizeHeader(testnet[0]));
389
+ chain = new Blockchain('testnet', 10000);
390
+ chain.initialize(utils.normalizeHeader(testnet[0]), 1);
381
391
  chain.addHeaders(testnet.slice(1, 500));
382
392
  });
383
393
 
package/test/spvchain.js CHANGED
@@ -1,19 +1,65 @@
1
1
  const { expect } = require('chai');
2
+ const X11 = require('../lib/x11');
2
3
  const { SpvChain } = require('../index');
3
-
4
4
  const { testnet } = require('./data/rawHeaders');
5
5
 
6
6
  describe('SPVChain', () => {
7
7
  let spvChain;
8
8
 
9
- beforeEach(() => {
9
+ beforeEach(async () => {
10
10
  spvChain = new SpvChain('testnet', 100);
11
+ await SpvChain.wasmX11Ready();
12
+ });
13
+
14
+ describe('#initialize', () => {
15
+ it('should throw an error if wasmX11 is not initialized', function it() {
16
+ this.sinon.stub(X11, 'ready').returns(false);
17
+ expect(() => spvChain.initialize())
18
+ .to.throw('X11 wasm not ready, call wasmX11Ready() first');
19
+ });
20
+
21
+ it('should throw an error if chain is already initialized', async () => {
22
+ spvChain.initialize(testnet[0], 10000);
23
+ expect(() => spvChain.initialize())
24
+ .to.throw('Chain already initialized');
25
+ });
26
+
27
+ it('should initialize chain with genesis header', async () => {
28
+ spvChain.initialize();
29
+ expect(spvChain.startBlockHeight).to.be.equal(0);
30
+ expect(spvChain.getLongestChain())
31
+ .to.be.deep.equal([spvChain.genesis]);
32
+ });
33
+
34
+ it('should not allow initializing from arbitrary block without height', () => {
35
+ expect(() => spvChain.initialize(testnet[0]))
36
+ .to.throw('Initialization error, please provide both startBlock and height');
37
+ });
38
+
39
+ it('should initialize from arbitrary block', () => {
40
+ spvChain.initialize(testnet[0], 10000);
41
+ expect(spvChain.startBlockHeight).to.be.equal(10000);
42
+ expect(spvChain.getLongestChain()[0].toBuffer().toString('hex'))
43
+ .to.be.equal(testnet[0]);
44
+ });
11
45
  });
12
46
 
13
47
  describe('#addHeaders', () => {
14
- it('should assemble headers chain if headers arriving out of order', () => {
48
+ it('should throw an error if wasmX11 is not initialized', function it() {
49
+ this.sinon.stub(X11, 'ready').returns(false);
50
+ expect(() => spvChain.addHeaders(testnet.slice(400)))
51
+ .to.throw('X11 wasm not ready, call wasmX11Ready() first');
52
+ });
53
+
54
+ it('should throw an error if chain is not initialized', async () => {
55
+ expect(() => spvChain.addHeaders(testnet.slice(400)))
56
+ .to.throw('Chain not initialized, either call initialize() or set pendingStartBlockHeight');
57
+ });
58
+
59
+ it('should assemble headers chain if headers arriving out of order', async () => {
15
60
  expect(true).to.equal(true);
16
- spvChain.reset(10000);
61
+
62
+ spvChain.initialize(testnet[0], 10000);
17
63
 
18
64
  spvChain.addHeaders(testnet.slice(400), 10400);
19
65
  expect(spvChain.getOrphanChunks()).to.have.length(1);