hypercore 10.0.0-alpha.24 → 10.0.0-alpha.27

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
@@ -101,6 +101,10 @@ Truncate the core to a smaller length.
101
101
  Per default this will update the fork id of the core to `+ 1`, but you can set the fork id you prefer with the option.
102
102
  Note that the fork id should be monotonely incrementing.
103
103
 
104
+ #### `const hash = await core.treeHash([length])`
105
+
106
+ Get the Merkle Tree hash of the core at a given length, defaulting to the current length of the core.
107
+
104
108
  #### `const stream = core.createReadStream([options])`
105
109
 
106
110
  Make a read stream. Options include:
package/index.js CHANGED
@@ -6,12 +6,12 @@ const c = require('compact-encoding')
6
6
  const b4a = require('b4a')
7
7
  const Xache = require('xache')
8
8
  const NoiseSecretStream = require('@hyperswarm/secret-stream')
9
+ const Protomux = require('protomux')
9
10
  const codecs = require('codecs')
10
11
 
11
12
  const fsctl = requireMaybe('fsctl') || { lock: noop, sparse: noop }
12
13
 
13
14
  const Replicator = require('./lib/replicator')
14
- const Extensions = require('./lib/extensions')
15
15
  const Core = require('./lib/core')
16
16
  const BlockEncryption = require('./lib/block-encryption')
17
17
  const { ReadStream, WriteStream } = require('./lib/streams')
