Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

Commit

Permalink
Merge branch 'master' into fix/rpc-relay
Browse files Browse the repository at this point in the history
  • Loading branch information
Nuhvi committed Jul 3, 2023
2 parents e624cd5 + 042d555 commit d7d0499
Show file tree
Hide file tree
Showing 19 changed files with 1,445 additions and 3,109 deletions.
4 changes: 3 additions & 1 deletion declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ declare module 'hypercore' {
sign?: (message: Uint8Array) => Uint8Array;
verify: (message: Uint8Array, signature: Uint8Array) => boolean;
}
export = class Hypercore extends EventEmitter<'close'> {
class Hypercore extends EventEmitter<'close'> {
constructor(storage: any, key?: Opts | Uint8Array, opts?: Opts);

length: number;
Expand Down Expand Up @@ -443,6 +443,8 @@ Populated after ready has been emitted. Will be null before the event.

sessions: Hypercore[];
};

export = Hypercore
}

// file://./node_modules/hyperblobs/index.js
Expand Down
3,799 changes: 1,319 additions & 2,480 deletions package-lock.json

Large diffs are not rendered by default.

52 changes: 0 additions & 52 deletions packages/drive/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,3 @@

[DEPRECATED] use `@synonymdev/slashtags-core-data` instead.

Drivestore is a Hyperdrive factory that makes it easier to manage large collections of named Hyperdrives.

## Features

- Public unencrypted drive
- Creates and keep track of all created encrypted private drives

## Installation

```
npm install @synonymdev/slashdrive
```

## Usage

```js
import Corestore from 'corestore'
import Drivestore from '@synonymdev/slashdrive'

const corestore = new Corestore('./corestore_dir')
const store = new Drivestore(corestore, keyPair)

const publicDrive = store.get('public') // or store.get()

const privateDrive = store.get('foo') // returns an encrypted Hyperdrive
```

## API

#### `const drivestore = new Drivestore(corestore, keyPair)`

Create new Drivestore.

