docs: Setting up blockstore storage and simple Libp2p configuation.

This commit is contained in:
Hayden Young 2024-01-18 20:48:04 +00:00
parent eb5104ead6
commit 8a9ebc6ca1
6 changed files with 140 additions and 48 deletions

View File

@ -9,10 +9,11 @@ Different access controllers can be assigned to the database using the `AccessCo
```js
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB, DefaultLibp2pOptions } from '@orbitdb/core'
import { createOrbitDB } from '@orbitdb/core'
import * as SomeAccessController from 'some-access-controller.js'
import { Libp2pOptions } from './config/libp2p.js'
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })
const orbitdb = await createOrbitDB({ ipfs })
@ -39,8 +40,9 @@ To change write access, pass the IPFSAccessController with the `write` parameter
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB, Identities, IPFSAccessController } from '@orbitdb/core'
import { Libp2pOptions } from './config/libp2p.js'
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })
const identities = await Identities()
@ -59,8 +61,9 @@ To allow anyone to write to the database, specify the wildcard '*':
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB, Identities, IPFSAccessController } from '@orbitdb/core'
import { Libp2pOptions } from './config/libp2p.js'
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })
const orbitdb = await createOrbitDB({ ipfs })
@ -78,8 +81,9 @@ The OrbitDB access controller provides configurable write access using grant and
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB, Identities, OrbitDBAccessController } from '@orbitdb/core'
import { Libp2pOptions } from './config/libp2p.js'
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })
const orbitdb = await createOrbitDB({ ipfs })
@ -166,8 +170,9 @@ Before passing the custom access controller to the `open` function, it must be a
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB, useAccessController } from '@orbitdb/core'
import { Libp2pOptions } from './config/libp2p.js'
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })
useAccessController(CustomAccessController)

View File

@ -9,9 +9,10 @@ Node.js allows libp2p to open connections with other Node.js daemons.
```javascript
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { Libp2pOptions } from './config/libp2p.js'
const initIPFSInstance = () => {
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
return createHelia({ libp2p })
}
@ -29,9 +30,10 @@ In remote networks, retrieval of content across peers may take significantly lon
```javascript
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { Libp2pOptions } from './config/libp2p.js'
const initIPFSInstance = () => {
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
return createHelia({ libp2p })
}
@ -56,8 +58,8 @@ import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { yamux } from '@chainsafe/libp2p-yamux'
import { noise } from '@chainsafe/libp2p-noise'
import { identifyService } from 'libp2p/identify'
import { circuitRelayServer} from 'libp2p/circuit-relay'
import { identify } from '@libp2p/identify'
import { circuitRelayServer } from '@libp2p/circuit-relay-v2'
import { webSockets } from '@libp2p/websockets'
import * as filters from '@libp2p/websockets/filters'
@ -73,7 +75,7 @@ const options = {
connectionEncryption: [noise()],
streamMuxers: [yamux()],
services: {
identify: identifyService(),
identify: identify(),
relay: circuitRelayServer()
}
}
@ -89,11 +91,11 @@ Within the browser, dial into the server using the server's exposed web socket:
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { yamux } from '@chainsafe/libp2p-yamux'
import { identifyService } from 'libp2p/identify'
import { identify } from '@libp2p/identify'
import { webSockets } from '@libp2p/websockets'
import { webRTC } from '@libp2p/webrtc'
import { noise } from '@chainsafe/libp2p-noise'
import { circuitRelayTransport } from 'libp2p/circuit-relay'
import { circuitRelayTransport } from '@libp2p/circuit-relay-v2'
const ws = new webSockets()
@ -120,7 +122,7 @@ const options = {
}
},
services: {
identify: identifyService()
identify: identify()
}
}
@ -153,11 +155,11 @@ In the first browser peer, dial the relay to discover the browser peer's address
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { yamux } from '@chainsafe/libp2p-yamux'
import { identifyService } from 'libp2p/identify'
import { identify } from '@libp2p/identify'
import { webSockets } from '@libp2p/websockets'
import { webRTC } from '@libp2p/webrtc'
import { noise } from '@chainsafe/libp2p-noise'
import { circuitRelayTransport } from 'libp2p/circuit-relay'
import { circuitRelayTransport } from '@libp2p/circuit-relay-v2'
import { multiaddr } from '@multiformats/multiaddr'
import { WebRTC as WebRTCMatcher } from '@multiformats/multiaddr-matcher'
import pRetry from 'p-retry'
@ -186,7 +188,7 @@ const options = {
}
},
services: {
identify: identifyService()
identify: identify()
}
}
@ -220,11 +222,11 @@ Configure the second browser node in the same way as the first, then dial in to
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { yamux } from '@chainsafe/libp2p-yamux'
import { identifyService } from 'libp2p/identify'
import { identify } from '@libp2p/identify'
import { webSockets } from '@libp2p/websockets'
import { webRTC } from '@libp2p/webrtc'
import { noise } from '@chainsafe/libp2p-noise'
import { circuitRelayTransport } from 'libp2p/circuit-relay'
import { circuitRelayTransport } from '@libp2p/circuit-relay-v2'
import { multiaddr } from '@multiformats/multiaddr'
import { WebRTC as WebRTCMatcher } from '@multiformats/multiaddr-matcher'
import pRetry from 'p-retry'
@ -253,7 +255,7 @@ const options = {
}
},
services: {
identify: identifyService()
identify: identify()
}
}