@@ -51,15 +51,15 @@ module.exports = class Hypercore extends EventEmitter {
51
51
  this.core = null
52
52
  this.replicator = null
53
53
  this.encryption = null
54
- this.extensions = opts.extensions || new Extensions()
54
+ this.extensions = opts.extensions || new Map()
55
55
  this.cache = opts.cache === true ? new Xache({ maxSize: 65536, maxAge: 0 }) : (opts.cache || null)
56
56
 
57
57
  this.valueEncoding = null
58
58
  this.encodeBatch = null
59
+ this.activeRequests = []
59
60
 
60
61
  this.key = key || null
61
62
  this.keyPair = null
62
- this.discoveryKey = null
63
63
  this.readable = true
64
64
  this.writable = false
65
65
  this.opened = false
@@ -73,6 +73,7 @@ module.exports = class Hypercore extends EventEmitter {
73
73
  this.opening.catch(noop)
74
74
 
75
75
  this._preappend = preappend.bind(this)
76
+ this._snapshot = opts.snapshot || null
76
77
  }
77
78
 
78
79
  [inspect] (depth, opts) {
@@ -93,10 +94,17 @@ module.exports = class Hypercore extends EventEmitter {
93
94
  indent + ')'
94
95
  }
95
96
 
97
+ static getProtocolMuxer (stream) {
98
+ return stream.noiseStream.userData
99
+ }
100
+
96
101
  static createProtocolStream (isInitiator, opts = {}) {
97
- let outerStream = isStream(isInitiator)
98
- ? isInitiator
99
- : opts.stream
102
+ let outerStream = Protomux.isProtomux(isInitiator)
103
+ ? isInitiator.stream
104
+ : isStream(isInitiator)
105
+ ? isInitiator
106
+ : opts.stream
107
+
100
108
  let noiseStream = null
101
109
 
102
110
  if (outerStream) {
@@ -108,10 +116,16 @@ module.exports = class Hypercore extends EventEmitter {
108
116
  if (!noiseStream) throw new Error('Invalid stream')
109
117
 
110
118
  if (!noiseStream.userData) {
111
- const protocol = Replicator.createProtocol(noiseStream, opts)
112
- if (opts.keepAlive !== false) protocol.setKeepAlive(true)
119
+ const protocol = new Protomux(noiseStream)
120
+
121
+ if (opts.ondiscoverykey) {
122
+ protocol.pair({ protocol: 'hypercore/alpha' }, opts.ondiscoverykey)
123
+ }
124
+ if (opts.keepAlive !== false) {
125
+ noiseStream.setKeepAlive(5000)
126
+ noiseStream.setTimeout(7000)
127
+ }
113
128
  noiseStream.userData = protocol
114
- noiseStream.on('error', noop) // All noise errors already propagate through outerStream
115
129
  }
116
130
 
117
131
  return outerStream
@@ -129,6 +143,10 @@ module.exports = class Hypercore extends EventEmitter {
129
143
  }
130
144
  }
131
145
 
146
+ snapshot () {
147
+ return this.session({ snapshot: { length: this.length, byteLength: this.byteLength, fork: this.fork } })
148
+ }
149
+
132
150
  session (opts = {}) {
133
151
  if (this.closing) {
134
152
  // This makes the closing logic alot easier. If this turns out to be a problem
@@ -154,7 +172,6 @@ module.exports = class Hypercore extends EventEmitter {
154
172
  if (!this.sign) this.sign = o.sign
155
173
  this.crypto = o.crypto
156
174
  this.key = o.key
157
- this.discoveryKey = o.discoveryKey
158
175
  this.core = o.core
159
176
  this.replicator = o.replicator
160
177
  this.encryption = o.encryption
@@ -232,10 +249,12 @@ module.exports = class Hypercore extends EventEmitter {
232
249
  this.storage = Hypercore.defaultStorage(opts.storage || storage)
233
250
 
234
251
  this.core = await Core.open(this.storage, {
252
+ force: opts.force,
235
253
  createIfMissing: opts.createIfMissing,
236
254
  overwrite: opts.overwrite,
237
255
  keyPair,
238
256
  crypto: this.crypto,
257
+ legacy: opts.legacy,
239
258
  onupdate: this._oncoreupdate.bind(this)
240
259
  })
241
260
 
@@ -245,20 +264,19 @@ module.exports = class Hypercore extends EventEmitter {
245
264
  }
246
265
  }
247
266
 
248
- this.replicator = new Replicator(this.core, {
249
- onupdate: this._onpeerupdate.bind(this),
250
- onupload: this._onupload.bind(this)
251
- })
252
-
253
- this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
254
267
  this.key = this.core.header.signer.publicKey
255
268
  this.keyPair = this.core.header.signer
256
269
 
270
+ this.replicator = new Replicator(this.core, this.key, {
271
+ eagerUpdate: true,
272
+ allowFork: opts.allowFork !== false,
273
+ onpeerupdate: this._onpeerupdate.bind(this),
274
+ onupload: this._onupload.bind(this)
275
+ })
276
+
257
277
  if (!this.encryption && opts.encryptionKey) {
258
278
  this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
259
279
  }
260
-
261
- this.extensions.attach(this.replicator)
262
280
  }
263
281
 
264
282
  close () {
@@ -277,6 +295,17 @@ module.exports = class Hypercore extends EventEmitter {
277
295
  this.readable = false
278
296
  this.writable = false
279
297
  this.closed = true
298
+ this.opened = false
299
+
300
+ const gc = []
301
+ for (const ext of this.extensions.values()) {
302
+ if (ext.session === this) gc.push(ext)
303
+ }
304
+ for (const ext of gc) ext.destroy()
305
+
306
+ if (this.replicator !== null) {
307
+ this.replicator.clearRequests(this.activeRequests)
308
+ }
280
309
 
281
310
  if (this.sessions.length) {
282
311
  // if this is the last session and we are auto closing, trigger that first to enforce error handling
@@ -297,24 +326,34 @@ module.exports = class Hypercore extends EventEmitter {
297
326
  const protocol = noiseStream.userData
298
327
 
299
328
  if (this.opened) {
300
- this.replicator.joinProtocol(protocol, this.key, this.discoveryKey)
329
+ this.replicator.attachTo(protocol)
301
330
  } else {
302
- this.opening.then(() => this.replicator.joinProtocol(protocol, this.key, this.discoveryKey), protocol.destroy.bind(protocol))
331
+ this.opening.then(() => this.replicator.attachTo(protocol), protocol.destroy.bind(protocol))
303
332
  }
304
333
 
305
334
  return protocolStream
306
335
  }
307
336
 
337
+ get discoveryKey () {
338
+ return this.replicator === null ? null : this.replicator.discoveryKey
339
+ }
340
+
308
341
  get length () {
309
- return this.core === null ? 0 : this.core.tree.length
342
+ return this._snapshot
343
+ ? this._snapshot.length
344
+ : (this.core === null ? 0 : this.core.tree.length)
310
345
  }
311
346
 
312
347
  get byteLength () {
313
- return this.core === null ? 0 : this.core.tree.byteLength - (this.core.tree.length * this.padding)
348
+ return this._snapshot
349
+ ? this._snapshot.byteLength
350
+ : (this.core === null ? 0 : this.core.tree.byteLength - (this.core.tree.length * this.padding))
314
351
  }
315
352
 
316
353
  get fork () {
317
- return this.core === null ? 0 : this.core.tree.fork
354
+ return this._snapshot
355
+ ? this._snapshot.fork
356
+ : (this.core === null ? 0 : this.core.tree.fork)
318
357
  }
319
358
 
320
359
  get peers () {
@@ -353,13 +392,11 @@ module.exports = class Hypercore extends EventEmitter {
353
392
  }
354
393
  }
355
394
 
356
- this.replicator.broadcastInfo()
395
+ this.replicator.localUpgrade()
357
396
  }
358
397
 
359
- if (bitfield && !bitfield.drop) { // TODO: support drop!
360
- for (let i = 0; i < bitfield.length; i++) {
361
- this.replicator.broadcastBlock(bitfield.start + i)
362
- }
398
+ if (bitfield) {
399
+ this.replicator.broadcastRange(bitfield.start, bitfield.length, bitfield.drop)
363
400
  }
364
401
 
365
402
  if (value) {
@@ -372,9 +409,14 @@ module.exports = class Hypercore extends EventEmitter {
372
409
  }
373
410
 
374
411
  _onpeerupdate (added, peer) {
375
- if (added) this.extensions.update(peer)
376
412
  const name = added ? 'peer-add' : 'peer-remove'
377
413
 
414
+ if (added) {
415
+ for (const ext of this.extensions.values()) {
416
+ peer.extensions.set(ext.name, ext)
417
+ }
418
+ }
419
+
378
420
  for (let i = 0; i < this.sessions.length; i++) {
379
421
  this.sessions[i].emit(name, peer)
380
422
  }
@@ -393,19 +435,32 @@ module.exports = class Hypercore extends EventEmitter {
393
435
  return null
394
436
  }
395
437
 
396
- async update () {
438
+ async update (opts) {
397
439
  if (this.opened === false) await this.opening
440
+
398
441
  // TODO: add an option where a writer can bootstrap it's state from the network also
399
- if (this.writable) return false
400
- return this.replicator.requestUpgrade()
442
+ if (this.writable || this.closing !== null) return false
443
+
444
+ const activeRequests = (opts && opts.activeRequests) || this.activeRequests
445
+ const req = this.replicator.addUpgrade(activeRequests)
446
+
447
+ return req.promise
401
448
  }
402
449
 
403
- async seek (bytes) {
450
+ async seek (bytes, opts) {
404
451
  if (this.opened === false) await this.opening
405
452
 
406
453
  const s = this.core.tree.seek(bytes, this.padding)
407
454
 
408
- return (await s.update()) || this.replicator.requestSeek(s)
455
+ const offset = await s.update()
456
+ if (offset) return offset
457
+
458
+ if (this.closing !== null) throw new Error('Session is closed')
459
+
460
+ const activeRequests = (opts && opts.activeRequests) || this.activeRequests
461
+ const req = this.replicator.addSeek(activeRequests, s)
462
+
463
+ return req.promise
409
464
  }
410
465
 
411
466
  async has (index) {
@@ -416,6 +471,8 @@ module.exports = class Hypercore extends EventEmitter {
416
471
 
417
472
  async get (index, opts) {
418
473
  if (this.opened === false) await this.opening
474
+ if (this.closing !== null) throw new Error('Session is closed')
475
+
419
476
  const c = this.cache && this.cache.get(index)
420
477
  if (c) return c
421
478
  const fork = this.core.tree.fork
@@ -434,7 +491,11 @@ module.exports = class Hypercore extends EventEmitter {
434
491
  } else {
435
492
  if (opts && opts.wait === false) return null
436
493
  if (opts && opts.onwait) opts.onwait(index)
437
- block = await this.replicator.requestBlock(index)
494
+
495
+ const activeRequests = (opts && opts.activeRequests) || this.activeRequests
496
+ const req = this.replicator.addBlock(activeRequests, index)
497
+
498
+ block = await req.promise
438
499
  }
439
500
 
440
501
  if (this.encryption) this.encryption.decrypt(index, block)
@@ -450,37 +511,27 @@ module.exports = class Hypercore extends EventEmitter {
450
511
  }
451
512
 
452
513
  download (range) {
453
- const linear = !!(range && range.linear)
454
-
455
- let start
456
- let end
457
- let filter
458
-
459
- if (range && range.blocks) {
460
- const blocks = range.blocks instanceof Set
461
- ? range.blocks
462
- : new Set(range.blocks)
463
-
464
- start = range.start || (blocks.size ? min(range.blocks) : 0)
465
- end = range.end || (blocks.size ? max(range.blocks) + 1 : 0)
466
-
467
- filter = (i) => blocks.has(i)
468
- } else {
469
- start = (range && range.start) || 0
470
- end = typeof (range && range.end) === 'number' ? range.end : -1 // download all
514
+ const reqP = this._download(range)
515
+
516
+ // do not crash in the background...
517
+ reqP.catch(noop)
518
+
519
+ // TODO: turn this into an actual object...
520
+ return {
521
+ async downloaded () {
522
+ const req = await reqP
523
+ return req.promise
524
+ },
525
+ destroy () {
526
+ reqP.then(req => req.context && req.context.detach(req), noop)
527
+ }
471
528
  }
472
-
473
- const r = Replicator.createRange(start, end, filter, linear)
474
-
475
- if (this.opened) this.replicator.addRange(r)
476
- else this.opening.then(() => this.replicator.addRange(r), noop)
477
-
478
- return r
479
529
  }
480
530
 
481
- // TODO: get rid of this / deprecate it?
482
- cancel (request) {
483
- // Do nothing for now
531
+ async _download (range) {
532
+ if (this.opened === false) await this.opening
533
+ const activeRequests = (range && range.activeRequests) || this.activeRequests
534
+ return this.replicator.addRange(activeRequests, range)
484
535
  }
485
536
 
486
537
  // TODO: get rid of this / deprecate it?
@@ -488,6 +539,11 @@ module.exports = class Hypercore extends EventEmitter {
488
539
  range.destroy(null)
489
540
  }
490
541
 
542
+ // TODO: get rid of this / deprecate it?
543
+ cancel (request) {
544
+ // Do nothing for now
545
+ }
546
+
491
547
  async truncate (newLength = 0, fork = -1) {
492
548
  if (this.opened === false) await this.opening
493
549
  if (this.writable === false) throw new Error('Core is not writable')
@@ -518,13 +574,58 @@ module.exports = class Hypercore extends EventEmitter {
518
574
  return await this.core.append(buffers, this.sign, { preappend })
519
575
  }
520
576
 
521
- registerExtension (name, handlers) {
522
- return this.extensions.register(name, handlers)
577
+ async treeHash (length) {
578
+ if (length === undefined) {
579
+ await this.ready()
580
+ length = this.core.length
581
+ }
582
+
583
+ const roots = await this.core.tree.getRoots(length)
584
+ return this.crypto.tree(roots)
523
585
  }
524
586
 
525
- // called by the extensions
526
- onextensionupdate () {
527
- if (this.replicator !== null) this.replicator.broadcastOptions()
587
+ registerExtension (name, handlers = {}) {
588
+ if (this.extensions.has(name)) {
589
+ const ext = this.extensions.get(name)
590
+ ext.handlers = handlers
591
+ ext.encoding = c.from(codecs(handlers.encoding) || c.buffer)
592
+ ext.session = this
593
+ return ext
594
+ }
595
+
596
+ const ext = {
597
+ name,
598
+ handlers,
599
+ encoding: c.from(codecs(handlers.encoding) || c.buffer),
600
+ session: this,
601
+ send (message, peer) {
602
+ const buffer = c.encode(this.encoding, message)
603
+ peer.extension(name, buffer)
604
+ },
605
+ broadcast (message) {
606
+ const buffer = c.encode(this.encoding, message)
607
+ for (const peer of this.session.peers) {
608
+ peer.extension(name, buffer)
609
+ }
610
+ },
611
+ destroy () {
612
+ for (const peer of this.session.peers) {
613
+ peer.extensions.delete(name)
614
+ }
615
+ this.session.extensions.delete(name)
616
+ },
617
+ _onmessage (state, peer) {
618
+ const m = this.encoding.decode(state)
619
+ if (this.handlers.onmessage) this.handlers.onmessage(m, peer)
620
+ }
621
+ }
622
+
623
+ this.extensions.set(name, ext)
624
+ for (const peer of this.peers) {
625
+ peer.extensions.set(name, ext)
626
+ }
627
+
628
+ return ext
528
629
  }
529
630
 
530
631
  _encode (enc, val) {
@@ -574,19 +675,6 @@ function toHex (buf) {
574
675
  return buf && b4a.toString(buf, 'hex')
575
676
  }
576
677
 
577
- function reduce (iter, fn, acc) {
578
- for (const item of iter) acc = fn(acc, item)
579
- return acc
580
- }
581
-
582
- function min (arr) {
583
- return reduce(arr, (a, b) => Math.min(a, b), Infinity)
584
- }
585
-
586
- function max (arr) {
587
- return reduce(arr, (a, b) => Math.max(a, b), -Infinity)
588
- }
589
-
590
678
  function preappend (blocks) {
591
679
  const offset = this.core.tree.length
592
680
  const fork = this.core.tree.fork
package/lib/bitfield.js CHANGED
@@ -41,8 +41,9 @@ module.exports = class Bitfield {
41
41
  this.pages = new BigSparseArray()
42
42
  this.unflushed = []
43
43
  this.storage = storage
44
+ this.resumed = !!(buf && buf.byteLength >= 4)
44
45
 
45
- const all = (buf && buf.byteLength >= 4)
46
+ const all = this.resumed
46
47
  ? new Uint32Array(buf.buffer, buf.byteOffset, Math.floor(buf.byteLength / 4))
47
48
  : new Uint32Array(1024)
48
49
 
@@ -93,8 +94,10 @@ module.exports = class Bitfield {
93
94
  clear () {
94
95
  return new Promise((resolve, reject) => {
95
96
  this.storage.del(0, Infinity, (err) => {
96
- if (err) reject(err)
97
- else resolve()
97
+ if (err) return reject(err)
98
+ this.pages = new BigSparseArray()
99
+ this.unflushed = []
100
+ resolve()
98
101
  })
99
102
  })
100
103
  }
package/lib/caps.js ADDED
@@ -0,0 +1,34 @@
1
+ const crypto = require('hypercore-crypto')
2
+ const sodium = require('sodium-universal')
3
+ const b4a = require('b4a')
4
+ const c = require('compact-encoding')
5
+
6
+ // TODO: rename this to "crypto" and move everything hashing related etc in here
7
+ // Also lets move the tree stuff from hypercore-crypto here, and loose the types
8
+ // from the hashes there - they are not needed since we lock the indexes in the tree
9
+ // hash and just makes alignment etc harder in other languages
10
+
11
+ const [TREE, REPLICATE_INITIATOR, REPLICATE_RESPONDER] = crypto.namespace('hypercore', 3)
12
+
13
+ exports.replicate = function (isInitiator, key, handshakeHash) {
14
+ const out = b4a.allocUnsafe(32)
15
+ sodium.crypto_generichash_batch(out, [isInitiator ? REPLICATE_INITIATOR : REPLICATE_RESPONDER, key], handshakeHash)
16
+ return out
17
+ }
18
+
19
+ exports.treeSignable = function (hash, length, fork) {
20
+ const state = { start: 0, end: 80, buffer: b4a.allocUnsafe(80) }
21
+ c.raw.encode(state, TREE)
22
+ c.raw.encode(state, hash)
23
+ c.uint64.encode(state, length)
24
+ c.uint64.encode(state, fork)
25
+ return state.buffer
26
+ }
27
+
28
+ exports.treeSignableLegacy = function (hash, length, fork) {
29
+ const state = { start: 0, end: 48, buffer: b4a.allocUnsafe(48) }
30
+ c.raw.encode(state, hash)
31
+ c.uint64.encode(state, length)
32
+ c.uint64.encode(state, fork)
33
+ return state.buffer
34
+ }
package/lib/core.js CHANGED
@@ -5,10 +5,10 @@ const Mutex = require('./mutex')
5
5
  const MerkleTree = require('./merkle-tree')
6
6
  const BlockStore = require('./block-store')
7
7
  const Bitfield = require('./bitfield')
8
- const { oplogHeader, oplogEntry } = require('./messages')
8
+ const m = require('./messages')
9
9
 
10
10
  module.exports = class Core {
11
- constructor (header, crypto, oplog, tree, blocks, bitfield, sign, onupdate) {
11
+ constructor (header, crypto, oplog, tree, blocks, bitfield, sign, legacy, onupdate) {
12
12
  this.onupdate = onupdate
13
13
  this.header = header
14
14
  this.crypto = crypto
@@ -24,6 +24,7 @@ module.exports = class Core {
24
24
  this._verifies = null
25
25
  this._verifiesFlushed = null
26
26
  this._mutex = new Mutex()
27
+ this._legacy = legacy
27
28
  }
28
29
 
29
30
  static async open (storage, opts = {}) {
@@ -57,18 +58,24 @@ module.exports = class Core {
57
58
  }
58
59
 
59
60
  static async resume (oplogFile, treeFile, bitfieldFile, dataFile, opts) {
60
- const overwrite = opts.overwrite === true
61
+ let overwrite = opts.overwrite === true
62
+
63
+ const force = opts.force === true
61
64
  const createIfMissing = opts.createIfMissing !== false
62
65
  const crypto = opts.crypto || hypercoreCrypto
63
66
 
64
67
  const oplog = new Oplog(oplogFile, {
65
- headerEncoding: oplogHeader,
66
- entryEncoding: oplogEntry
68
+ headerEncoding: m.oplog.header,
69
+ entryEncoding: m.oplog.entry
67
70
  })
68
71
 
69
72
  let { header, entries } = await oplog.open()
70
73
 
71
- if (!header || overwrite === true) {
74
+ if (force && opts.keyPair && header && header.signer && !b4a.equals(header.signer.publicKey, opts.keyPair.publicKey)) {
75
+ overwrite = true
76
+ }
77
+
78
+ if (!header || overwrite) {
72
79
  if (!createIfMissing) {
73
80
  throw new Error('No hypercore is stored here')
74
81
  }
@@ -103,6 +110,10 @@ module.exports = class Core {
103
110
  await tree.clear()
104
111
  await blocks.clear()
105
112
  await bitfield.clear()
113
+ entries = []
114
+ } else if (bitfield.resumed && header.tree.length === 0) {
115
+ // If this was an old bitfield, reset it since it loads based on disk size atm (TODO: change that)
116
+ await bitfield.clear()
106
117
  }
107
118
 
108
119
  const sign = opts.sign || (header.signer.secretKey ? this.createSigner(crypto, header.signer) : null)
@@ -119,7 +130,7 @@ module.exports = class Core {
119
130
  }
120
131
 
121
132
  if (e.bitfield) {
122
- bitfield.setRange(e.bitfield.start, e.bitfield.length)
133
+ bitfield.setRange(e.bitfield.start, e.bitfield.length, !e.bitfield.drop)
123
134
  }
124
135
 
125
136
  if (e.treeUpgrade) {
@@ -136,7 +147,7 @@ module.exports = class Core {
136
147
  }
137
148
  }
138
149
 
139
- return new this(header, crypto, oplog, tree, blocks, bitfield, sign, opts.onupdate || noop)
150
+ return new this(header, crypto, oplog, tree, blocks, bitfield, sign, !!opts.legacy, opts.onupdate || noop)
140
151
  }
141
152
 
142
153
  _shouldFlush () {
@@ -227,7 +238,7 @@ module.exports = class Core {
227
238
  for (const val of values) batch.append(val)
228
239
 
229
240
  const hash = batch.hash()
230
- batch.signature = await sign(batch.signable(hash))
241
+ batch.signature = await sign(this._legacy ? batch.signableLegacy(hash) : batch.signable(hash))
231
242
 
232
243
  const entry = {
233
244
  userData: null,
@@ -259,10 +270,15 @@ module.exports = class Core {
259
270
  }
260
271
  }
261
272
 
273
+ _signed (batch, hash) {
274
+ const signable = this._legacy ? batch.signableLegacy(hash) : batch.signable(hash)
275
+ return this.crypto.verify(signable, batch.signature, this.header.signer.publicKey)
276
+ }
277
+
262
278
  async _verifyExclusive ({ batch, bitfield, value, from }) {
263
279
  // TODO: move this to tree.js
264
280
  const hash = batch.hash()
265
- if (!batch.signature || !this.crypto.verify(batch.signable(hash), batch.signature, this.header.signer.publicKey)) {
281
+ if (!batch.signature || !this._signed(batch, hash)) {
266
282
  throw new Error('Remote signature does not match')
267
283
  }
268
284