- `corestore` must be an instance of [Corestore](https://github.com/hypercore-protocol/corestore).

If the instance is a [namespace](https://github.com/hypercore-protocol/corestore#const-store--storenamespacename), the internal corestore will reset its namespace to the `DEFAULT_NAMESPACE` (32 0-bytes).

- `keyPair` public and secret keys to create the public Hyperdrive, the secret key will be used as the `primaryKey` for the internal corestore.

#### `await drivestore.ready()`

Awaits opening metadata hypercore. Useful before [async iterating](#for-await-let-name-of-drivestore) over all created drives.

#### `const hyperdrive = drivestore.get([name])`

Returns an encrypted [Hyperdrive](https://github.com/hypercore-protocol/hyperdrive-next) for a given name.

If `name` is undefined or equal to `/public` it will return a public unencrypted drive, by the same keypair passed to the contsructor.

#### `const stream = drivestore.replicate(stream)`

Same as [drivestore.corestore.replicate(stream)](https://github.com/hypercore-protocol/corestore#const-stream--storereplicateoptsorstream)
97 changes: 11 additions & 86 deletions packages/drive/index.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,25 @@
const Hyperdrive = require('hyperdrive')
const Hyperbee = require('hyperbee')
const b4a = require('b4a')
const safetyCatch = require('safety-catch')

const METADATA_KEY = 'slashtags-drivestore-metadata'

class Drivestore {
/**
* @param {import('corestore')} corestore
* @param {import('hyperdht').KeyPair} keyPair
* @param {import('@synonymdev/slashtags-core-data')} coreData
*/
constructor (corestore, keyPair) {
this.fava = Math.random()
this.keyPair = keyPair
/** @type {import('corestore')} */
this.corestore = corestore.session({ primaryKey: this.keyPair.secretKey, namespace: null })

const metadataCore = this.corestore.get({
name: METADATA_KEY,
encryptionKey: this.keyPair.secretKey
})
this._metadata = new Hyperbee(metadataCore, { keyEncoding: 'utf8' })
this._drives = this._metadata.sub('drives')

this._opening = this._open().catch(safetyCatch)
constructor (coreData) {
this._coreData = coreData
this.corestore = this._coreData._corestoreSession
}

/** @returns {import('hyperbee').Iterator<{name: string}>} */
/**
* @deprecated
*/
[Symbol.asyncIterator] () {
if (!this.opened) return emptyIterator
const iterator = this._drives.createReadStream()[Symbol.asyncIterator]()
return {
async next () {
const node = await iterator.next()
const value = node.value
return { done: node.done, value: value && { name: value.key } }
}
}
return new Error('not supported')
}

get closed () {
return this.corestore._root._closing
}

async _open () {
await this._drives.feed.ready()
this.opened = true
return this.corestore._root.closing
}

ready () {
return this._opening
return this._coreData.ready()
}

/** @param {Parameters<import('corestore')['replicate']>} args */
Expand All @@ -61,53 +31,8 @@ class Drivestore {
* Get a Hyperdrive by its name.
*/
get (name = 'public') {
validateName(name)
const ns = this.corestore.namespace(name).session({ primaryKey: this.keyPair.secretKey })
const _preload = ns._preload.bind(ns)
ns._preload = (opts) => this._preload.bind(this)(opts, _preload, ns, name)
return new Hyperdrive(ns)
return this._coreData._getLocalDrive(name)
}

/**
* Set the correct and current key and encryption Key (enables future key rotation)
* @param {Parameters<import('corestore')['get']>[0]} opts
* @param {*} preload orginal ns._preload
* @param {import('corestore')} ns
* @param {string} name
* @returns {Promise<any>}
*/
async _preload (opts, preload, ns, name) {
const isPublic = name === 'public'

// Get keyPair programatically from name
const { from } = await preload(opts)

// public drive needs no encryption
// No need currently to save a record about the public drive
if (isPublic) {
if (opts.name !== 'db') return { from }
const session = this.corestore.get({ keyPair: this.keyPair })
await session.ready()
return { from: session }
}

this._drives.ready().then(async () => {
const saved = await this._drives.get(name)
if (!saved) await this._drives.put(name, b4a.from(''))
// TODO enable key rotation, where we overwrite keys, or use saved ones.
// TODO block closing drivestore before this update is flushed
})

// Add encryption keys for non public drives
return { from, encryptionKey: ns._namespace }
}
}

/** @param {string} name */
function validateName (name) {
if (!/^[0-9a-zA-Z-._ ]*$/.test(name)) throw new Error('Invalid drive name')
}

const emptyIterator = { async next () { return { done: true, value: null } } }

module.exports = Drivestore
10 changes: 2 additions & 8 deletions packages/drive/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,12 @@
"types",
"!**/*.tsbuildinfo"
],
"dependencies": {
"b4a": "^1.6.0",
"corestore": "^6.2.1",
"hyperbee": "^2.0.1",
"hyperdrive": "^11.0.0-alpha.5",
"safety-catch": "^1.0.2"
},
"devDependencies": {
"@synonymdev/slashtags-core-data": "^1.0.0-alpha.9",
"b4a": "^1.6.4",
"brittle": "^3.0.2",
"depcheck": "^1.4.3",
"hypercore-crypto": "^3.3.0",
"random-access-memory": "^5.0.1",
"standard": "^17.0.0",
"typescript": "^4.8.2"
}
Expand Down
15 changes: 9 additions & 6 deletions packages/drive/test/get.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
const test = require('brittle')
const Corestore = require('corestore')
const crypto = require('hypercore-crypto')
const b4a = require('b4a')
const CoreData = require('@synonymdev/slashtags-core-data')

const Drivestore = require('../index.js')
const { tmpdir } = require('./helpers/index.js')

test('get - public drive', async (t) => {
const keyPair = crypto.keyPair()
const drivestore = new Drivestore(new Corestore(tmpdir()), keyPair)
const coreData = new CoreData({ keyPair })
const drivestore = new Drivestore(coreData)

const publicA = drivestore.get('public')
await publicA.ready()
Expand All @@ -17,8 +17,6 @@ test('get - public drive', async (t) => {
const publicB = drivestore.get()
await publicB.ready()
t.alike(publicB.key, keyPair.publicKey)

t.not(publicA, publicB, 'should return a session')
t.alike(publicA.key, publicB.key, 'same public key')
t.absent(publicA.core.encryptionKey, 'do not encrypet public drive')
t.absent(publicB.core.encryptionKey, 'do not encrypet public drive')
Expand All @@ -36,11 +34,14 @@ test('get - public drive', async (t) => {
const buf = b4a.from('bar')
await publicA.put('foo', buf)
t.alike(await publicA.get('foo'), buf)

coreData.close()
})

test('get - private drive basic', async (t) => {
const keyPair = crypto.keyPair()
const drivestore = new Drivestore(new Corestore(tmpdir()), keyPair)
const coreData = new CoreData({ keyPair })
const drivestore = new Drivestore(coreData)

const foo = drivestore.get('foo')
const bar = drivestore.get('bar')
Expand All @@ -67,4 +68,6 @@ test('get - private drive basic', async (t) => {
t.ok(foo.blobs?.core.writable)
t.unlike(foo.blobs?.core.key, foo.key)
t.unlike(bar.blobs?.core.key, foo.key)

coreData.close()
})
33 changes: 6 additions & 27 deletions packages/drive/test/keys.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,30 @@
const test = require('brittle')
const Corestore = require('corestore')
const crypto = require('hypercore-crypto')
const path = require('path')
const fs = require('fs')
const CoreData = require('@synonymdev/slashtags-core-data')

const Drivestore = require('../index.js')
const { tmpdir } = require('./helpers/index.js')

test('dont store secretKey at rest', async (t) => {
const dir = tmpdir()
const corestore = new Corestore(dir)
await corestore.ready()

const drivestore = new Drivestore(corestore, crypto.keyPair())
await drivestore.ready()

t.ok(drivestore.corestore.primaryKey)

await corestore.close()
const stored = fs.readFileSync(path.join(dir, 'primary-key'))
t.unlike(stored, drivestore.corestore.primaryKey)
})

test('unique private drives for unique keyPairs', async (t) => {
const dir = tmpdir()
const corestore = new Corestore(dir)
await corestore.ready()

const kp1 = crypto.keyPair()
const ds1 = new Drivestore(corestore, kp1)
const ds1 = new Drivestore(new CoreData({ keyPair: kp1 }))
await ds1.ready()
const ds1Public = ds1.get()
const ds1Private = ds1.get('contacts')

const kp2 = crypto.keyPair()
const ds2 = new Drivestore(corestore, kp2)
const ds2 = new Drivestore(new CoreData({ keyPair: kp2 }))
await ds2.ready()
const ds2Public = ds2.get('contacts')
const ds2Private = ds2.get('contacts')

await Promise.all([ds1Public, ds2Public, ds1Private, ds2Private].map(d => d.ready()))

t.unlike(ds1.keyPair.secretKey, ds2.keyPair.secretKey)

t.ok(ds1Public.key)
t.unlike(ds2Public.key, ds1Public.key)

t.ok(ds1Private.key)
t.unlike(ds2Private.key, ds1Private.key)

ds1._coreData.close()
ds2._coreData.close()
})
Loading

0 comments on commit d7d0499

Please sign in to comment.