Skip to content

Commit 4e1f07c

Browse files
Add Hypercore.treeHashFromStorage() method to check tree in storage (#783)
This method will not throw if the passed session is a resumed session. If that session is `.ready`'ed then it would throw. The resumed sessions skip loading the roots from storage and copy the parent roots.
1 parent 6306735 commit 4e1f07c

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ class Hypercore extends EventEmitter {
188188
return Replicator.clearRequests(session, err)
189189
}
190190

191+
static async treeHashFromStorage(session, length = session.length) {
192+
const roots = await MerkleTree.getRoots(session.state, length)
193+
return crypto.tree(roots)
194+
}
195+
191196
snapshot(opts) {
192197
return this.session({ ...opts, snapshot: true })
193198
}

test/basic.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,56 @@ test('treeHash with default length', async function (t) {
248248
await core2.close()
249249
})
250250

251+
test('treeHashFromStorage() = treeHash()', async function (t) {
252+
const core = await create(t)
253+
await core.ready()
254+
255+
t.alike(await core.treeHash(), await Hypercore.treeHashFromStorage(core))
256+
257+
await core.append('a')
258+
259+
t.alike(await core.treeHash(), await Hypercore.treeHashFromStorage(core))
260+
261+
await core.close()
262+
})
263+
264+
test('treeHashFromStorage() throws with bad storage', async function (t) {
265+
const dir = await t.tmp()
266+
const storage = await createStorage(t, dir)
267+
const core = new Hypercore(storage)
268+
t.teardown(() => core.close(), { order: 1 })
269+
await core.ready()
270+
await core.append('a')
271+
272+
const batch = core.session({ name: 'batch' })
273+
274+
await batch.state.mutex.lock()
275+
const tx = batch.state.storage.write()
276+
// Set a nonsense dependency to force all tree nodes to fail
277+
tx.setDependency({
278+
dataPointer: 1337,
279+
length: 3
280+
})
281+
const flushed = await tx.flush()
282+
batch.state._unlock()
283+
await core.close()
284+
await storage.close()
285+
286+
t.ok(flushed, 'storage corrupted')
287+
288+
const storage2 = await createStorage(t, dir)
289+
const core2 = new Hypercore(storage2)
290+
t.teardown(() => core2.close(), { order: 1 })
291+
await core2.ready()
292+
const batch2 = core2.session({ name: 'batch' })
293+
await batch2.ready() // ensure the session is resumed
294+
295+
t.exception(Hypercore.treeHashFromStorage(batch2), /INVALID_OPERATION: Expected tree node/)
296+
297+
await core2.close()
298+
await storage2.close()
299+
})
300+
251301
test('snapshot locks the state', async function (t) {
252302
const core = new Hypercore(await createStorage(t))
253303
await core.ready()

0 commit comments

Comments
 (0)