Skip to content

Commit 781f38a

Browse files
committed
Merge branch 'main' of github.com:holepunchto/hypercore into use-safetyCatch
# Conflicts: # index.js # lib/batch.js
2 parents 27e0dc6 + bee72e6 commit 781f38a

19 files changed

+1336
-505
lines changed

index.js

+40-9
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@ const Protomux = require('protomux')
1010
const z32 = require('z32')
1111
const id = require('hypercore-id-encoding')
1212
const safetyCatch = require('safety-catch')
13+
const { createTracer } = require('hypertrace')
1314

1415
const Replicator = require('./lib/replicator')
1516
const Core = require('./lib/core')
1617
const BlockEncryption = require('./lib/block-encryption')
1718
const Info = require('./lib/info')
1819
const Download = require('./lib/download')
1920
const Batch = require('./lib/batch')
20-
const { manifestHash, defaultSignerManifest, createVerifier, createManifest, isCompat } = require('./lib/manifest')
21+
const { manifestHash, defaultSignerManifest, createManifest, isCompat, sign } = require('./lib/verifier')
2122
const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
2223
const {
24+
ASSERTION,
2325
BAD_ARGUMENT,
2426
SESSION_CLOSED,
2527
SESSION_NOT_WRITABLE,
@@ -29,6 +31,10 @@ const {
2931
const promises = Symbol.for('hypercore.promises')
3032
const inspect = Symbol.for('nodejs.util.inspect.custom')
3133

34+
// Hypercore actually does not have any notion of max/min block sizes
35+
// but we enforce 15mb to ensure smooth replication (each block is transmitted atomically)
36+
const MAX_SUGGESTED_BLOCK_SIZE = 15 * 1024 * 1024
37+
3238
module.exports = class Hypercore extends EventEmitter {
3339
constructor (storage, key, opts) {
3440
super()
@@ -49,6 +55,7 @@ module.exports = class Hypercore extends EventEmitter {
4955

5056
this[promises] = true
5157

58+
this.tracer = createTracer(this)
5259
this.storage = null
5360
this.crypto = opts.crypto || hypercoreCrypto
5461
this.core = null
@@ -133,8 +140,10 @@ module.exports = class Hypercore extends EventEmitter {
133140
indent + ')'
134141
}
135142

143+
static MAX_SUGGESTED_BLOCK_SIZE = MAX_SUGGESTED_BLOCK_SIZE
144+
136145
static key (manifest, { compat } = {}) {
137-
return compat ? manifest.signer.publicKey : manifestHash(createManifest(manifest))
146+
return compat ? manifest.signers[0].publicKey : manifestHash(createManifest(manifest))
138147
}
139148

140149
static discoveryKey (key) {
@@ -267,6 +276,8 @@ module.exports = class Hypercore extends EventEmitter {
267276
this.writable = this._isWritable()
268277
this.autoClose = o.autoClose
269278

279+
if (o.core) this.tracer.setParent(o.core.tracer)
280+
270281
if (this.snapshotted && this.core && !this._snapshot) this._updateSnapshot()
271282
}
272283

@@ -377,6 +388,7 @@ module.exports = class Hypercore extends EventEmitter {
377388
onupdate: this._oncoreupdate.bind(this),
378389
onconflict: this._oncoreconflict.bind(this)
379390
})
391+
this.tracer.setParent(this.core.tracer)
380392

381393
if (opts.userData) {
382394
for (const [key, value] of Object.entries(opts.userData)) {
@@ -490,14 +502,14 @@ module.exports = class Hypercore extends EventEmitter {
490502
}
491503

492504
const manifest = opts.manifest || defaultSignerManifest(keyPair.publicKey)
493-
const key = opts.key || (opts.compat !== false ? manifest.signer.publicKey : manifestHash(manifest))
505+
const key = opts.key || (opts.compat !== false ? manifest.signers[0].publicKey : manifestHash(manifest))
494506

495507
if (b4a.equals(key, this.key)) {
496508
throw BAD_ARGUMENT('Clone cannot share verification information')
497509
}
498510

499511
const signature = opts.signature === undefined
500-
? createVerifier(createManifest(manifest), { compat: isCompat(key, manifest) }).sign(this.core.tree.batch(), keyPair)
512+
? sign(createManifest(manifest), this.core.tree.batch(), keyPair, { compat: isCompat(key, manifest) })
501513
: opts.signature
502514

503515
const sparse = opts.sparse === false ? false : this.sparse
@@ -809,12 +821,13 @@ module.exports = class Hypercore extends EventEmitter {
809821
return true
810822
}
811823

812-
batch ({ checkout = -1, autoClose = true, session = true, restore = false } = {}) {
813-
return new Batch(session ? this.session() : this, checkout, autoClose, restore)
824+
batch ({ checkout = -1, autoClose = true, session = true, restore = false, clear = false } = {}) {
825+
return new Batch(session ? this.session() : this, checkout, autoClose, restore, clear)
814826
}
815827

816828
async seek (bytes, opts) {
817829
if (this.opened === false) await this.opening
830+
if (!isValidIndex(bytes)) throw ASSERTION('seek is invalid')
818831

819832
const tree = (opts && opts.tree) || this.core.tree
820833
const s = tree.seek(bytes, this.padding)
@@ -837,17 +850,20 @@ module.exports = class Hypercore extends EventEmitter {
837850

838851
async has (start, end = start + 1) {
839852
if (this.opened === false) await this.opening
853+
if (!isValidIndex(start) || !isValidIndex(end)) throw ASSERTION('has range is invalid')
840854

841-
const length = end - start
842-
if (length <= 0) return false
843-
if (length === 1) return this.core.bitfield.get(start)
855+
if (end === start + 1) return this.core.bitfield.get(start)
844856

845857
const i = this.core.bitfield.firstUnset(start)
846858
return i === -1 || i >= end
847859
}
848860

849861
async get (index, opts) {
850862
if (this.opened === false) await this.opening
863+
if (!isValidIndex(index)) throw ASSERTION('block index is invalid')
864+
865+
this.tracer.trace('get', { index })
866+
851867
if (this.closing !== null) throw SESSION_CLOSED()
852868
if (this._snapshot !== null && index >= this._snapshot.compatLength) throw SNAPSHOT_NOT_AVAILABLE()
853869

@@ -878,6 +894,8 @@ module.exports = class Hypercore extends EventEmitter {
878894
end = start + 1
879895
}
880896

897+
if (!isValidIndex(start) || !isValidIndex(end)) throw ASSERTION('clear range is invalid')
898+
881899
const cleared = (opts && opts.diff) ? { blocks: 0 } : null
882900

883901
if (start >= end) return cleared
@@ -910,6 +928,7 @@ module.exports = class Hypercore extends EventEmitter {
910928
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
911929

912930
const req = this.replicator.addBlock(activeRequests, index)
931+
req.snapshot = index < this.length
913932

914933
const timeout = opts && opts.timeout !== undefined ? opts.timeout : this.timeout
915934
if (timeout) req.context.setTimeout(req, timeout)
@@ -962,6 +981,8 @@ module.exports = class Hypercore extends EventEmitter {
962981
async _download (range) {
963982
if (this.opened === false) await this.opening
964983

984+
this.tracer.trace('download', { range })
985+
965986
const activeRequests = (range && range.activeRequests) || this.activeRequests
966987

967988
return this.replicator.addRange(activeRequests, range)
@@ -1004,6 +1025,7 @@ module.exports = class Hypercore extends EventEmitter {
10041025
if (writable === false) throw SESSION_NOT_WRITABLE()
10051026

10061027
blocks = Array.isArray(blocks) ? blocks : [blocks]
1028+
this.tracer.trace('append', { blocks })
10071029

10081030
const preappend = this.encryption && this._preappend
10091031

@@ -1014,6 +1036,11 @@ module.exports = class Hypercore extends EventEmitter {
10141036
buffers[i] = this._encode(this.valueEncoding, blocks[i])
10151037
}
10161038
}
1039+
for (const b of buffers) {
1040+
if (b.byteLength > MAX_SUGGESTED_BLOCK_SIZE) {
1041+
throw BAD_ARGUMENT('Appended block exceeds the maximum suggested block size')
1042+
}
1043+
}
10171044

10181045
return this.core.append(buffers, { keyPair, signature, preappend })
10191046
}
@@ -1133,3 +1160,7 @@ function ensureEncryption (core, opts) {
11331160
function createCache (cache) {
11341161
return cache === true ? new Xache({ maxSize: 65536, maxAge: 0 }) : (cache || null)
11351162
}
1163+
1164+
function isValidIndex (index) {
1165+
return index === 0 || index > 0
1166+
}

lib/batch.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const b4a = require('b4a')
55
const safetyCatch = require('safety-catch')
66

77
module.exports = class HypercoreBatch extends EventEmitter {
8-
constructor (session, checkoutLength, autoClose, restore) {
8+
constructor (session, checkoutLength, autoClose, restore, clear) {
99
super()
1010

1111
this.session = session
@@ -26,8 +26,10 @@ module.exports = class HypercoreBatch extends EventEmitter {
2626
this._sessionByteLength = 0
2727
this._sessionBatch = null
2828
this._flushing = null
29+
this._clear = clear
2930

30-
this.opening = this.ready().catch(safetyCatch)
31+
this.opening = this._open()
32+
this.opening.catch(safetyCatch)
3133
}
3234

3335
get id () {
@@ -70,23 +72,27 @@ module.exports = class HypercoreBatch extends EventEmitter {
7072
return this.session.manifest
7173
}
7274

73-
async ready () {
75+
ready () {
76+
return this.opening
77+
}
78+
79+
async _open () {
7480
await this.session.ready()
75-
if (this.opened) return
81+
82+
if (this._clear) this._checkoutLength = this.core.tree.length
7683

7784
if (this._checkoutLength !== -1) {
7885
const batch = await this.session.core.tree.restoreBatch(this._checkoutLength)
7986
batch.treeLength = this._checkoutLength
80-
if (this.opened) return
8187
this._sessionLength = batch.length
8288
this._sessionByteLength = batch.byteLength
8389
this._sessionBatch = batch
90+
if (this._clear) await this.core.clearBatch()
8491
} else {
8592
const last = this.restore ? this.session.core.bitfield.findFirst(false, this.session.length) : 0
8693

8794
if (last > this.session.length) {
8895
const batch = await this.session.core.tree.restoreBatch(last)
89-
if (this.opened) return
9096
this._sessionLength = batch.length
9197
this._sessionByteLength = batch.byteLength - this.session.padding * batch.length
9298
this._sessionBatch = batch
@@ -188,9 +194,9 @@ module.exports = class HypercoreBatch extends EventEmitter {
188194
}
189195
}
190196

191-
async restoreBatch (length) {
197+
async restoreBatch (length, blocks) {
192198
if (this.opened === false) await this.opening
193-
if (length >= this._sessionLength) return this.createTreeBatch(length)
199+
if (length >= this._sessionLength) return this.createTreeBatch(length, blocks)
194200
return this.session.core.tree.restoreBatch(length)
195201
}
196202

lib/block-store.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const b4a = require('b4a')
2+
const { WRITE_FAILED } = require('hypercore-errors')
23

34
module.exports = class BlockStore {
45
constructor (storage, tree) {
@@ -54,7 +55,7 @@ module.exports = class BlockStore {
5455
_write (offset, data) {
5556
return new Promise((resolve, reject) => {
5657
this.storage.write(offset, data, (err) => {
57-
if (err) reject(err)
58+
if (err) reject(WRITE_FAILED(err.message))
5859
else resolve(offset + data.byteLength)
5960
})
6061
})

lib/caps.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ exports.replicate = function (isInitiator, key, handshakeHash) {
2525
return out
2626
}
2727

28-
exports.treeSignable = function (namespace, hash, length, fork) {
28+
exports.treeSignable = function (manifestHash, treeHash, length, fork) {
2929
const state = { start: 0, end: 112, buffer: b4a.allocUnsafe(112) }
30-
c.raw.encode(state, TREE)
31-
c.raw.encode(state, namespace)
32-
c.raw.encode(state, hash)
30+
c.fixed32.encode(state, TREE)
31+
c.fixed32.encode(state, manifestHash)
32+
c.fixed32.encode(state, treeHash)
3333
c.uint64.encode(state, length)
3434
c.uint64.encode(state, fork)
3535
return state.buffer
@@ -38,8 +38,8 @@ exports.treeSignable = function (namespace, hash, length, fork) {
3838
exports.treeSignableCompat = function (hash, length, fork, noHeader) {
3939
const end = noHeader ? 48 : 80
4040
const state = { start: 0, end, buffer: b4a.allocUnsafe(end) }
41-
if (!noHeader) c.raw.encode(state, TREE) // ultra legacy mode, kill in future major
42-
c.raw.encode(state, hash)
41+
if (!noHeader) c.fixed32.encode(state, TREE) // ultra legacy mode, kill in future major
42+
c.fixed32.encode(state, hash)
4343
c.uint64.encode(state, length)
4444
c.uint64.encode(state, fork)
4545
return state.buffer

0 commit comments

Comments
 (0)