Merge remote-tracking branch 'origin' into feat/encryption

This commit is contained in:
Hayden Young 2025-02-22 15:27:07 +01:00
commit 5f54993660
26 changed files with 3639 additions and 6578 deletions

View File

@ -25,7 +25,7 @@ jobs:
registry-url: https://registry.npmjs.org/ registry-url: https://registry.npmjs.org/
- run: npm ci - run: npm ci
- run: npm run build:docs - run: npm run build:docs
- uses: actions/upload-pages-artifact@v2 - uses: actions/upload-pages-artifact@v3
with: with:
path: ./docs/api/ path: ./docs/api/
@ -37,5 +37,5 @@ jobs:
steps: steps:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v2 # or the latest "vX.X.X" version tag for this action uses: actions/deploy-pages@v4 # or the latest "vX.X.X" version tag for this action

View File

@ -0,0 +1 @@
https://orbitdb.org/funding.json

View File

@ -2,4 +2,10 @@
For now, please refer to our Git commit history for a list of changes. For now, please refer to our Git commit history for a list of changes.
https://github.com/orbitdb/orbitdb/commits/v1.0.2 https://github.com/orbitdb/orbitdb/compare/v2.4.3...v2.5.0
You can also use the following git command to generate a log of changes:
```
git log v2.4.3..v2.5.0 --oneline
```

View File

@ -28,7 +28,7 @@ Install OrbitDB and its dependencies:
npm install @orbitdb/core helia npm install @orbitdb/core helia
``` ```
### Browser <script> tag ### Browser `<script>` tag
OrbitDB can be loaded in the browser using the distributed js file with the `<script/>` tag. OrbitDB is the global namespace and all external functions are available via this namespace: OrbitDB can be loaded in the browser using the distributed js file with the `<script/>` tag. OrbitDB is the global namespace and all external functions are available via this namespace:

View File