View File

@ -37,9 +37,10 @@ In order to replicate the database with peers, the address is what you need to g
```js
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB, DefaultLibp2pOptions } from '@orbitdb/core'
import { createOrbitDB } from '@orbitdb/core'
import { Libp2pOptions } from './config/libp2p.js'
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })
const orbitdb = await createOrbitDB({ ipfs })
@ -68,13 +69,14 @@ The manifest is an [IPLD data structure](https://ipld.io/docs/) which can be ret
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import * as Block from 'multiformats/block'
import { createOrbitDB, OrbitDBAddress, DefaultLibp2pOptions } from '@orbitdb/core'
import { createOrbitDB, OrbitDBAddress } from '@orbitdb/core'
import * as dagCbor from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'
import { base58btc } from 'multiformats/bases/base58'
import { CID } from 'multiformats/cid'
import { Libp2pOptions } from './config/libp2p.js'
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })
// Create the db then close.
@ -174,15 +176,16 @@ await db.del(hash)
The power of OrbitDB lies in its ability to replicate databases across distributed systems that may not always be connected.
A simple way to replicate a database between peers can be accomplished by opening a database, listening for updates and iterating over the records as those updates occur.
A simple way to replicate a database between peers can be accomplished by connecting one peer to another and then opening the database by its address:
```js
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB } from '@orbitdb/core'
import { Libp2pOptions } from './config/libp2p.js'
const initIPFSInstance = () => {
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
return createHelia({ libp2p })
}
@ -200,18 +203,9 @@ await db1.add('hello world')
// database heads will be synchronized.
const db2 = await orbitdb2.open(db1.address)
// We only have the latest record of db1. To replicate all of db1's records, we will
// need to iterate over db1's entire record set.
// We can determine when heads have been synchronized from db1 to db2 by
// listening for the "update" event and iterating over the record set.
db2.events.on('update', async (entry) => {
for await (const record of db2.iterator()) {
for await (const record of db2.iterator()) {
console.log(record)
}
// we can use the convenience function db.all() instead of iterating over
// db2's records.
// await db2.all()
})
}
```
To learn more, check out [OrbitDB's sychronization protocol](https://orbitdb.org/api/module-Sync.html) and the [OrbitDB replication documentation](./REPLICATION.md).

View File

@ -16,6 +16,95 @@ You will also need Helia for replication:
npm i helia
```
## Prerequisites: Helia and Libp2p
OrbitDB uses Helia for block storage and Libp2p for database synchronization. However, you need to configure Helia and pass it to OrbitDB when creating a peer.
### Block Storage
Helia uses memory block storage by default. This means that storage is destroyed every time your application ends and OrbitDB will no longer be able to retrieve blocks from Helia. Therefore, it is necessary to configure Helia with permanent block storage. Helia comes with [a variety of storage solutions](https://github.com/ipfs-examples/helia-101#blockstore) including filesystem storage, IndexDB and Level. To add one of these storage mechanisms to Helia, install the relevant package:
```
npm i blockstore-level
```
then instantiate and pass to Helia:
```
import { LevelBlockstore } from 'blockstore-level'
const blockstore = new LevelBlockstore('./ipfs')
const ipfs = createHelia({ blockstore })
```
### Libp2p
OrbitDB synchronizes databases between peers using a p2p networking stack called [Libp2p](https://github.com/libp2p/js-libp2p/).
An instance of Libp2p is required by Helia which is then used by OrbitDB to synchronize database data across various networks.
A simple Node.js example might look something like:
```json
{
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0/ws']
},
transports: [
webSockets({
filter: all
}),
webRTC(),
circuitRelayTransport({
discoverRelays: 1
})
],
connectionEncryption: [noise()],
streamMuxers: [yamux()],
connectionGater: {
denyDialMultiaddr: () => false
},
services: {
identify: identify(),
pubsub: gossipsub({ allowPublishToZeroPeers: true })
}
}
```
You can export the above configuration from a file:
```js
export const Libp2pOptions = {
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0/ws']
},
transports: [
webSockets({
filter: all
}),
webRTC(),
circuitRelayTransport({
discoverRelays: 1
})
],
connectionEncryption: [noise()],
streamMuxers: [yamux()],
connectionGater: {
denyDialMultiaddr: () => false
},
services: {
identify: identify(),
pubsub: gossipsub({ allowPublishToZeroPeers: true })
}
}
```
Throughout this documentation, you will see the above Libp2p configuration imported from a file called **./config/libp2p.js**, for example:
```js
import { Libp2pOptions } from './config/libp2p.js'
```
## Creating a standalone database
To create a database on a single machine, launch an instance of OrbitDB. Once launched, you can open a new database.
@ -34,9 +123,10 @@ Create a file in your project called index.js and add the following code to it:
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB } from '@orbitdb/core'
import { Libp2pOptions } from './config/libp2p.js'
// Create an IPFS instance with defaults.
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
// Create an IPFS instance.
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })
const orbitdb = await createOrbitDB({ ipfs })
@ -121,10 +211,11 @@ Create a new file called index.js and paste in the following code:
```js
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { OrbitDB, IPFSAccessController, DefaultLibp2pBrowserOptions } from '@orbitdb/core'
import { OrbitDB, IPFSAccessController } from '@orbitdb/core'
import { Libp2pOptions } from './config/libp2p.js'
const main = async () => {
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })
// create a random directory to avoid OrbitDB conflicts.

View File

@ -91,13 +91,14 @@ The identity object is stored like any other [IPLD data structure](https://ipld.
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia' from 'ipfs-core'
import * as Block from 'multiformats/block'
import { Identities, DefaultLibp2pOptions } from '@orbitdb/core'
import { Identities } from '@orbitdb/core'
import * as dagCbor from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'
import { base58btc } from 'multiformats/bases/base58'
import { CID } from 'multiformats/cid'
import { Libp2pOptions } from './config/libp2p.js'
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })
const identities = await Identities({ ipfs })

View File

@ -6,11 +6,12 @@ Below is a simple replication example. Both peers run within the same Nodejs pro
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB } from '@orbitdb/core'
import { Libp2pOptions } from './config/libp2p.js'
// Our ipfs instances will be connecting over websockets. However, you could achieve the same here using tcp. You can find out more about peer connectivity at https://connectivity.libp2p.io/.
const initIPFSInstance = () => {
const libp2p = await createLibp2p({ ...DefaultLibp2pOptions })
const libp2p = await createLibp2p(Libp2pOptions)
return createHelia({ libp2p })
}
@ -53,11 +54,9 @@ db2.events.on('join', async (peerId, heads) => {
console.log(peerId, (await ipfs1.id()).id)
})
// Listen for any updates to db2. This is especially useful when listening for
// new heads that are available on db1.
// Listen for any updates to db2.
// If we want to listen for new data on db2, add an "update" listener to db1.
db2.events.on('update', async (entry) => {
console.log(await db2.all())
db2Updated = true
})