Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support rocksdb backed Hypercore #182

Open
wants to merge 45 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8564e04
use rocks db hypercore API
chm-diederichs Aug 19, 2024
eb71da0
truncate is async
chm-diederichs Aug 19, 2024
c67c89a
shouldFlush checks core flushed length
chm-diederichs Aug 23, 2024
01c3ac9
tidy up core flush
chm-diederichs Aug 23, 2024
4f9056e
update basic tests
chm-diederichs Aug 23, 2024
93818ad
load initial system from persisted batch
chm-diederichs Aug 30, 2024
c09788b
autocore loads from batch state
chm-diederichs Aug 30, 2024
60449bd
catchup only loads writer nodes into linearizer
chm-diederichs Aug 30, 2024
073c7ec
persist updates array across restarts
chm-diederichs Aug 30, 2024
347582b
fixup core signing
chm-diederichs Aug 30, 2024
63c9659
add missing timeouts on hypercore gets
chm-diederichs Aug 30, 2024
10fb10f
update tests to batch api
chm-diederichs Aug 30, 2024
1f5d621
clear updates if we clear boot record
chm-diederichs Sep 4, 2024
e9b40c6
encode update system length
chm-diederichs Sep 4, 2024
b7fd230
fix test timings
chm-diederichs Sep 4, 2024
6144f5e
skip pending tests and fix test comparisons
chm-diederichs Sep 5, 2024
cc11213
remove debug statements
chm-diederichs Sep 11, 2024
b68db26
update tests: ensure await and closed cores
chm-diederichs Sep 17, 2024
0595419
always check if we are interrupting
chm-diederichs Sep 17, 2024
66a03b2
make sure we close every core
chm-diederichs Sep 17, 2024
a9e60fa
teardown makes sure views are closed and skip bad tests
chm-diederichs Sep 17, 2024
635790e
standard
chm-diederichs Sep 17, 2024
ceb8a08
readd snapshot support for rocks (#178)
chm-diederichs Sep 17, 2024
b4ba9e2
set userData on originalCore as well
chm-diederichs Sep 24, 2024
bff5439
emit append when catching up beyond current length
chm-diederichs Sep 24, 2024
d39dbfc
close view store
chm-diederichs Sep 24, 2024
359db8e
use hypercore rocksdb dep
chm-diederichs Sep 25, 2024
8283286
standard
chm-diederichs Sep 27, 2024
4f59a50
rocks cores have no session
chm-diederichs Sep 27, 2024
9843503
update tests to rocks api
chm-diederichs Oct 7, 2024
c31c455
use corestore rocksdb dep
chm-diederichs Oct 7, 2024
890b0c9
remove additional blocks from copyPrologue
chm-diederichs Oct 18, 2024
eb6a7e4
hypercore refresh option renamed to overwrite
chm-diederichs Oct 18, 2024
e0c8c43
Merge branch 'main' into rocksdb
chm-diederichs Oct 22, 2024
8eb93c2
store explicitly closed
chm-diederichs Oct 23, 2024
fcaa700
close all bases
chm-diederichs Oct 23, 2024
1979c38
rocksdb: update for hypercore sync constructor API (#189)
chm-diederichs Nov 13, 2024
e95807c
rocksdb: apply operates on memory view (#191)
chm-diederichs Nov 29, 2024
745a4af
do not set namespace on the store (#193)
chm-diederichs Dec 3, 2024
0b01593
rocksdb: user views are hypercores (#200)
chm-diederichs Dec 13, 2024
d0fda4b
Merge branch 'main' into rocksdb
chm-diederichs Dec 13, 2024
27ecfb1
close store in test
chm-diederichs Dec 13, 2024
0859505
rocksdb: use atomic batches for state updates (#202)
chm-diederichs Jan 6, 2025
9e3a6db
rocksdb: ff does not do a full copy when overwriting (#203)
chm-diederichs Jan 7, 2025
cb9683e
await system ready also
mafintosh Jan 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
808 changes: 527 additions & 281 deletions index.js

Large diffs are not rendered by default.

793 changes: 206 additions & 587 deletions lib/core.js

Large diffs are not rendered by default.

84 changes: 74 additions & 10 deletions lib/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,26 +131,42 @@ const Wakeup = {

const Views = c.array(c.string)

const V0BootRecord = {
preencode () {
throw new Error('version 0 records cannot be encoded')
},
encode () {
throw new Error('version 0 records cannot be encoded')
},
decode (state) {
const indexed = Checkout.decode(state)
const heads = Clock.decode(state)
const views = Views.decode(state)

return { version: 0, indexed, heads, views }
}
}

const BootRecord = {
preencode (state, m) {
c.uint.preencode(state, 0) // version
Checkout.preencode(state, m.indexed)
Clock.preencode(state, m.heads)
c.uint.preencode(state, 1) // version
c.fixed32.preencode(state, m.key)
Views.preencode(state, m.views)
},
encode (state, m) {
c.uint.encode(state, 0) // version
Checkout.encode(state, m.indexed)
Clock.encode(state, m.heads)
c.uint.encode(state, 1) // version
c.fixed32.encode(state, m.key)
Views.encode(state, m.views)
},
decode (state) {
const v = c.uint.decode(state)
assert(v === 0, 'Unsupported version: ' + v)
if (v === 0) return V0BootRecord.decode(state)

assert(v === 1, 'Unsupported version: ' + v)

return {
indexed: Checkout.decode(state),
heads: Clock.decode(state),
version: 1,
key: c.fixed32.decode(state),
views: Views.decode(state)
}
}
Expand Down Expand Up @@ -508,6 +524,53 @@ const Member = {
}
}

const View = {
preencode (state, v) {
c.int.preencode(state, v.core)
c.uint.preencode(state, v.appending)
},
encode (state, v) {
c.int.encode(state, v.core)
c.uint.encode(state, v.appending)
},
decode (state) {
return {
core: c.int.decode(state),
appending: c.uint.decode(state)
}
}
}

const ViewArray = c.array(View)

const Update = {
preencode (state, u) {
c.uint.preencode(state, u.batch)
c.bool.preencode(state, u.indexers)
ViewArray.preencode(state, u.views)
c.uint.preencode(state, u.version)
c.uint.preencode(state, u.systemLength)
},
encode (state, u) {
c.uint.encode(state, u.batch)
c.bool.encode(state, u.indexers)
ViewArray.encode(state, u.views)
c.uint.encode(state, u.version)
c.uint.encode(state, u.systemLength)
},
decode (state) {
return {
batch: c.uint.decode(state),
indexers: c.bool.decode(state),
views: ViewArray.decode(state),
version: c.uint.decode(state),
systemLength: c.uint.decode(state)
}
}
}

const UpdateArray = c.array(Update)

module.exports = {
Wakeup,
Clock,
Expand All @@ -517,5 +580,6 @@ module.exports = {
OplogMessage,
Checkpoint,
Info,
Member
Member,
UpdateArray
}
36 changes: 20 additions & 16 deletions lib/signer.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const b4a = require('b4a')
const crypto = require('hypercore-crypto')
const { partialSignature } = require('hypercore/lib/multisig.js')

module.exports = class Signer {
Expand All @@ -9,27 +10,29 @@ module.exports = class Signer {
this.opened = false
this.checkpoints = new Map()

this.open()
this.open(core.originalCore.length)
}

async sign (indexers, length) {
const signatures = await this.getSignatures(indexers, length)
const tree = this.core.core.createTreeBatch()
async sign (indexers, length, pendingIndexedLength) {
const signatures = await this.getSignatures(indexers, length, pendingIndexedLength)
const tree = this.core.core.state.tree
const p = await Promise.all(signatures.map(s => partialSignature(tree, s.signer, length, s.length, s.signature)))
return this.core.core.session.core.verifier.assemble(p)
return this.core.core.core.verifier.assemble(p)
}

async _verify (length, signature, key) {
if (!this.core.core || length > this.core.core.length) return false
if (length < this.core.core.indexedLength) return true
const batch = await this.core.core.restoreBatch(length)
// against the main fork
batch.fork = this.core.core.core.tree.fork
const { publicKey } = this.base.getNamespace(key, this.core)
return batch.tree.crypto.verify(batch.signable(this.core.key), signature, publicKey)
return crypto.verify(batch.signable(this.core.key), signature, publicKey)
}

open () {
open (length) {
if (this.opened) return true
if (!this.core.pendingIndexedLength) return false
if (!length) return false

for (const idx of this.base.linearizer.indexers) {
for (const checkpoint of idx.flushCheckpoints(this.core.systemIndex)) {
Expand All @@ -55,13 +58,13 @@ module.exports = class Signer {
checkpoints.push(checkpoint)
}

bestCheckpoint (idx, gc = false) {
bestCheckpoint (idx, length, gc = false) {
const hex = b4a.toString(idx.core.key, 'hex')

const checkpoints = this.checkpoints.get(hex)
if (!checkpoints) return null

const i = findBestCheckpoint(checkpoints, this.core.pendingIndexedLength)
const i = findBestCheckpoint(checkpoints, length)
if (i === -1) return null

const checkpoint = checkpoints[i]
Expand All @@ -73,15 +76,16 @@ module.exports = class Signer {
return checkpoint
}

async getSignableLength (indexers) {
if (!this.open()) return 0
async getSignableLength (indexers, length) {
if (!this.open(length)) return 0

const signed = []
const thres = (indexers.length >> 1) + 1

for (const idx of indexers) {
const checkpoint = this.bestCheckpoint(idx)
if (!checkpoint) continue
const checkpoint = this.bestCheckpoint(idx, length)

if (!checkpoint || checkpoint.length <= this.core.core.signedLength) continue

// signature is invalid
if (!(await this._verify(checkpoint.length, checkpoint.signature, idx.core.key))) {
Expand All @@ -94,13 +98,13 @@ module.exports = class Signer {
return signed.length < thres ? 0 : signed.sort(descendingOrder)[thres - 1]
}

async getSignatures (indexers, length) {
async getSignatures (indexers, length, pendingIndexedLength) {
const signatures = []
const thres = (indexers.length >> 1) + 1

for (let signer = 0; signer < indexers.length; signer++) {
const idx = indexers[signer]
const checkpoint = this.bestCheckpoint(idx, true)
const checkpoint = this.bestCheckpoint(idx, pendingIndexedLength, true)
if (!checkpoint) continue

// signature is invalid
Expand Down
Loading
Loading