@ -72,7 +72,7 @@ const options = {
filter: filters.all filter: filters.all
}) })
], ],
connectionEncryption: [noise()], connectionEncrypters: [noise()],
streamMuxers: [yamux()], streamMuxers: [yamux()],
services: { services: {
identify: identify(), identify: identify(),
@ -114,7 +114,7 @@ const options = {
discoverRelays: 1 discoverRelays: 1
}) })
], ],
connectionEncryption: [noise()], connectionEncrypters: [noise()],
streamMuxers: [yamux()], streamMuxers: [yamux()],
connectionGater: { connectionGater: {
denyDialMultiaddr: () => { denyDialMultiaddr: () => {
@ -180,7 +180,7 @@ const options = {
discoverRelays: 1 discoverRelays: 1
}) })
], ],
connectionEncryption: [noise()], connectionEncrypters: [noise()],
streamMuxers: [yamux()], streamMuxers: [yamux()],
connectionGater: { connectionGater: {
denyDialMultiaddr: () => { denyDialMultiaddr: () => {
@ -247,7 +247,7 @@ const options = {
discoverRelays: 1 discoverRelays: 1
}) })
], ],
connectionEncryption: [noise()], connectionEncrypters: [noise()],
streamMuxers: [yamux()], streamMuxers: [yamux()],
connectionGater: { connectionGater: {
denyDialMultiaddr: () => { denyDialMultiaddr: () => {

View File

@ -264,7 +264,7 @@ CustomDB.type = type
export default CustomDB export default CustomDB
``` ```
[Documents](../src/db/documents.js), [Events](../src/db/events.js) and [KeyValue](../src/db/keyvalue.js) provide good examples of how a database is implemented in OrbitDB and how to add the logic for returning records from the database (the state of the database). [Documents](../src/databases/documents.js), [Events](../src/databases/events.js) and [KeyValue](../src/databases/keyvalue.js) provide good examples of how a database is implemented in OrbitDB and how to add the logic for returning records from the database (the state of the database).
To use a custom database, add it to the list of supported database types: To use a custom database, add it to the list of supported database types:

View File

@ -50,7 +50,7 @@ A simple Node.js example might look something like:
transports: [ transports: [
tcp() tcp()
], ],
connectionEncryption: [noise()], connectionEncrypters: [noise()],
streamMuxers: [yamux()], streamMuxers: [yamux()],
services: { services: {
identify: identify(), identify: identify(),
@ -79,7 +79,7 @@ export const Libp2pOptions = {
transports: [ transports: [
tcp() tcp()
], ],
connectionEncryption: [noise()], connectionEncrypters: [noise()],
streamMuxers: [yamux()], streamMuxers: [yamux()],
services: { services: {
identify: identify(), identify: identify(),
@ -104,7 +104,7 @@ Assuming you have a Node.js development environment installed, create a new proj
mkdir orbitdb-app mkdir orbitdb-app
cd orbitdb-app cd orbitdb-app
npm init npm init
npm i helia orbitdb/core blockstore-level @chainsafe/libp2p-gossipsub npm i helia @orbitdb/core blockstore-level @chainsafe/libp2p-gossipsub
``` ```
Create a file in your project called index.js and add the following code to it: Create a file in your project called index.js and add the following code to it:
@ -159,7 +159,7 @@ const db = await orbitdb.open('my-db')
to: to:
```js ```js
const db = await orbitdb.open('my-documents-db', { type: 'documents '}) const db = await orbitdb.open('my-documents-db', { type: 'documents' })
``` ```
Also replace: Also replace:
@ -172,8 +172,8 @@ await db.add('hello world 2')
with: with:
```js ```js
await db.put('doc1', { hello: "world 1", hits: 5 }) await db.put({ _id: "doc1", hello: "world 1", hits: 5 })
await db.put('doc2', { hello: "world 2", hits: 2 }) await db.put({ _id: "doc2", hello: "world 2", hits: 2 })
``` ```
Run index.js again: Run index.js again:
@ -195,7 +195,7 @@ To create an OrbitDB database peer, create a new project called `orbitdb-peer`:
mkdir orbitdb-peer mkdir orbitdb-peer
cd orbitdb-peer cd orbitdb-peer
npm init npm init
npm i helia orbitdb/core blockstore-level @chainsafe/libp2p-gossipsub npm i helia @orbitdb/core blockstore-level @chainsafe/libp2p-gossipsub
``` ```
Create a new file called index.js and paste in the following code: Create a new file called index.js and paste in the following code:
@ -206,6 +206,7 @@ import { createHelia } from 'helia'
import { createOrbitDB, IPFSAccessController } from '@orbitdb/core' import { createOrbitDB, IPFSAccessController } from '@orbitdb/core'
import { LevelBlockstore } from 'blockstore-level' import { LevelBlockstore } from 'blockstore-level'
import { Libp2pOptions } from './config/libp2p.js' import { Libp2pOptions } from './config/libp2p.js'
import { multiaddr } from '@multiformats/multiaddr'
const main = async () => { const main = async () => {
// create a random directory to avoid OrbitDB conflicts. // create a random directory to avoid OrbitDB conflicts.
@ -219,7 +220,9 @@ const main = async () => {
let db let db
if (process.argv[2]) { if (process.argv[2] && process.argv[3]) {
await orbitdb.ipfs.libp2p.dial(multiaddr(process.argv[3]))
console.log('opening db', process.argv[2])
db = await orbitdb.open(process.argv[2]) db = await orbitdb.open(process.argv[2])
} else { } else {
// When we open a new database, write access is only available to the // When we open a new database, write access is only available to the
@ -230,6 +233,8 @@ const main = async () => {
// access using grant and revoke. // access using grant and revoke.
db = await orbitdb.open('my-db', { AccessController: IPFSAccessController({ write: ['*']}) }) db = await orbitdb.open('my-db', { AccessController: IPFSAccessController({ write: ['*']}) })
console.log('libp2p address', '(copy one of these addresses then dial into this node from the second node)', orbitdb.ipfs.libp2p.getMultiaddrs())
// Copy this output if you want to connect a peer to another. // Copy this output if you want to connect a peer to another.
console.log('my-db address', '(copy my db address and use when launching peer 2)', db.address) console.log('my-db address', '(copy my db address and use when launching peer 2)', db.address)
} }
@ -263,27 +268,33 @@ const main = async () => {
main() main()
``` ```
Open two consoles in your command line terminal. Launch peer 1 from the terminal:
In terminal 1, run the first peer: ```bash
node test.js
```sh
node index.js
``` ```
When running, you should see the address of the database, for example: Once launched you will see some output which may look something like this:
```sh ```
libp2p address (copy one of these addresses then dial into this node from the second node) [
Multiaddr(/ip4/127.0.0.1/tcp/36161/p2p/12D3KooWKFWB78Hka2uPVNYYoXfucWp6rDLsQzr5CFiP67NAo7YF),
Multiaddr(/ip4/192.168.1.22/tcp/36161/p2p/12D3KooWKFWB78Hka2uPVNYYoXfucWp6rDLsQzr5CFiP67NAo7YF),
Multiaddr(/ip4/100.64.100.6/tcp/36161/p2p/12D3KooWKFWB78Hka2uPVNYYoXfucWp6rDLsQzr5CFiP67NAo7YF)
]
my-db address (copy my db address and use when launching peer 2) /orbitdb/zdpuB2aYUCnZ7YUBrDkCWpRLQ8ieUbqJEVRZEd5aDhJBDpBqj my-db address (copy my db address and use when launching peer 2) /orbitdb/zdpuB2aYUCnZ7YUBrDkCWpRLQ8ieUbqJEVRZEd5aDhJBDpBqj
``` ```
Copy the database's address from terminal 1 and, in terminal 2, run: It contains the libp2p address and db address. You will need both of these when connecting from peer 2.
```sh Open another terminal and launch peer 2. The command takes the form `node test.js <orbitdb-address> <libp2p-address>`
node index.js /orbitdb/zdpuB2aYUCnZ7YUBrDkCWpRLQ8ieUbqJEVRZEd5aDhJBDpBqj
```bash
node test.js /orbitdb/zdpuB2aYUCnZ7YUBrDkCWpRLQ8ieUbqJEVRZEd5aDhJBDpBqj /ip4/127.0.0.1/tcp/36161/p2p/12D3KooWKFWB78Hka2uPVNYYoXfucWp6rDLsQzr5CFiP67NAo7YF
``` ```
Both peers will print new records to the terminal as the log is updated. When you stop each peer using ctrl+c, the final state of the database will be printed to the terminal. They should match. What is happening is the second peer is dialing the first peer on the /ip4/ address then opens the database.
**PLEASE NOTE:** **PLEASE NOTE:**

View File

@ -109,7 +109,7 @@ const identity = await identities.createIdentity({ id: 'me' })
const cid = CID.parse(identity.hash, base58btc) const cid = CID.parse(identity.hash, base58btc)
// Extract the hash from the full db path. // Extract the hash from the full db path.
const bytes = await ipfs.block.get(cid) const bytes = await ipfs.blockstore.get(cid)
// Defines how we serialize/hash the data. // Defines how we serialize/hash the data.
const codec = dagCbor const codec = dagCbor

View File

@ -1,4 +1,4 @@
## OrbitDB API - v2.2 ## OrbitDB API - v2.5
OrbitDB is a serverless, distributed, peer-to-peer database. OrbitDB uses IPFS OrbitDB is a serverless, distributed, peer-to-peer database. OrbitDB uses IPFS
as its data storage and Libp2p Pubsub to automatically sync databases with peers. It's an eventually consistent database that uses Merkle-CRDTs for conflict-free database writes and merges making OrbitDB an excellent choice for p2p and decentralized apps, blockchain applications and local first web applications. as its data storage and Libp2p Pubsub to automatically sync databases with peers. It's an eventually consistent database that uses Merkle-CRDTs for conflict-free database writes and merges making OrbitDB an excellent choice for p2p and decentralized apps, blockchain applications and local first web applications.

9963
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@orbitdb/core", "name": "@orbitdb/core",
"version": "2.2.0", "version": "2.5.0",
"description": "Distributed p2p database on IPFS", "description": "Distributed p2p database on IPFS",
"author": "Haad", "author": "Haad",
"license": "MIT", "license": "MIT",
@ -19,8 +19,7 @@
"main": "src/index.js", "main": "src/index.js",
"dependencies": { "dependencies": {
"@ipld/dag-cbor": "^9.0.6", "@ipld/dag-cbor": "^9.0.6",
"@libp2p/crypto": "^3.0.2", "@libp2p/crypto": "^5.0.5",
"eth-crypto": "^2.6.0",
"it-pipe": "^3.0.1", "it-pipe": "^3.0.1",
"level": "^8.0.0", "level": "^8.0.0",
"lru": "^3.1.0", "lru": "^3.1.0",
@ -30,23 +29,20 @@
"uint8arrays": "^5.0.0" "uint8arrays": "^5.0.0"
}, },
"devDependencies": { "devDependencies": {
"@chainsafe/libp2p-gossipsub": "^13.0.0", "@chainsafe/libp2p-gossipsub": "^14.1.0",
"@chainsafe/libp2p-yamux": "^6.0.1", "@libp2p/circuit-relay-v2": "^3.1.0",
"@helia/block-brokers": "^1.0.0", "blockstore-level": "^2.0.1",
"@libp2p/circuit-relay-v2": "^1.0.10",
"blockstore-level": "^1.1.7",
"c8": "^8.0.1", "c8": "^8.0.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"fs-extra": "^11.2.0", "fs-extra": "^11.2.0",
"helia": "^4.0.1", "helia": "^5.1.0",
"it-all": "^3.0.4", "it-all": "^3.0.4",
"jsdoc": "^4.0.2", "jsdoc": "^4.0.2",
"mocha": "^10.2.0", "mocha": "^10.2.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"playwright-test": "^14.0.0", "playwright-test": "^14.1.6",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"standard": "^17.1.0", "standard": "^17.1.0",
"stream-browserify": "^3.0.0",
"webpack": "^5.89.0", "webpack": "^5.89.0",
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"
}, },

View File

@ -84,7 +84,7 @@ const IPFSAccessController = ({ write, storage } = {}) => async ({ orbitdb, iden
// Allow if the write access list contain the writer's id or is '*' // Allow if the write access list contain the writer's id or is '*'
if (write.includes(id) || write.includes('*')) { if (write.includes(id) || write.includes('*')) {
// Check that the identity is valid // Check that the identity is valid
return identities.verifyIdentity(writerIdentity) return await identities.verifyIdentity(writerIdentity)
} }
return false return false
} }

View File

@ -58,7 +58,7 @@ const OrbitDBAccessController = ({ write } = {}) => async ({ orbitdb, identities
// If the ACL contains the writer's public key or it contains '*' // If the ACL contains the writer's public key or it contains '*'
const hasWriteAccess = await hasCapability('write', id) || await hasCapability('admin', id) const hasWriteAccess = await hasCapability('write', id) || await hasCapability('admin', id)
if (hasWriteAccess) { if (hasWriteAccess) {
return identities.verifyIdentity(writerIdentity) return await identities.verifyIdentity(writerIdentity)
} }
return false return false

View File

@ -52,7 +52,7 @@ const PublicKeyIdentityProvider = ({ keystore }) => async () => {
} }
const key = await keystore.getKey(id) || await keystore.createKey(id) const key = await keystore.getKey(id) || await keystore.createKey(id)
return uint8ArrayToString(key.public.marshal(), 'base16') return uint8ArrayToString(key.publicKey.raw, 'base16')
} }
/** /**

View File

@ -8,7 +8,7 @@
* const storage = await MemoryStorage() * const storage = await MemoryStorage()
* const keystore = await KeyStore({ storage }) * const keystore = await KeyStore({ storage })
*/ */
import * as crypto from '@libp2p/crypto' import { privateKeyFromRaw, publicKeyFromRaw, generateKeyPair } from '@libp2p/crypto/keys'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import { compare as uint8ArrayCompare } from 'uint8arrays/compare' import { compare as uint8ArrayCompare } from 'uint8arrays/compare'
@ -16,9 +16,6 @@ import ComposedStorage from './storage/composed.js'
import LevelStorage from './storage/level.js' import LevelStorage from './storage/level.js'
import LRUStorage from './storage/lru.js' import LRUStorage from './storage/lru.js'
const unmarshal = crypto.keys.supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey
const unmarshalPubKey = crypto.keys.supportedKeys.secp256k1.unmarshalSecp256k1PublicKey
const verifySignature = async (signature, publicKey, data) => { const verifySignature = async (signature, publicKey, data) => {
if (!signature) { if (!signature) {
throw new Error('No signature given') throw new Error('No signature given')
@ -38,7 +35,7 @@ const verifySignature = async (signature, publicKey, data) => {
let res = false let res = false
try { try {
const pubKey = unmarshalPubKey(uint8ArrayFromString(publicKey, 'base16')) const pubKey = publicKeyFromRaw(uint8ArrayFromString(publicKey, 'base16'))
res = await isValid(pubKey, data, uint8ArrayFromString(signature, 'base16')) res = await isValid(pubKey, data, uint8ArrayFromString(signature, 'base16'))
} catch (e) { } catch (e) {
// Catch error: sig length wrong // Catch error: sig length wrong
@ -195,7 +192,7 @@ const KeyStore = async ({ storage, path } = {}) => {
const { privateKey } = key const { privateKey } = key
await storage.put('private_' + id, privateKey) await storage.put('private_' + id, privateKey)
// Unmarshal the key and add it to the cache // Unmarshal the key and add it to the cache
const unmarshaledPrivateKey = unmarshal(privateKey) const unmarshaledPrivateKey = privateKeyFromRaw(privateKey)
await keyCache.put(id, unmarshaledPrivateKey) await keyCache.put(id, unmarshaledPrivateKey)
} }
@ -213,17 +210,16 @@ const KeyStore = async ({ storage, path } = {}) => {
} }
// Generate a private key // Generate a private key
const keyPair = await crypto.keys.generateKeyPair('secp256k1') const keyPair = await generateKeyPair('secp256k1')
const keys = await crypto.keys.unmarshalPrivateKey(keyPair.bytes)
const key = { const key = {
publicKey: keys.public.marshal(), publicKey: keyPair.publicKey.raw,
privateKey: keys.marshal() privateKey: keyPair.raw
} }
await addKey(id, key) await addKey(id, key)
return keys return keyPair
} }
/** /**
@ -254,7 +250,8 @@ const KeyStore = async ({ storage, path } = {}) => {
return return
} }
key = unmarshal(storedKey) key = privateKeyFromRaw(storedKey)
await keyCache.put(id, key) await keyCache.put(id, key)
} }
@ -281,7 +278,7 @@ const KeyStore = async ({ storage, path } = {}) => {
throw new Error('Supported formats are `hex` and `buffer`') throw new Error('Supported formats are `hex` and `buffer`')
} }
const pubKey = keys.public.marshal() const pubKey = keys.publicKey.raw
return format === 'buffer' ? pubKey : uint8ArrayToString(pubKey, 'base16') return format === 'buffer' ? pubKey : uint8ArrayToString(pubKey, 'base16')
} }

View File

@ -16,13 +16,17 @@ const ManifestStore = async ({ ipfs, storage } = {}) => {
*/ */
storage = storage || await ComposedStorage( storage = storage || await ComposedStorage(
await LRUStorage({ size: 1000 }), await LRUStorage({ size: 100000 }),
await IPFSBlockStorage({ ipfs, pin: true }) await IPFSBlockStorage({ ipfs, pin: true })
) )
const get = async (address) => { const get = async (address) => {
const bytes = await storage.get(address) const bytes = await storage.get(address)
const { value } = await Block.decode({ bytes, codec, hasher }) const { value } = await Block.decode({ bytes, codec, hasher })
if (value) {
// Write to storage to make sure it gets pinned on IPFS
await storage.put(address, bytes)
}
return value return value
} }

View File

@ -194,6 +194,7 @@ const OrbitDB = async ({ ipfs, id, identity, identities, directory } = {}) => {
ipfs, ipfs,
directory, directory,
keystore, keystore,
identities,
identity, identity,
peerId peerId
} }

View File

@ -197,9 +197,8 @@ const Sync = async ({ ipfs, log, events, onSynced, start, timeout }) => {
const stream = await libp2p.dialProtocol(remotePeer, headsSyncAddress, { signal }) const stream = await libp2p.dialProtocol(remotePeer, headsSyncAddress, { signal })
await pipe(sendHeads, stream, receiveHeads(peerId)) await pipe(sendHeads, stream, receiveHeads(peerId))
} catch (e) { } catch (e) {
console.error(e)
peers.delete(peerId) peers.delete(peerId)
if (e.code === 'ERR_UNSUPPORTED_PROTOCOL') { if (e.name === 'UnsupportedProtocolError') {
// Skip peer, they don't have this database currently // Skip peer, they don't have this database currently
} else { } else {
events.emit('error', e) events.emit('error', e)
@ -236,6 +235,10 @@ const Sync = async ({ ipfs, log, events, onSynced, start, timeout }) => {
} }
} }
const handlePeerDisconnected = async event => {
peers.delete(event.detail.toString())
}
/** /**
* Add a log entry to the Sync Protocol to be sent to peers. * Add a log entry to the Sync Protocol to be sent to peers.
* @function add * @function add
@ -264,6 +267,7 @@ const Sync = async ({ ipfs, log, events, onSynced, start, timeout }) => {
pubsub.removeEventListener('message', handleUpdateMessage) pubsub.removeEventListener('message', handleUpdateMessage)
await libp2p.unhandle(headsSyncAddress) await libp2p.unhandle(headsSyncAddress)
await pubsub.unsubscribe(address) await pubsub.unsubscribe(address)
libp2p.removeEventListener('peer:disconnect', handlePeerDisconnected)
peers.clear() peers.clear()
} }
} }
@ -282,6 +286,8 @@ const Sync = async ({ ipfs, log, events, onSynced, start, timeout }) => {
pubsub.addEventListener('message', handleUpdateMessage) pubsub.addEventListener('message', handleUpdateMessage)
// Subscribe to the pubsub channel for this database through which updates are sent // Subscribe to the pubsub channel for this database through which updates are sent
await pubsub.subscribe(address) await pubsub.subscribe(address)
// Remove disconnected peers from `peers`, as otherwise they will not resync heads on reconnection
libp2p.addEventListener('peer:disconnect', handlePeerDisconnected)
started = true started = true
} }
} }

View File

@ -1,10 +1,7 @@
import * as crypto from '@libp2p/crypto' import { privateKeyFromRaw } from '@libp2p/crypto/keys'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { Identities, KeyStore } from '../../src/index.js' import { Identities, KeyStore } from '../../src/index.js'
const unmarshal = crypto.keys.supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey
const unmarshalPubKey = crypto.keys.supportedKeys.secp256k1.unmarshalSecp256k1PublicKey
const keysPath = './testkeys' const keysPath = './testkeys'
const isBrowser = () => typeof window !== 'undefined' const isBrowser = () => typeof window !== 'undefined'
@ -52,10 +49,10 @@ before(async () => {
] ]
for (let user of users) { for (let user of users) {
const privateKey1 = unmarshal(uint8ArrayFromString(user.privateKey, 'base16')) const privateKey1 = privateKeyFromRaw(uint8ArrayFromString(user.privateKey, 'base16'))
const privateKey2 = unmarshal(uint8ArrayFromString(user.identity.privateKey, 'base16')) const privateKey2 = privateKeyFromRaw(uint8ArrayFromString(user.identity.privateKey, 'base16'))
await keystore.addKey(user.id, { privateKey: privateKey1.marshal() }) await keystore.addKey(user.id, { privateKey: privateKey1.raw })
await keystore.addKey(user.identity.id, { privateKey: privateKey2.marshal() }) await keystore.addKey(user.identity.id, { privateKey: privateKey2.raw })
} }
await keystore.close() await keystore.close()

View File

@ -11,7 +11,7 @@ import createHelia from '../../utils/create-helia.js'
const keysPath = './testkeys' const keysPath = './testkeys'
describe('Documents Database Replication', function () { describe('Documents Database Replication', function () {
this.timeout(30000) this.timeout(10000)
let ipfs1, ipfs2 let ipfs1, ipfs2
let keystore let keystore

View File

@ -38,7 +38,7 @@ describe('Identities', function () {
identities = await Identities({ path: keysPath }) identities = await Identities({ path: keysPath })
identity = await identities.createIdentity({ id }) identity = await identities.createIdentity({ id })
const key = await identities.keystore.getKey(id) const key = await identities.keystore.getKey(id)
const externalId = uint8ArrayToString(key.public.marshal(), 'base16') const externalId = uint8ArrayToString(key.publicKey.raw, 'base16')
assert.strictEqual(identity.id, externalId) assert.strictEqual(identity.id, externalId)
}) })
}) })
@ -106,7 +106,7 @@ describe('Identities', function () {
identity = await identities.createIdentity({ id }) identity = await identities.createIdentity({ id })
keystore = identities.keystore keystore = identities.keystore
const key = await keystore.getKey(id) const key = await keystore.getKey(id)
const externalId = uint8ArrayToString(key.public.marshal(), 'base16') const externalId = uint8ArrayToString(key.publicKey.raw, 'base16')
assert.strictEqual(identity.id, externalId) assert.strictEqual(identity.id, externalId)
}) })
@ -117,7 +117,7 @@ describe('Identities', function () {
it('has the correct public key', async () => { it('has the correct public key', async () => {
const key = await keystore.getKey(id) const key = await keystore.getKey(id)
const externalId = uint8ArrayToString(key.public.marshal(), 'base16') const externalId = uint8ArrayToString(key.publicKey.raw, 'base16')
const signingKey = await keystore.getKey(externalId) const signingKey = await keystore.getKey(externalId)
assert.notStrictEqual(signingKey, undefined) assert.notStrictEqual(signingKey, undefined)
assert.strictEqual(identity.publicKey, keystore.getPublic(signingKey)) assert.strictEqual(identity.publicKey, keystore.getPublic(signingKey))
@ -125,10 +125,10 @@ describe('Identities', function () {
it('has a signature for the id', async () => { it('has a signature for the id', async () => {
const key = await keystore.getKey(id) const key = await keystore.getKey(id)
const externalId = uint8ArrayToString(key.public.marshal(), 'base16') const externalId = uint8ArrayToString(key.publicKey.raw, 'base16')
const signingKey = await keystore.getKey(externalId) const signingKey = await keystore.getKey(externalId)
const idSignature = await signMessage(signingKey, externalId) const idSignature = await signMessage(signingKey, externalId)
const publicKey = uint8ArrayToString(signingKey.public.marshal(), 'base16') const publicKey = uint8ArrayToString(signingKey.publicKey.raw, 'base16')
const verifies = await verifyMessage(idSignature, publicKey, externalId) const verifies = await verifyMessage(idSignature, publicKey, externalId)
assert.strictEqual(verifies, true) assert.strictEqual(verifies, true)
assert.strictEqual(identity.signatures.id, idSignature) assert.strictEqual(identity.signatures.id, idSignature)
@ -136,7 +136,7 @@ describe('Identities', function () {
it('has a signature for the publicKey', async () => { it('has a signature for the publicKey', async () => {
const key = await keystore.getKey(id) const key = await keystore.getKey(id)
const externalId = uint8ArrayToString(key.public.marshal(), 'base16') const externalId = uint8ArrayToString(key.publicKey.raw, 'base16')
const signingKey = await keystore.getKey(externalId) const signingKey = await keystore.getKey(externalId)
const idSignature = await signMessage(signingKey, externalId) const idSignature = await signMessage(signingKey, externalId)
const externalKey = await keystore.getKey(id) const externalKey = await keystore.getKey(id)
@ -171,7 +171,7 @@ describe('Identities', function () {
it('has the correct id', async () => { it('has the correct id', async () => {
const key = await savedKeysKeyStore.getKey(id) const key = await savedKeysKeyStore.getKey(id)
assert.strictEqual(identity.id, uint8ArrayToString(key.public.marshal(), 'base16')) assert.strictEqual(identity.id, uint8ArrayToString(key.publicKey.raw, 'base16'))
}) })
it('has the correct public key', async () => { it('has the correct public key', async () => {

View File

@ -145,7 +145,7 @@ describe('KeyStore', () => {
}) })
describe('Options', () => { describe('Options', () => {
const unmarshal = crypto.keys.supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey const unmarshal = crypto.keys.privateKeyFromRaw
const privateKey = '198594a8de39fd97017d11996d619b3746211605a9d290964badf58bc79bdb33' const privateKey = '198594a8de39fd97017d11996d619b3746211605a9d290964badf58bc79bdb33'
const publicKey = '0260baeaffa1de1e4135e5b395e0380563a622b9599d1b8e012a0f7603f516bdaa' const publicKey = '0260baeaffa1de1e4135e5b395e0380563a622b9599d1b8e012a0f7603f516bdaa'
let privateKeyBuffer, publicKeyBuffer, unmarshalledPrivateKey let privateKeyBuffer, publicKeyBuffer, unmarshalledPrivateKey

View File

@ -1,9 +1,11 @@
import { deepStrictEqual } from 'assert' import { deepStrictEqual, strictEqual } from 'assert'
import { rimraf } from 'rimraf' import { rimraf } from 'rimraf'
import { createOrbitDB } from '../src/index.js' import { createOrbitDB } from '../src/index.js'
import connectPeers from './utils/connect-nodes.js' import connectPeers from './utils/connect-nodes.js'
import waitFor from './utils/wait-for.js' import waitFor from './utils/wait-for.js'
import createHelia from './utils/create-helia.js' import createHelia from './utils/create-helia.js'
import { CID } from 'multiformats/cid'
import { base58btc } from 'multiformats/bases/base58'
describe('Replicating databases', function () { describe('Replicating databases', function () {
this.timeout(10000) this.timeout(10000)
@ -23,8 +25,8 @@ describe('Replicating databases', function () {
after(async () => { after(async () => {
await orbitdb1.stop() await orbitdb1.stop()
await orbitdb2.stop() await orbitdb2.stop()
await ipfs1.blockstore.child.child.close() await ipfs1.blockstore.child.child.child.close()
await ipfs2.blockstore.child.child.close() await ipfs2.blockstore.child.child.child.close()
await ipfs1.stop() await ipfs1.stop()
await ipfs2.stop() await ipfs2.stop()
@ -136,8 +138,12 @@ describe('Replicating databases', function () {
await orbitdb1.stop() await orbitdb1.stop()
await orbitdb2.stop() await orbitdb2.stop()
await ipfs1.blockstore.child.child.close() // TODO: Strange issue with ClassicLevel. Causes subsequent Helia
await ipfs2.blockstore.child.child.close() // instantiations to error with db closed. Explicitly closing the
// nested ClassicLevel db seems to resolve the issue. Requires further
// investigation.
await ipfs1.blockstore.child.child.child.close()
await ipfs2.blockstore.child.child.child.close()
await ipfs1.stop() await ipfs1.stop()
await ipfs2.stop() await ipfs2.stop()
@ -163,5 +169,26 @@ describe('Replicating databases', function () {
console.log('events:', amount) console.log('events:', amount)
}) })
it('pins all entries in the replicated database', async () => {
const db1 = await orbitdb1.open('helloworld', { referencesCount: 0 })
const hash = await db1.add('hello world')
let replicated = false
const onJoin = async (peerId, heads) => {
replicated = true
}
const db2 = await orbitdb2.open(db1.address)
db2.events.on('join', onJoin)
await waitFor(() => replicated, () => true)
const cid = CID.parse(hash, base58btc)
strictEqual(await ipfs1.pins.isPinned(cid), true)
strictEqual(await ipfs2.pins.isPinned(cid), true)
})
}) })
}) })

View File

@ -72,8 +72,8 @@ describe('OrbitDB', function () {
const privateKey = await orbitdb1.keystore.getKey(orbitdb1.identity.id) const privateKey = await orbitdb1.keystore.getKey(orbitdb1.identity.id)
notStrictEqual(privateKey, undefined) notStrictEqual(privateKey, undefined)
strictEqual(privateKey.constructor.name, 'Secp256k1PrivateKey') strictEqual(privateKey.constructor.name, 'Secp256k1PrivateKey')
notStrictEqual(privateKey._key, undefined) notStrictEqual(privateKey.raw, undefined)
notStrictEqual(privateKey._publicKey, undefined) notStrictEqual(privateKey.publicKey, undefined)
}) })
it('has a keystore that contains a public key that matches the identity\'s public key', async () => { it('has a keystore that contains a public key that matches the identity\'s public key', async () => {
@ -102,8 +102,8 @@ describe('OrbitDB', function () {
notStrictEqual(orbitdb1.peerId, undefined) notStrictEqual(orbitdb1.peerId, undefined)
}) })
it('has a peerId of type Ed25519PeerIdImpl', async () => { it('has a peerId of type Ed25519', async () => {
strictEqual(orbitdb1.peerId.constructor.name, 'Ed25519PeerIdImpl') strictEqual(orbitdb1.peerId.type, 'Ed25519')
}) })
it('has a peerId that matches the IPFS id', async () => { it('has a peerId that matches the IPFS id', async () => {
@ -164,8 +164,8 @@ describe('OrbitDB', function () {
const privateKey = await orbitdb1.keystore.getKey(orbitdb1.identity.id) const privateKey = await orbitdb1.keystore.getKey(orbitdb1.identity.id)
notStrictEqual(privateKey, undefined) notStrictEqual(privateKey, undefined)
strictEqual(privateKey.constructor.name, 'Secp256k1PrivateKey') strictEqual(privateKey.constructor.name, 'Secp256k1PrivateKey')
notStrictEqual(privateKey._key, undefined) notStrictEqual(privateKey.raw, undefined)
notStrictEqual(privateKey._publicKey, undefined) notStrictEqual(privateKey.publicKey, undefined)
}) })
it('has a keystore that contains a public key that matches the identity\'s public key', async () => { it('has a keystore that contains a public key that matches the identity\'s public key', async () => {
@ -194,8 +194,8 @@ describe('OrbitDB', function () {
notStrictEqual(orbitdb1.peerId, undefined) notStrictEqual(orbitdb1.peerId, undefined)
}) })
it('has a peerId of type Ed25519PeerIdImpl', async () => { it('has a peerId of type Ed25519', async () => {
strictEqual(orbitdb1.peerId.constructor.name, 'Ed25519PeerIdImpl') strictEqual(orbitdb1.peerId.type, 'Ed25519')
}) })
it('has a peerId that matches the IPFS id', async () => { it('has a peerId that matches the IPFS id', async () => {

View File

@ -21,13 +21,9 @@ const Libp2pOptions = {
transports: [ transports: [
webSockets({ webSockets({
filter: all filter: all
}),
webRTC(),
circuitRelayTransport({
discoverRelays: 1
}) })
], ],
connectionEncryption: [noise()], connectionEncrypters: [noise()],
streamMuxers: [yamux()], streamMuxers: [yamux()],
connectionGater: { connectionGater: {
denyDialMultiaddr: () => false denyDialMultiaddr: () => false
@ -43,18 +39,16 @@ const Libp2pOptions = {
*/ */
const Libp2pBrowserOptions = { const Libp2pBrowserOptions = {
addresses: { addresses: {
listen: ['/webrtc'] listen: ['/webrtc', '/p2p-circuit']
}, },
transports: [ transports: [
webSockets({ webSockets({
filter: all filter: all
}), }),
webRTC(), webRTC(),
circuitRelayTransport({ circuitRelayTransport()
discoverRelays: 1
})
], ],
connectionEncryption: [noise()], connectionEncrypters: [noise()],
streamMuxers: [yamux()], streamMuxers: [yamux()],
connectionGater: { connectionGater: {
denyDialMultiaddr: () => false denyDialMultiaddr: () => false

View File

@ -5,21 +5,18 @@ import { circuitRelayServer } from '@libp2p/circuit-relay-v2'
import { webSockets } from '@libp2p/websockets' import { webSockets } from '@libp2p/websockets'
import * as filters from '@libp2p/websockets/filters' import * as filters from '@libp2p/websockets/filters'
import { identify } from '@libp2p/identify' import { identify } from '@libp2p/identify'
import { createFromPrivKey } from '@libp2p/peer-id-factory'
import { unmarshalPrivateKey } from '@libp2p/crypto/keys'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { privateKeyFromProtobuf } from '@libp2p/crypto/keys'
// output of: console.log(server.peerId.privateKey.toString('hex')) // output of: console.log(server.peerId.privateKey.toString('hex'))
const relayPrivKey = '08011240821cb6bc3d4547fcccb513e82e4d718089f8a166b23ffcd4a436754b6b0774cf07447d1693cd10ce11ef950d7517bad6e9472b41a927cd17fc3fb23f8c70cd99' const relayPrivKey = '08011240821cb6bc3d4547fcccb513e82e4d718089f8a166b23ffcd4a436754b6b0774cf07447d1693cd10ce11ef950d7517bad6e9472b41a927cd17fc3fb23f8c70cd99'
// the peer id of the above key // the peer id of the above key
// const relayId = '12D3KooWAJjbRkp8FPF5MKgMU53aUTxWkqvDrs4zc1VMbwRwfsbE' // const relayId = '12D3KooWAJjbRkp8FPF5MKgMU53aUTxWkqvDrs4zc1VMbwRwfsbE'
const encoded = uint8ArrayFromString(relayPrivKey, 'hex') const privateKey = privateKeyFromProtobuf(uint8ArrayFromString(relayPrivKey, 'hex'))
const privateKey = await unmarshalPrivateKey(encoded)
const peerId = await createFromPrivKey(privateKey)
const server = await createLibp2p({ const server = await createLibp2p({
peerId, privateKey,
addresses: { addresses: {
listen: ['/ip4/0.0.0.0/tcp/12345/ws'] listen: ['/ip4/0.0.0.0/tcp/12345/ws']
}, },
@ -28,14 +25,13 @@ const server = await createLibp2p({
filter: filters.all filter: filters.all
}) })
], ],
connectionEncryption: [noise()], connectionEncrypters: [noise()],
streamMuxers: [yamux()], streamMuxers: [yamux()],
services: { services: {
identify: identify(), identify: identify(),
relay: circuitRelayServer({ relay: circuitRelayServer({
reservations: { reservations: {
maxReservations: 5000, maxReservations: 5000,
reservationTtl: 1000,
defaultDataLimit: BigInt(1024 * 1024 * 1024) defaultDataLimit: BigInt(1024 * 1024 * 1024)
} }
}) })