Merge branch 'master' of github.com:orbitdb/orbit-db into feat/new-acs

This commit is contained in:
shamb0t
2019-02-20 11:37:23 +00:00
28 changed files with 4926 additions and 3135 deletions

View File

@@ -5,7 +5,7 @@ orbitdb/
node_modules/
# Don't distribute examples with the module
# See examples at https://github.com/orbitdb/orbit-db
# See examples at https://github.com/orbitdb/orbit-db
examples/
# Don't distribute source maps
@@ -15,5 +15,5 @@ dist/*.map
dist/orbitdb.js
# Don't distribute screenshot
# See examples at https://github.com/orbitdb/orbit-db
screenshots/
# See examples at https://github.com/orbitdb/orbit-db
images/

23
API.md
View File

@@ -10,6 +10,7 @@ Read the **[GETTING STARTED](https://github.com/orbitdb/orbit-db/blob/master/GUI
* [constructor(ipfs, [directory], [options])](#constructoripfs-directory-options)
- [Public Instance Methods](#public-instance-methods)
* [orbitdb.create(name, type, [options])](#orbitdbcreatename-type-options)
* [orbitdb.determineAddress(name, type, [options])](#orbitdbdetermineaddressname-type-options)
* [orbitdb.open(address, [options])](#orbitdbopenaddress-options)
* [orbitdb.disconnect()](#orbitdbdisconnect)
* [orbitdb.stop()](#orbitdbstop)
@@ -75,6 +76,8 @@ Creates and returns an instance of OrbitDB. Use the optional `directory` argumen
- `keystore` (Keystore Instance) : By default creates an instance of [Keystore](https://github.com/orbitdb/orbit-db-keystore). A custom keystore instance can be used, see [this](https://github.com/orbitdb/orbit-db/blob/master/test/utils/custom-test-keystore.js) for an example.
- 'cache' (Cache Instance) : By default creates an instance of [Cache](https://github.com/orbitdb/orbit-db-cache). A custom cache instance can also be used.
After creating an `OrbitDB` instance , you can access the different data stores. Creating a database instance, eg. with `orbitdb.keyvalue(...)`, returns a *Promise* that resolves to a [database instance](#store-api). See the [Store](#store-api) section for details of common methods and properties.
*For further details, see usage for [kvstore](https://github.com/orbitdb/orbit-db-kvstore#usage), [eventlog](https://github.com/orbitdb/orbit-db-eventstore#usage), [feed](https://github.com/orbitdb/orbit-db-feedstore#usage), [docstore](https://github.com/orbitdb/orbit-db-docstore#usage) and [counter](https://github.com/orbitdb/orbit-db-counterstore#usage).*
@@ -90,7 +93,7 @@ const db = await orbitdb.keyvalue('profile')
Returns a `Promise` that resolves to [a database instance](#store-api). `name` (string) should be the database name, not an OrbitDB address (i.e. `user.posts`). `type` is a supported database type (i.e. `eventlog` or [an added custom type](https://github.com/orbitdb/orbit-db#custom-store-types)). `options` is an object with any of the following properties:
- `directory` (string): The directory where data will be stored (Default: uses directory option passed to OrbitDB constructor or `./orbitdb` if none was provided).
- `write` (array): An array of hex encoded public keys which are used to set write acces to the database. `["*"]` can be passed in to give write access to everyone. See the [GETTING STARTED](https://github.com/orbitdb/orbit-db/blob/master/GUIDE.md) guide for more info. (Default: uses the OrbitDB instance key `orbitdb.key`, which would give write access only to yourself)
- `write` (array): An array of hex encoded public keys which are used to set write access to the database. `["*"]` can be passed in to give write access to everyone. See the [GETTING STARTED](https://github.com/orbitdb/orbit-db/blob/master/GUIDE.md) guide for more info. (Default: uses the OrbitDB instance key `orbitdb.key`, which would give write access only to yourself)
- `overwrite` (boolean): Overwrite an existing database (Default: `false`)
- `replicate` (boolean): Replicate the database with peers, requires IPFS PubSub. (Default: `true`)
```javascript
@@ -104,6 +107,21 @@ const db = await orbitdb.create('user.posts', 'eventlog', {
})
// db created & opened
```
### orbitdb.determineAddress(name, type, [options])
> Returns the orbit-db address for given parameters
Returns a `Promise` that resolves to an orbit-db address. The parameters correspond exactly with the parameters of [orbit-db.create](#orbitdbcreatename-type-options). This is useful for determining a database address ahead of time, or deriving another peer's address from their public key and the database name and type. *No database is actually created.*
```javascript
const dbAddress = await orbitdb.determineAddress('user.posts', 'eventlog', {
write: [
// This could be someone else's public key
'042c07044e7ea51a489c02854db5e09f0191690dc59db0afd95328c9db614a2976e088cab7c86d7e48183191258fc59dc699653508ce25bf0369d67f33d5d77839'
]
})
```
### orbitdb.open(address, [options])
> Opens an OrbitDB database.
@@ -332,7 +350,6 @@ Returns a `Promise` that resolves to the multihash of the entry as a `String`.
Returns an `Array` with a single `Object` if key exists.
```javascript
const profile = db.get('shamb0t')
.map((e) => e.payload.value)
// [{ _id: 'shamb0t', name: 'shamb0t', followers: 500 }]
```
@@ -356,7 +373,7 @@ Alias for [`orbitdb.docs()`](#orbitdbdocsnameaddress-options)
### orbitdb.counter(name|address)
> Creates and opens a counter database
Returns a `Promise` that resolves to a [`DocumentStore` instance](https://github.com/orbitdb/orbit-db-docstore).
Returns a `Promise` that resolves to a [`CounterStore` instance](https://github.com/orbitdb/orbit-db-counterstore).
```javascript
const counter = await orbitdb.counter('song_123.play_count')

71
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,71 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [community@orbitdb.org](mailto:community@orbitdb.org), which goes to all members of the @OrbitDB community team, or to [richardlitt@orbitdb.org](mailto:richardlitt@orbitdb.org), which goes only to [@RichardLitt](https://github.com/RichardLitt) or to [haadcode@orbitdb.org](mailto:haadcode@orbitdb.org), which goes only to [@haadcode](https://github.com/haadcode).
All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org

4
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,4 @@
# Contributing
Check out our organization-wide [Contributing Guide](https://github.com/orbitdb/welcome/blob/master/contributing.md) before contributing to this repository. Thank you.

224
CONTRIBUTORS.md Normal file
View File

@@ -0,0 +1,224 @@
# Contributors
Many, many people have contributed to OrbitDB, both as individuals and as part of their organizations. Here is a complete list of people who have committed code, opened PRs, reviewed code, or opened or commented in issues - only in this repository. All of these interactions are part of contributing to this community. Thank you, everyone.
This list is updated every infrequently, to limit noise. It was [generated](https://github.com/orbitdb/orbit-db/issues/287) programmatically using [name-your-contributors](https://github.com/mntnr/name-your-contributors).
Note: This list is opt-out. If you'd rather not be here, send an email to [richardlitt@orbitdb.org](mailto:richardlitt@orbitdb.org) and you'll be silently removed and excluded from future lists.
## Thanks to these people
- [@0xflotus](https://github.com/0xflotus)
- [@509dave16](https://github.com/509dave16)
- [@5310](https://github.com/5310)
- [@7flash](https://github.com/7flash)
- [@abh1manyu](https://github.com/abh1manyu)
- [@adamski](https://github.com/adamski)
- [@adrprado](https://github.com/adrprado)
- [@aeschylus](https://github.com/aeschylus)
- [@ajayrao80](https://github.com/ajayrao80)
- [@alanshaw](https://github.com/alanshaw)
- [@AlessandroChecco](https://github.com/AlessandroChecco)
- [@Alex-Werner](https://github.com/Alex-Werner)
- [@alexanderattar](https://github.com/alexanderattar)
- [@alvinzach](https://github.com/alvinzach)
- [@ambertch](https://github.com/ambertch)
- [@amoreo](https://github.com/amoreo)
- [@aphelionz](https://github.com/aphelionz)
- [@ashays](https://github.com/ashays)
- [@ashwin-yardi](https://github.com/ashwin-yardi)
- [@atfornes](https://github.com/atfornes)
- [@baimurzin](https://github.com/baimurzin)
- [@Bajix](https://github.com/Bajix)
- [@balupton](https://github.com/balupton)
- [@barlock](https://github.com/barlock)
- [@BartKnucle](https://github.com/BartKnucle)
- [@bedeho](https://github.com/bedeho)
- [@beingmohit](https://github.com/beingmohit)
- [@benrogmans](https://github.com/benrogmans)
- [@bmuller](https://github.com/bmuller)
- [@brainstorm](https://github.com/brainstorm)
- [@bronger](https://github.com/bronger)
- [@CaptainQuark](https://github.com/CaptainQuark)
- [@carlbror](https://github.com/carlbror)
- [@CarlChryniszzswics](https://github.com/CarlChryniszzswics)
- [@cblgh](https://github.com/cblgh)
- [@cbruguera](https://github.com/cbruguera)
- [@ccutch](https://github.com/ccutch)
- [@christroutner](https://github.com/christroutner)
- [@claus](https://github.com/claus)
- [@compsocial](https://github.com/compsocial)
- [@coodoo](https://github.com/coodoo)
- [@coyotespike](https://github.com/coyotespike)
- [@crazybuster](https://github.com/crazybuster)
- [@cristiano-belloni](https://github.com/cristiano-belloni)
- [@cryptoquick](https://github.com/cryptoquick)
- [@cyclopse87](https://github.com/cyclopse87)
- [@DaniellMesquita](https://github.com/DaniellMesquita)
- [@DanielVF](https://github.com/DanielVF)
- [@daviddias](https://github.com/daviddias)
- [@davidrichard23](https://github.com/davidrichard23)
- [@dexterchan](https://github.com/dexterchan)
- [@dibu28](https://github.com/dibu28)
- [@dignifiedquire](https://github.com/dignifiedquire)
- [@dirkmc](https://github.com/dirkmc)
- [@drwasho](https://github.com/drwasho)
- [@durac](https://github.com/durac)
- [@edsilv](https://github.com/edsilv)
- [@elsehow](https://github.com/elsehow)
- [@eusthace](https://github.com/eusthace)
- [@evalsocket](https://github.com/evalsocket)
- [@expressflow](https://github.com/expressflow)
- [@fazo96](https://github.com/fazo96)
- [@fdietze](https://github.com/fdietze)
- [@felixSchl](https://github.com/felixSchl)
- [@fiatjaf](https://github.com/fiatjaf)
- [@filips123](https://github.com/filips123)
- [@fluidnotions](https://github.com/fluidnotions)
- [@FrauBienenstich](https://github.com/FrauBienenstich)
- [@futpib](https://github.com/futpib)
- [@gagarin55](https://github.com/gagarin55)
- [@garbados](https://github.com/garbados)
- [@glensimister](https://github.com/glensimister)
- [@greenkeeperio-bot](https://github.com/greenkeeperio-bot)
- [@GriffGreen](https://github.com/GriffGreen)
- [@grvhi](https://github.com/grvhi)
- [@guoliu](https://github.com/guoliu)
- [@gyuri-lajos](https://github.com/gyuri-lajos)
- [@haadcode](https://github.com/haadcode)
- [@haoliangyu](https://github.com/haoliangyu)
- [@Hazae41](https://github.com/Hazae41)
- [@HenryNguyen5](https://github.com/HenryNguyen5)
- [@hugcoday](https://github.com/hugcoday)
- [@hushino](https://github.com/hushino)
- [@huyhoangCSUH](https://github.com/huyhoangCSUH)
- [@IamCarbonMan](https://github.com/IamCarbonMan)
- [@imrehg](https://github.com/imrehg)
- [@ionicc](https://github.com/ionicc)
- [@its-VSP](https://github.com/its-VSP)
- [@jamesgibson14](https://github.com/jamesgibson14)
- [@jbenet](https://github.com/jbenet)
- [@jchris](https://github.com/jchris)
- [@jdebeer](https://github.com/jdebeer)
- [@jehunter5811](https://github.com/jehunter5811)
- [@jenswachtel](https://github.com/jenswachtel)
- [@jobando89](https://github.com/jobando89)
- [@jonasbostoen](https://github.com/jonasbostoen)
- [@joshfraser](https://github.com/joshfraser)
- [@joshmh](https://github.com/joshmh)
- [@jphastings](https://github.com/jphastings)
- [@JulianaDixon](https://github.com/JulianaDixon)
- [@KamuelaFranco](https://github.com/KamuelaFranco)
- [@KennethHolmSeelig](https://github.com/KennethHolmSeelig)
- [@KevinLiLu](https://github.com/KevinLiLu)
- [@kidinamoto01](https://github.com/kidinamoto01)
- [@klueq](https://github.com/klueq)
- [@Kotevode](https://github.com/Kotevode)
- [@kouohhashi](https://github.com/kouohhashi)
- [@Kubuxu](https://github.com/Kubuxu)
- [@LastExile16](https://github.com/LastExile16)
- [@lgierth](https://github.com/lgierth)
- [@lgleim](https://github.com/lgleim)
- [@lufte](https://github.com/lufte)
- [@LuigiCerone](https://github.com/LuigiCerone)
- [@lukas2005](https://github.com/lukas2005)
- [@maht0rz](https://github.com/maht0rz)
- [@marciok](https://github.com/marciok)
- [@marcusfelix](https://github.com/marcusfelix)
- [@maroodb](https://github.com/maroodb)
- [@MartianH](https://github.com/MartianH)
- [@MartinArens](https://github.com/MartinArens)
- [@martinhbramwell](https://github.com/martinhbramwell)
- [@matteodem](https://github.com/matteodem)
- [@mattisstenejohansen](https://github.com/mattisstenejohansen)
- [@maxkerp](https://github.com/maxkerp)
- [@maxvisser](https://github.com/maxvisser)
- [@mccoysc](https://github.com/mccoysc)
- [@MichaelMure](https://github.com/MichaelMure)
- [@MikeFair](https://github.com/MikeFair)
- [@millette](https://github.com/millette)
- [@MirceaKitsune](https://github.com/MirceaKitsune)
- [@mistakia](https://github.com/mistakia)
- [@mitar](https://github.com/mitar)
- [@mmick66](https://github.com/mmick66)
- [@mmsqe](https://github.com/mmsqe)
- [@natachadelarosa](https://github.com/natachadelarosa)
- [@nbanmp](https://github.com/nbanmp)
- [@nezzard](https://github.com/nezzard)
- [@nightwolfz](https://github.com/nightwolfz)
- [@niksmac](https://github.com/niksmac)
- [@nillia](https://github.com/nillia)
- [@Nipol](https://github.com/Nipol)
- [@nmarley](https://github.com/nmarley)
- [@nothingismagick](https://github.com/nothingismagick)
- [@oed](https://github.com/oed)
- [@olegls2000](https://github.com/olegls2000)
- [@onionjake](https://github.com/onionjake)
- [@OR13](https://github.com/OR13)
- [@osarrouy](https://github.com/osarrouy)
- [@oskarpyke](https://github.com/oskarpyke)
- [@paulogr](https://github.com/paulogr)
- [@picrypto](https://github.com/picrypto)
- [@piyushmadan](https://github.com/piyushmadan)
- [@prabodhmeshram](https://github.com/prabodhmeshram)
- [@pranaygp](https://github.com/pranaygp)
- [@ra312](https://github.com/ra312)
- [@raptortech-js](https://github.com/raptortech-js)
- [@rbaid1221](https://github.com/rbaid1221)
- [@ricardojmendez](https://github.com/ricardojmendez)
- [@RichardLitt](https://github.com/RichardLitt)
- [@richardschneider](https://github.com/richardschneider)
- [@rikur](https://github.com/rikur)
- [@riteable](https://github.com/riteable)
- [@rkyleg](https://github.com/rkyleg)
- [@RobertChristopher](https://github.com/RobertChristopher)
- [@roderik](https://github.com/roderik)
- [@rogerlzp](https://github.com/rogerlzp)
- [@rusfearuth](https://github.com/rusfearuth)
- [@ryancbarry](https://github.com/ryancbarry)
- [@shamb0t](https://github.com/shamb0t)
- [@shd101wyy](https://github.com/shd101wyy)
- [@shi-yan](https://github.com/shi-yan)
- [@shotlom](https://github.com/shotlom)
- [@singpolyma](https://github.com/singpolyma)
- [@sirfumblestone](https://github.com/sirfumblestone)
- [@skyne98](https://github.com/skyne98)
- [@spaceywolfi](https://github.com/spaceywolfi)
- [@Steake](https://github.com/Steake)
- [@Stradivario](https://github.com/Stradivario)
- [@strotter](https://github.com/strotter)
- [@subhasisbanik](https://github.com/subhasisbanik)
- [@sundbry](https://github.com/sundbry)
- [@telackey](https://github.com/telackey)
- [@theD1360](https://github.com/theD1360)
- [@theoturner](https://github.com/theoturner)
- [@therebelrobot](https://github.com/therebelrobot)
- [@thiagodelgado111](https://github.com/thiagodelgado111)
- [@thisconnect](https://github.com/thisconnect)
- [@ticruz38](https://github.com/ticruz38)
- [@tyleryasaka](https://github.com/tyleryasaka)
- [@tyvdh](https://github.com/tyvdh)
- [@UnsignedInt8](https://github.com/UnsignedInt8)
- [@uriva](https://github.com/uriva)
- [@varcario](https://github.com/varcario)
- [@vasa-develop](https://github.com/vasa-develop)
- [@victorb](https://github.com/victorb)
- [@vijayee](https://github.com/vijayee)
- [@vishalveerareddy](https://github.com/vishalveerareddy)
- [@vongohren](https://github.com/vongohren)
- [@vrogojin](https://github.com/vrogojin)
- [@vvp](https://github.com/vvp)
- [@whyrusleeping](https://github.com/whyrusleeping)
- [@wigy-opensource-developer](https://github.com/wigy-opensource-developer)
- [@willemneal](https://github.com/willemneal)
- [@wthompson40](https://github.com/wthompson40)
- [@XCJT](https://github.com/XCJT)
- [@Xinjie-canya](https://github.com/Xinjie-canya)
- [@yashwanth2804](https://github.com/yashwanth2804)
- [@ydennisy](https://github.com/ydennisy)
- [@yuppies09](https://github.com/yuppies09)
- [@zabirauf](https://github.com/zabirauf)
- [@zachferland](https://github.com/zachferland)
- [@zaptrem](https://github.com/zaptrem)
- [@zbyte64](https://github.com/zbyte64)
- [@ZeeCoder](https://github.com/ZeeCoder)

54
FAQ.md Normal file
View File

@@ -0,0 +1,54 @@
# Frequently Asked Questions
OrbitDB, like all code, is in a state of constant development. Doubtless, you're going to have some questions. The purpose of this FAQ is to answer the most common questions regarding how to get OrbitDB up and running, how to address common issues, and how to deal with pitfalls and common errors implementing it.
This is a living document. If you see an answer that could be improved, please [open an issue](https://github.com/orbitdb/orbit-db/issues/new) or submit a PR directly. If you think than a question is missing, please [open an issue](https://github.com/orbitdb/orbit-db/issues/new). If you think that there is a better way to resolve a question - perhaps by improving the `orbitdb --help` docs or by adding a feature - please [open an issue](https://github.com/orbitdb/orbit-db/issues/new). Sense a theme yet? :)
This document is seeded by questions from people opening issues in this repository. If enough people ask the same question, we'll add one here and point newcomers to it. Please don't be offended if the maintainers say "read the FAQ" - it's our way of making sure we don't spend all of our time answering the same questions.
**Questions**
<!-- toc -->
- [Database replication is not working. Why?](#database-replication-is-not-working-why)
- [Can I recreate the entire database on another machine based on the address?](#can-i-recreate-the-entire-database-on-another-machine-based-on-the-address)
- [Is every `put` to OrbitDB immediately sent to the network and persisted?](#is-every-put-to-orbitdb-immediately-sent-to-the-network-and-persisted)
- [Does OrbitDB already support pinning when using js-ipfs ?](#does-orbitdb-already-support-pinning-when-using-js-ipfs-)
- [Does orbit have a shared feed between peers where multiple peers can append to the same feed?](#does-orbit-have-a-shared-feed-between-peers-where-multiple-peers-can-append-to-the-same-feed)
- [Contribute](#contribute)
<!-- tocstop -->
---
### Database replication is not working. Why?
_The answer to this question is a work in progress. See [orbit-db#505](https://github.com/orbitdb/orbit-db/issues/505)._
### Can I recreate the entire database on another machine based on the address?
A database can't be "recreated" without downloading the database from other peers. Knowing an address will allow a user to open the database, which automatically connects to other peers who have the database open, and downloads the database which then "recreates" the database state locally, ie. replicates the database.
### Is every `put` to OrbitDB immediately sent to the network and persisted?
When calling `put` or any other update operation on a database, the data is 1) saved locally and persisted to IPFS and 2) send to the network, through IPFS Pubsub, to peers who have the database open (ie. peers).
Upon calling `put` (or other updates), OrbitDB saves the data locally and returns. That is, the operation and its data is saved to the local node only after which `put` returns and *asynchronously* sends a message to pubsub peers. OrbitDB doesn't have a notion of confirming replication status from other peers (although this can be added on user-level) and considers operation a success upon persisting it locally. OrbitDB doesn't use consensus nor does it wait for the network to confirm operations making it an *eventually consistent* system.
In short: it can't be assumed that data has been replicated to the network after an update-operation call finishes (eg. `put`, `add`).
### Does OrbitDB already support pinning when using js-ipfs ?
Currently [js-ipfs](https://github.com/ipfs/js-ipfs) doesn't have GC, so nothing gets removed meaning everything is pinned by default.
However, this will change in the future as js-ipfs gets GC and we want to make sure that OrbitDB is actually persisting everything (by default), so some work on pinning needs to happen. If you're using OrbitDB with go-ipfs (through js-ipfs-api), then GC happens and data may not be persisted anymore after a time. This is a known issue and we're planning to implement actual pinning (from IPFS perspective) soon.
### Does orbit have a shared feed between peers where multiple peers can append to the same feed?
> "...or, is it done more like scuttlebutt, where each peer has their own feed"
All databases (feeds) are shared between peers, so nobody "owns them" like users do in ssb (afaik). Multiple peers can append to the same db. @tyleryasaka is right in that each peer has their own copy of the db (the log) and they may have different versions between them but, as @tyleryasaka (correctly) describes, through the syncing/replication process the peers exchange "their knowledge of the db" (heads) with each other, the dbs/logs get merged. This is what the "CRDT" in ipfs-log enables. But from address/authority/ownership perspective, they all share the same feed.
### How can I contribute to this FAQ?
See the introduction at the top! Please open any issues and pull requests you can to improve this FAQ.md. It is here for you. If you're confused, ask another question publicly; it's possible that other people are, too. If you don't want to open an issue, feel free to jump onto [the Gitter](https://gitter.im/orbitdb/Lobby) and ask us there, too.

View File

@@ -55,7 +55,7 @@ const OrbitDB = require('orbit-db')
// OrbitDB uses Pubsub which is an experimental feature
// and need to be turned on manually.
// Note that these options need to be passed to IPFS in
// all examples in this document even if not specfied so.
// all examples in this document even if not specified so.
const ipfsOptions = {
EXPERIMENTAL: {
pubsub: true
@@ -202,8 +202,8 @@ ipfs.on('ready', async () => {
],
}
const db = await orbitdb.keyvalue('first-database', access)
console.log(db.address.toString())
const db1 = await orbitdb.keyvalue('first-database', access)
console.log(db1.address.toString())
// /orbitdb/Qmdgwt7w4uBsw8LXduzCd18zfGXeTmBsiR8edQ1hSfzcJC/first-database
// Second peer opens the database from the address
@@ -358,7 +358,7 @@ ipfs1.on('ready', async () => {
## Custom Stores
Use a custom store to implement case specifc functionality that is not supported by the default OrbitDB database stores. Then, you can easily add and use your custom store with OrbitDB:
Use a custom store to implement case specific functionality that is not supported by the default OrbitDB database stores. Then, you can easily add and use your custom store with OrbitDB:
```javascript
// define custom store type

View File

@@ -20,6 +20,10 @@ build: test
clean:
rm -rf orbitdb/
rm -rf node_modules/
rm package-lock.json
clean-dependencies: clean
if [ -a package-lock.json ]; then rm package-lock.json; fi;
rebuild: | clean-dependencies build
.PHONY: test build

View File

@@ -1,15 +1,12 @@
# OrbitDB
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/orbitdb/Lobby)
[![](https://img.shields.io/badge/freenode-%23orbitdb-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23orbitdb)
[![CircleCI Status](https://circleci.com/gh/orbitdb/orbit-db.svg?style=shield)](https://circleci.com/gh/orbitdb/orbit-db)
[![npm version](https://badge.fury.io/js/orbit-db.svg)](https://www.npmjs.com/package/orbit-db)
[![node](https://img.shields.io/node/v/orbit-db.svg)](https://www.npmjs.com/package/orbit-db)
[![Project Status](https://badge.waffle.io/orbitdb/orbit-db.svg?columns=In%20Progress&title=In%20Progress)](https://waffle.io/orbitdb/orbit-db)
<p align="left">
<img src="images/orbit_db_logo_color.jpg" width="256" />
</p>
> A peer-to-peer database for the decentralized web
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/orbitdb/Lobby) [![CircleCI Status](https://circleci.com/gh/orbitdb/orbit-db.svg?style=shield)](https://circleci.com/gh/orbitdb/orbit-db) [![npm version](https://badge.fury.io/js/orbit-db.svg)](https://www.npmjs.com/package/orbit-db) [![node](https://img.shields.io/node/v/orbit-db.svg)](https://www.npmjs.com/package/orbit-db)
OrbitDB is a serverless, distributed, peer-to-peer database. OrbitDB uses [IPFS](https://ipfs.io) as its data storage and [IPFS Pubsub](https://github.com/ipfs/go-ipfs/blob/master/core/commands/pubsub.go#L23) to automatically sync databases with peers. It's an eventually consistent database that uses [CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) for conflict-free database merges making OrbitDB an excellent choice for decentralized apps (dApps), blockchain applications and offline-first web applications.
OrbitDB is a **serverless, distributed, peer-to-peer database**. OrbitDB uses [IPFS](https://ipfs.io) as its data storage and [IPFS Pubsub](https://github.com/ipfs/go-ipfs/blob/master/core/commands/pubsub.go#L23) to automatically sync databases with peers. It's an eventually consistent database that uses [CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) for conflict-free database merges making OrbitDB an excellent choice for decentralized apps (dApps), blockchain applications and offline-first web applications.
**Test it live at [Live demo 1](https://ipfs.io/ipfs/QmeESXh9wPib8Xz7hdRzHuYLDuEUgkYTSuujZ2phQfvznQ/), [Live demo 2](https://ipfs.io/ipfs/QmasHFRj6unJ3nSmtPn97tWDaQWEZw3W9Eh3gUgZktuZDZ/), or [P2P TodoMVC app](https://ipfs.io/ipfs/QmTJGHccriUtq3qf3bvAQUcDUHnBbHNJG2x2FYwYUecN43/)**!
@@ -17,7 +14,7 @@ OrbitDB is a serverless, distributed, peer-to-peer database. OrbitDB uses [IPFS]
OrbitDB provides various types of databases for different data models and use cases:
- **[log](https://github.com/orbitdb/orbit-db/blob/master/API.md#orbitdblognameaddress)**: an immutable (append-only) log with traversable history. Useful for *"latest N"* use cases or as a message queue.
- **[feed](https://github.com/orbitdb/orbit-db/blob/master/API.md#orbitdbfeednameaddress)**: a mutable log with traversable history. Entries can be added and removed. Useful for *"shopping cart" type of use cases, or for example as a feed of blog posts or "tweets".
- **[feed](https://github.com/orbitdb/orbit-db/blob/master/API.md#orbitdbfeednameaddress)**: a mutable log with traversable history. Entries can be added and removed. Useful for *"shopping cart"* type of use cases, or for example as a feed of blog posts or "tweets".
- **[keyvalue](https://github.com/orbitdb/orbit-db/blob/master/API.md#orbitdbkeyvaluenameaddress)**: a key-value database just like your favourite key-value database.
- **[docs](https://github.com/orbitdb/orbit-db/blob/master/API.md#orbitdbdocsnameaddress-options)**: a document database to which JSON documents can be stored and indexed by a specified key. Useful for building search indices or version controlling documents and data.
- **[counter](https://github.com/orbitdb/orbit-db/blob/master/API.md#orbitdbcounternameaddress)**: Useful for counting events separate from log/feed data.
@@ -25,7 +22,17 @@ OrbitDB provides various types of databases for different data models and use ca
All databases are [implemented](https://github.com/orbitdb/orbit-db-store) on top of [ipfs-log](https://github.com/orbitdb/ipfs-log), an immutable, operation-based conflict-free replicated data structure (CRDT) for distributed systems. If none of the OrbitDB database types match your needs and/or you need case-specific functionality, you can easily [implement and use a custom database store](https://github.com/orbitdb/orbit-db/blob/master/GUIDE.md#custom-stores) of your own.
#### Project status & support
This is the Javascript implementation and it works both in **Browsers** and **Node.js** with support for Linux and OS X (Windows is not supported yet). The minimum required version of Node.js is now 8.0.0. To use with older versions of Node.js, we provide an ES5-compatible build through the npm package, located in `dist/es5/` when installed through npm.
Status: **in active development**
***NOTE!*** *OrbitDB is **alpha-stage** software. It means OrbitDB hasn't been security audited and programming APIs and data formats can still change. We encourage you to [reach out to the maintainers](https://gitter.im/orbitdb/Lobby) if you plan to use OrbitDB in mission critical systems.*
This is the Javascript implementation and it works both in **Browsers** and **Node.js** with support for Linux and OS X (Windows is not supported yet). The minimum required version of Node.js is now 8.6.0 due to the usage of `...` spread syntax. LTS versions (even numbered versions 8, 10, etc) are preferred.
To use with older versions of Node.js, we provide an ES5-compatible build through the npm package, located in `dist/es5/` when installed through npm.
#### Community Calls
We also have regular community calls, which we announce in the issues in [the @orbitdb welcome repository](https://github.com/orbitdb/welcome/issues). Join us!
## Table of Contents
@@ -61,7 +68,7 @@ Read the **[GETTING STARTED](https://github.com/orbitdb/orbit-db/blob/master/GUI
For the CLI tool to manage orbit-db database, see **[OrbitDB CLI](https://github.com/orbitdb/orbit-db-cli)**.
It can be installed from Npm with:
It can be installed from npm with:
```
npm install orbit-db-cli -g
@@ -74,7 +81,7 @@ If you're using `orbitd-db` to develop **browser** or **Node.js** applications,
Install dependencies:
```
npm install orbit-db ipfs
npm install orbit-db ipfs@0.33.0
```
```javascript
@@ -132,8 +139,9 @@ const OrbitDB = require('orbit-db')
const ipfs = IpfsApi('localhost', '5001')
const orbitdb = new OrbitDB(ipfs)
const db = await orbitdb.log('hello')
...
orbitdb.log('hello').then(db => {
// Do something with your db.
})
```
## API
@@ -178,7 +186,7 @@ npm run examples:browser-linux
```
<p align="left">
<img src="https://raw.githubusercontent.com/orbitdb/orbit-db/master/screenshots/example1.png" width="33%">
<img src="https://raw.githubusercontent.com/orbitdb/orbit-db/master/images/example1.png" width="33%">
</p>
Check the code in [examples/browser/browser.html](https://github.com/orbitdb/orbit-db/blob/master/examples/browser/browser.html) and try the [live example](https://ipfs.io/ipfs/QmRosp97r8GGUEdj5Wvivrn5nBkuyajhRXFUcWCp5Zubbo/).
@@ -189,7 +197,7 @@ Check the code in [examples/browser/browser.html](https://github.com/orbitdb/orb
npm run examples:node
```
<img src="https://raw.githubusercontent.com/orbitdb/orbit-db/master/screenshots/orbit-db-demo3.gif" width="66%">
<img src="https://raw.githubusercontent.com/orbitdb/orbit-db/master/images/orbit-db-demo3.gif" width="66%">
**Eventlog**
@@ -222,6 +230,8 @@ OrbitDB uses the following modules:
To understand a little bit about the architecture, check out a visualization of the data flow at https://github.com/haadcode/proto2 or a live demo: http://celebdil.benet.ai:8080/ipfs/Qmezm7g8mBpWyuPk6D84CNcfLKJwU6mpXuEN5GJZNkX3XK/.
Community-maintained Typescript typings are available here: https://github.com/orbitdb/orbit-db-types
## Development
### Run Tests
@@ -251,11 +261,15 @@ LOG=debug node <file>
## Contributing
We would be happy to accept PRs! If you want to work on something, it'd be good to talk beforehand to make sure nobody else is working on it. You can reach us on IRC [#orbitdb](http://webchat.freenode.net/?channels=%23orbitdb) on Freenode, or in the comments of the [issues section](https://github.com/orbitdb/orbit-db/issues).
**Take a look at our organization-wide [Contributing Guide](https://github.com/orbitdb/welcome/blob/master/contributing.md).** You'll find most of your questions answered there. Some questions may be answered in the [FAQ](FAQ.md), as well.
As far as code goes, we would be happy to accept PRs! If you want to work on something, it'd be good to talk beforehand to make sure nobody else is working on it. You can reach us [on Gitter](https://gitter.im/orbitdb/Lobby), or in the [issues section](https://github.com/orbitdb/orbit-db/issues).
We also have **regular community calls**, which we announce in the issues in [the @orbitdb welcome repository](https://github.com/orbitdb/welcome/issues). Join us!
If you want to code but don't know where to start, check out the issues labelled ["help wanted"](https://github.com/orbitdb/orbit-db/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+sort%3Areactions-%2B1-desc) or the project's [status board](https://waffle.io/orbitdb/orbit-db).
If you want to code but don't know where to start, check out the issues labelled ["help wanted"](https://github.com/orbitdb/orbit-db/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+sort%3Areactions-%2B1-desc).
Please note that we have a [Code of Conduct](CODE_OF_CONDUCT.md), and that all activity in the [@orbitdb](https://github.com/orbitdb) organization falls under it. Read it when you get the chance, as being part of this community means that you agree to abide by it. Thanks.
## Sponsors

View File

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 185 KiB

View File

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 136 KiB

View File

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

6746
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,8 @@
"main": "src/OrbitDB.js",
"dependencies": {
"ipfs-pubsub-1on1": "~0.0.4",
"ipld-dag-pb": "0.14.11",
"localstorage-down": "^0.6.7",
"logplease": "^1.2.14",
"multihashes": "^0.4.12",
"orbit-db-cache": "~0.2.4",
@@ -58,7 +60,7 @@
"build:examples": "webpack --config conf/webpack.example.config.js --sort-modules-by size && mkdir -p examples/browser/lib && cp node_modules/ipfs/dist/index.js examples/browser/lib/ipfs.js",
"build:dist": "webpack --config conf/webpack.config.js --sort-modules-by size && mkdir -p examples/browser/lib && cp dist/orbitdb.min.js examples/browser/lib/orbitdb.min.js",
"build:debug": "webpack --config conf/webpack.debug.config.js --sort-modules-by size && mkdir -p examples/browser/lib && cp dist/orbitdb.js examples/browser/lib/orbitdb.js && cp dist/orbitdb.js.map examples/browser/lib/orbitdb.js.map",
"build:docs/toc": "markdown-toc --no-first1 -i README.md && markdown-toc --no-first1 -i API.md && markdown-toc --no-first1 -i GUIDE.md && markdown-toc --no-first1 -i CHANGELOG.md",
"build:docs/toc": "markdown-toc --no-first1 -i README.md && markdown-toc --no-first1 -i API.md && markdown-toc --no-first1 -i GUIDE.md && markdown-toc --no-first1 -i CHANGELOG.md && markdown-toc --no-first1 -i FAQ.md ",
"build:es5": "babel src --out-dir ./dist/es5/ --presets babel-preset-es2015 --plugins babel-plugin-transform-runtime"
}
}

View File

@@ -9,7 +9,6 @@ const DocumentStore = require('orbit-db-docstore')
const Pubsub = require('orbit-db-pubsub')
const Cache = require('orbit-db-cache')
const Keystore = require('orbit-db-keystore')
const IdentityProvider = require('orbit-db-identity-provider')
const AccessController = require('./ipfs-access-controller')
const OrbitDBAddress = require('./orbit-db-address')
const createDBManifest = require('./db-manifest')
@@ -38,14 +37,11 @@ class OrbitDB {
this.stores = {}
this.directory = directory || './orbitdb'
this.keystore = options.keystore || Keystore.create(path.join(this.directory, this.id, '/keystore'))
this.identity = options.identity
this.cache = options.cache || Cache
this.key = this.keystore.getKey(this.id) || this.keystore.createKey(this.id)
this._directConnections = {}
}
async initialize(options = {}) {
this.identity = await IdentityProvider.createIdentity(this.keystore, this.id, options.identitySignerFn)
}
/* Databases */
async feed (address, options = {}) {
options = Object.assign({ create: true, type: 'feed' }, options)
@@ -117,11 +113,8 @@ class OrbitDB {
/* Private methods */
async _createStore (type, address, options) {
if (!this.identity)
await this.initialize()
// Get the type -> class mapping
const Store = databaseTypes[type]
// this.identity = this.identity || await IdentityProvider.createIdentity(this.keystore, this.id, options.identitySignerFn)
if (!Store)
throw new Error(`Invalid database type '${type}'`)
@@ -136,11 +129,12 @@ class OrbitDB {
const opts = Object.assign({ replicate: true }, options, {
accessController: accessController,
keystore: this.keystore,
cache: cache,
onClose: this._onClose.bind(this),
})
const store = new Store(this._ipfs, this.identity, address, opts)
const store = new Store(this._ipfs, this.id, address, opts)
store.events.on('write', this._onWrite.bind(this))
// ID of the store is the address as a string
@@ -210,29 +204,10 @@ class OrbitDB {
delete this.stores[address]
}
/* Create and Open databases */
/*
options = {
admin: [], // array of keys that are the admins of this database (same as write access)
write: [], // array of keys that can write to this database
directory: './orbitdb', // directory in which to place the database files
overwrite: false, // whether we should overwrite the existing database if it exists
}
*/
async create (name, type, options = {}) {
logger.debug(`create()`)
if (!this.identity)
await this.initialize()
async _determineAddress(name, type, options = {}, onlyHash) {
if (!OrbitDB.isValidType(type))
throw new Error(`Invalid database type '${type}'`)
// The directory to look databases from can be passed in as an option
const directory = options.directory || this.directory
logger.debug(`Creating database '${name}' as ${type} in '${directory}'`)
if (OrbitDBAddress.isValid(name))
throw new Error(`Given database name is an address. Please give only the name of the database!`)
@@ -251,21 +226,43 @@ class OrbitDB {
options.write.forEach(e => accessController.add('write', e))
} else {
// Default is to add ourselves as the admin of the database
accessController.add('write', this.identity.publicKey)
accessController.add('write', this.key.getPublic('hex'))
}
// Save the Access Controller in IPFS
const accessControllerAddress = await accessController.save()
const accessControllerAddress = await accessController.save(onlyHash)
// Save the manifest to IPFS
const manifestHash = await createDBManifest(this._ipfs, name, type, accessControllerAddress)
const manifestHash = await createDBManifest(this._ipfs, name, type, accessControllerAddress, onlyHash)
// Create the database address
const dbAddress = OrbitDBAddress.parse(path.join('/orbitdb', manifestHash, name))
return OrbitDBAddress.parse(path.join('/orbitdb', manifestHash, name))
}
// // Load local cache
const haveDB = await this._loadCache(directory, dbAddress)
.then(cache => cache ? cache.get(path.join(dbAddress.toString(), '_manifest')) : null)
.then(data => data !== undefined && data !== null)
/* Create and Open databases */
/*
options = {
admin: [], // array of keys that are the admins of this database (same as write access)
write: [], // array of keys that can write to this database
directory: './orbitdb', // directory in which to place the database files
overwrite: false, // whether we should overwrite the existing database if it exists
}
*/
async create (name, type, options = {}) {
logger.debug(`create()`)
// The directory to look databases from can be passed in as an option
const directory = options.directory || this.directory
logger.debug(`Creating database '${name}' as ${type} in '${directory}'`)
// Create the database address
const dbAddress = await this._determineAddress(name, type, options)
// Load the locally saved database information
const cache = await this._loadCache(directory, dbAddress)
// Check if we have the database locally
const haveDB = await this._haveLocalData(cache, dbAddress)
if (haveDB && !options.overwrite)
throw new Error(`Database '${dbAddress}' already exists!`)
@@ -279,10 +276,14 @@ class OrbitDB {
return this.open(dbAddress, options)
}
async determineAddress(name, type, options = {}) {
return this._determineAddress(name, type, options, true)
}
/*
options = {
localOnly: false // if set to true, throws an error if database can't be found locally
create: false // wether to create the database
create: false // whether to create the database
type: TODO
overwrite: TODO
@@ -290,10 +291,6 @@ class OrbitDB {
*/
async open (address, options = {}) {
logger.debug(`open()`)
if (!this.identity)
await this.initialize()
options = Object.assign({ localOnly: false, create: false }, options)
logger.debug(`Open database '${address}'`)
@@ -317,11 +314,11 @@ class OrbitDB {
// Parse the database address
const dbAddress = OrbitDBAddress.parse(address)
// Check if we have the database
const haveDB = await this._loadCache(directory, dbAddress)
.then(cache => cache ? cache.get(path.join(dbAddress.toString(), '_manifest')) : null)
.then(data => data !== undefined && data !== null)
// Load the locally saved db information
const cache = await this._loadCache(directory, dbAddress)
// Check if we have the database
const haveDB = await this._haveLocalData(cache, dbAddress)
logger.debug((haveDB ? 'Found' : 'Didn\'t find') + ` database '${dbAddress}'`)
// If we want to try and open the database local-only, throw an error
@@ -357,10 +354,11 @@ class OrbitDB {
logger.debug(`Saved manifest to IPFS as '${dbAddress.root}'`)
}
// Loads the locally saved database information (manifest, head hashes)
async _loadCache (directory, dbAddress) {
let cache
try {
cache = await Cache.load(directory, dbAddress)
cache = await this.cache.load(directory, dbAddress)
} catch (e) {
console.log(e)
logger.error("Couldn't load Cache:", e)
@@ -369,6 +367,20 @@ class OrbitDB {
return cache
}
/**
* Check if we have the database, or part of it, saved locally
* @param {[Cache]} cache [The OrbitDBCache instance containing the local data]
* @param {[OrbitDBAddress]} dbAddress [Address of the database to check]
* @return {[Boolean]} [Returns true if we have cached the db locally, false if not]
*/
async _haveLocalData (cache, dbAddress) {
if (!cache) {
return false
}
const data = await cache.get(path.join(dbAddress.toString(), '_manifest'))
return data !== undefined && data !== null
}
/**
* Returns supported database types as an Array of strings
* Eg. [ 'counter', 'eventlog', 'feed', 'docstore', 'keyvalue']

View File

@@ -1,13 +1,27 @@
const path = require('path')
const { DAGNode } = require('ipld-dag-pb')
// Creates a DB manifest file and saves it in IPFS
const createDBManifest = async (ipfs, name, type, accessControllerAddress) => {
const createDBManifest = async (ipfs, name, type, accessControllerAddress, onlyHash) => {
const manifest = {
name: name,
type: type,
accessController: path.join('/ipfs', accessControllerAddress),
}
const dag = await ipfs.object.put(Buffer.from(JSON.stringify(manifest)))
let dag
const manifestJSON = JSON.stringify(manifest)
if (onlyHash) {
dag = await new Promise(resolve => {
DAGNode.create(Buffer.from(manifestJSON), (err, n) => {
if (err) {
throw err
}
resolve(n)
})
})
} else {
dag = await ipfs.object.put(Buffer.from(manifestJSON))
}
return dag.toJSON().multihash.toString()
}

View File

@@ -1,6 +1,8 @@
'use strict'
const AccessController = require('./access-controller')
const { DAGNode } = require('ipld-dag-pb')
class IPFSAccessController extends AccessController {
constructor (ipfs) {
@@ -23,11 +25,23 @@ class IPFSAccessController extends AccessController {
}
}
async save () {
async save (onlyHash) {
let hash
try {
const access = JSON.stringify(this._access, null, 2)
const dag = await this._ipfs.object.put(new Buffer(access))
let dag
if (onlyHash) {
dag = await new Promise(resolve => {
DAGNode.create(Buffer.from(access), (err, n) => {
if (err) {
throw err
}
resolve(n)
})
})
} else {
dag = await this._ipfs.object.put(new Buffer(access))
}
hash = dag.toJSON().multihash.toString()
} catch (e) {
console.log("ACCESS ERROR:", e)

View File

@@ -186,6 +186,48 @@ Object.keys(testAPIs).forEach(API => {
})
})
describe('determineAddress', function() {
describe('Errors', function() {
it('throws an error if given an invalid database type', async () => {
let err
try {
await orbitdb.determineAddress('first', 'invalid-type')
} catch (e) {
err = e.toString()
}
assert.equal(err, 'Error: Invalid database type \'invalid-type\'')
})
it('throws an error if given an address instead of name', async () => {
let err
try {
await orbitdb.determineAddress('/orbitdb/Qmc9PMho3LwTXSaUXJ8WjeBZyXesAwUofdkGeadFXsqMzW/first', 'feed')
} catch (e) {
err = e.toString()
}
assert.equal(err, 'Error: Given database name is an address. Please give only the name of the database!')
})
})
describe('Success', function() {
before(async () => {
address = await orbitdb.determineAddress('third', 'feed', { replicate: false })
localDataPath = path.join(dbPath, address.root, address.path)
})
it('does not save the address locally', async () => {
assert.equal(fs.existsSync(localDataPath), false)
})
it('returns the address that would have been created', async () => {
db = await orbitdb.create('third', 'feed', { replicate: false })
assert.equal(address.toString().indexOf('/orbitdb'), 0)
assert.equal(address.toString().indexOf('Qm'), 9)
assert.equal(address.toString(), db.address.toString())
})
})
})
describe('Open', function() {
before(async () => {
db = await orbitdb.open('abc', { create: true, type: 'feed' })

58
test/custom-cache.test.js Normal file
View File

@@ -0,0 +1,58 @@
'use strict'
const assert = require('assert')
const rmrf = require('rimraf')
const OrbitDB = require('../src/OrbitDB')
const CustomCache = require('orbit-db-cache')
// Include test utilities
const {
config,
startIpfs,
stopIpfs,
testAPIs,
CustomTestCache,
databases,
} = require('./utils')
const dbPath = './orbitdb/tests/customKeystore'
const ipfsPath = './orbitdb/tests/customKeystore/ipfs'
Object.keys(testAPIs).forEach(API => {
describe(`orbit-db - Use a Custom Cache (${API})`, function() {
this.timeout(20000)
let ipfsd, ipfs, orbitdb1
before(async () => {
config.daemon1.repo = ipfsPath
rmrf.sync(config.daemon1.repo)
rmrf.sync(dbPath)
ipfsd = await startIpfs(API, config.daemon1)
ipfs = ipfsd.api
orbitdb1 = new OrbitDB(ipfs, dbPath + '/1', {
cache: CustomTestCache
})
})
after(async () => {
if(orbitdb1)
await orbitdb1.stop()
if (ipfsd)
await stopIpfs(ipfsd)
})
describe('allows orbit to use a custom cache with different store types', function() {
databases.forEach(async (database) => {
it(database.type + ' allows custom keystore', async () => {
const db1 = await database.create(orbitdb1, 'custom-keystore')
await database.tryInsert(db1)
assert.deepEqual(database.getTestValue(db1), database.expectedValue)
await db1.close()
})
})
})
})
})

View File

@@ -10,54 +10,12 @@ const {
stopIpfs,
testAPIs,
CustomTestKeystore,
databases,
} = require('./utils')
const dbPath = './orbitdb/tests/customKeystore'
const ipfsPath = './orbitdb/tests/customKeystore/ipfs'
const databases = [
{
type: 'eventlog',
create: (orbitdb, name, options) => orbitdb.eventlog(name, options),
tryInsert: (db) => db.add('hello'),
query: (db) => db.iterator({ limit: -1 }).collect(),
getTestValue: (db) => db.iterator({ limit: -1 }).collect()[0].payload.value,
expectedValue: 'hello',
},
{
type: 'feed',
create: (orbitdb, name, options) => orbitdb.feed(name, options),
tryInsert: (db) => db.add('hello'),
query: (db) => db.iterator({ limit: -1 }).collect(),
getTestValue: (db) => db.iterator({ limit: -1 }).collect()[0].payload.value,
expectedValue: 'hello',
},
{
type: 'key-value',
create: (orbitdb, name, options) => orbitdb.kvstore(name, options),
tryInsert: (db) => db.set('one', 'hello'),
query: (db) => [],
getTestValue: (db) => db.get('one'),
expectedValue: 'hello',
},
{
type: 'documents',
create: (orbitdb, name, options) => orbitdb.docstore(name, options),
tryInsert: (db) => db.put({ _id: 'hello world', doc: 'all the things'}),
query: (db) => [],
getTestValue: (db) => db.get('hello world'),
expectedValue: [{ _id: 'hello world', doc: 'all the things'}],
},
{
type: 'counter',
create: (orbitdb, name, options) => orbitdb.counter(name, options),
tryInsert: (db) => db.inc(8),
query: (db) => [],
getTestValue: (db) => db.value,
expectedValue: 8,
},
]
Object.keys(testAPIs).forEach(API => {
describe(`orbit-db - Use a Custom Keystore (${API})`, function() {
this.timeout(20000)

View File

@@ -66,7 +66,7 @@ Object.keys(testAPIs).forEach(API => {
let localDatabases = []
let remoteDatabases = []
// Create two IPFS instances and two OrbitDB instaces (2 nodes/peers)
// Create two IPFS instances and two OrbitDB instances (2 nodes/peers)
before(async () => {
config.daemon1.repo = ipfsPath1
config.daemon2.repo = ipfsPath2

View File

@@ -11,265 +11,279 @@ const {
startIpfs,
stopIpfs,
testAPIs,
CustomTestCache
} = require('./utils')
const dbPath = './orbitdb/tests/persistency'
const ipfsPath = './orbitdb/tests/persistency/ipfs'
const tests = [
{
title: 'Persistency',
orbitDBConfig: {}
},
{
title: 'Persistency with custom cache',
orbitDBConfig: { cache: CustomTestCache }
}
]
Object.keys(testAPIs).forEach(API => {
describe(`orbit-db - Persistency (${API})`, function() {
this.timeout(config.timeout)
tests.forEach(test => {
describe(`orbit-db - ${test.title} (${API})`, function() {
this.timeout(config.timeout)
const entryCount = 65
const entryCount = 65
let ipfsd, ipfs, orbitdb1, db, address
let ipfsd, ipfs, orbitdb1, db, address
before(async () => {
config.daemon1.repo = ipfsPath
rmrf.sync(config.daemon1.repo)
rmrf.sync(dbPath)
ipfsd = await startIpfs(API, config.daemon1)
ipfs = ipfsd.api
orbitdb1 = new OrbitDB(ipfs, dbPath + '/1')
})
after(async () => {
if(orbitdb1)
await orbitdb1.stop()
if (ipfsd)
await stopIpfs(ipfsd)
})
describe('load', function() {
beforeEach(async () => {
const dbName = new Date().getTime().toString()
const entryArr = []
for (let i = 0; i < entryCount; i ++)
entryArr.push(i)
db = await orbitdb1.eventlog(dbName)
address = db.address.toString()
await mapSeries(entryArr, (i) => db.add('hello' + i))
await db.close()
db = null
before(async () => {
config.daemon1.repo = ipfsPath
rmrf.sync(config.daemon1.repo)
rmrf.sync(dbPath)
ipfsd = await startIpfs(API, config.daemon1)
ipfs = ipfsd.api
orbitdb1 = new OrbitDB(ipfs, dbPath + '/1', test.orbitDBConfig)
})
afterEach(async () => {
await db.drop()
after(async () => {
if(orbitdb1)
await orbitdb1.stop()
if (ipfsd)
await stopIpfs(ipfsd)
})
it('loads database from local cache', async () => {
db = await orbitdb1.eventlog(address)
await db.load()
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount)
assert.equal(items[0].payload.value, 'hello0')
assert.equal(items[items.length - 1].payload.value, 'hello' + (entryCount - 1))
})
describe('load', function() {
beforeEach(async () => {
const dbName = new Date().getTime().toString()
const entryArr = []
it('loads database partially', async () => {
const amount = 33
db = await orbitdb1.eventlog(address)
await db.load(amount)
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, amount)
assert.equal(items[0].payload.value, 'hello' + (entryCount - amount))
assert.equal(items[1].payload.value, 'hello' + (entryCount - amount + 1))
assert.equal(items[items.length - 1].payload.value, 'hello' + (entryCount - 1))
})
for (let i = 0; i < entryCount; i ++)
entryArr.push(i)
it('load and close several times', async () => {
const amount = 8
for (let i = 0; i < amount; i ++) {
db = await orbitdb1.eventlog(dbName)
address = db.address.toString()
await mapSeries(entryArr, (i) => db.add('hello' + i))
await db.close()
db = null
})
afterEach(async () => {
await db.drop()
})
it('loads database from local cache', async () => {
db = await orbitdb1.eventlog(address)
await db.load()
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount)
assert.equal(items[0].payload.value, 'hello0')
assert.equal(items[1].payload.value, 'hello1')
assert.equal(items[items.length - 1].payload.value, 'hello' + (entryCount - 1))
await db.close()
}
})
})
it('closes database while loading', async () => {
db = await orbitdb1.eventlog(address)
db.load() // don't wait for load to finish
await db.close()
assert.equal(db._cache.store, null)
})
it('load, add one, close - several times', async () => {
const amount = 8
for (let i = 0; i < amount; i ++) {
it('loads database partially', async () => {
const amount = 33
db = await orbitdb1.eventlog(address)
await db.load()
await db.add('hello' + (entryCount + i))
await db.load(amount)
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount + i + 1)
assert.equal(items[items.length - 1].payload.value, 'hello' + (entryCount + i))
await db.close()
}
})
assert.equal(items.length, amount)
assert.equal(items[0].payload.value, 'hello' + (entryCount - amount))
assert.equal(items[1].payload.value, 'hello' + (entryCount - amount + 1))
assert.equal(items[items.length - 1].payload.value, 'hello' + (entryCount - 1))
})
it('loading a database emits \'ready\' event', async () => {
db = await orbitdb1.eventlog(address)
return new Promise(async (resolve) => {
db.events.on('ready', () => {
it('load and close several times', async () => {
const amount = 8
for (let i = 0; i < amount; i ++) {
db = await orbitdb1.eventlog(address)
await db.load()
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount)
assert.equal(items[0].payload.value, 'hello0')
assert.equal(items[1].payload.value, 'hello1')
assert.equal(items[items.length - 1].payload.value, 'hello' + (entryCount - 1))
resolve()
})
await db.load()
await db.close()
}
})
})
it('loading a database emits \'load.progress\' event', async () => {
db = await orbitdb1.eventlog(address)
return new Promise(async (resolve, reject) => {
let count = 0
db.events.on('load.progress', (address, hash, entry) => {
count ++
try {
assert.equal(address, db.address.toString())
const { progress, max } = db.replicationStatus
assert.equal(max, entryCount)
assert.equal(progress, count)
assert.notEqual(hash, null)
assert.notEqual(entry, null)
if (progress === entryCount && count === entryCount) {
setTimeout(() => {
resolve()
}, 200)
}
} catch (e) {
reject(e)
}
})
// Start loading the database
await db.load()
})
})
})
describe('load from empty snapshot', function() {
it('loads database from an empty snapshot', async () => {
db = await orbitdb1.eventlog('empty-snapshot')
address = db.address.toString()
await db.saveSnapshot()
await db.close()
db = await orbitdb1.open(address)
await db.loadFromSnapshot()
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, 0)
})
})
describe('load from snapshot', function() {
beforeEach(async () => {
const dbName = new Date().getTime().toString()
const entryArr = []
for (let i = 0; i < entryCount; i ++)
entryArr.push(i)
db = await orbitdb1.eventlog(dbName)
address = db.address.toString()
await mapSeries(entryArr, (i) => db.add('hello' + i))
await db.saveSnapshot()
await db.close()
db = null
})
afterEach(async () => {
await db.drop()
})
it('loads database from snapshot', async () => {
db = await orbitdb1.eventlog(address)
await db.loadFromSnapshot()
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount)
assert.equal(items[0].payload.value, 'hello0')
assert.equal(items[entryCount - 1].payload.value, 'hello' + (entryCount - 1))
})
it('load, add one and save snapshot several times', async () => {
const amount = 4
for (let i = 0; i < amount; i ++) {
it('closes database while loading', async () => {
db = await orbitdb1.eventlog(address)
await db.loadFromSnapshot()
await db.add('hello' + (entryCount + i))
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount + i + 1)
assert.equal(items[0].payload.value, 'hello0')
assert.equal(items[items.length - 1].payload.value, 'hello' + (entryCount + i))
db.load() // don't wait for load to finish
await db.close()
assert.equal(db._cache.store, null)
})
it('load, add one, close - several times', async () => {
const amount = 8
for (let i = 0; i < amount; i ++) {
db = await orbitdb1.eventlog(address)
await db.load()
await db.add('hello' + (entryCount + i))
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount + i + 1)
assert.equal(items[items.length - 1].payload.value, 'hello' + (entryCount + i))
await db.close()
}
})
it('loading a database emits \'ready\' event', async () => {
db = await orbitdb1.eventlog(address)
return new Promise(async (resolve) => {
db.events.on('ready', () => {
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount)
assert.equal(items[0].payload.value, 'hello0')
assert.equal(items[items.length - 1].payload.value, 'hello' + (entryCount - 1))
resolve()
})
await db.load()
})
})
it('loading a database emits \'load.progress\' event', async () => {
db = await orbitdb1.eventlog(address)
return new Promise(async (resolve, reject) => {
let count = 0
db.events.on('load.progress', (address, hash, entry) => {
count ++
try {
assert.equal(address, db.address.toString())
const { progress, max } = db.replicationStatus
assert.equal(max, entryCount)
assert.equal(progress, count)
assert.notEqual(hash, null)
assert.notEqual(entry, null)
if (progress === entryCount && count === entryCount) {
setTimeout(() => {
resolve()
}, 200)
}
} catch (e) {
reject(e)
}
})
// Start loading the database
await db.load()
})
})
})
describe('load from empty snapshot', function() {
it('loads database from an empty snapshot', async () => {
db = await orbitdb1.eventlog('empty-snapshot')
address = db.address.toString()
await db.saveSnapshot()
await db.close()
}
})
it('throws an error when trying to load a missing snapshot', async () => {
db = await orbitdb1.eventlog(address)
await db.drop()
db = null
db = await orbitdb1.eventlog(address)
let err
try {
await db.loadFromSnapshot()
} catch (e) {
err = e.toString()
}
assert.equal(err, `Error: Snapshot for ${address} not found!`)
})
it('loading a database emits \'ready\' event', async () => {
db = await orbitdb1.eventlog(address)
return new Promise(async (resolve) => {
db.events.on('ready', () => {
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount)
assert.equal(items[0].payload.value, 'hello0')
assert.equal(items[entryCount - 1].payload.value, 'hello' + (entryCount - 1))
resolve()
})
db = await orbitdb1.open(address)
await db.loadFromSnapshot()
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, 0)
})
})
it('loading a database emits \'load.progress\' event', async () => {
db = await orbitdb1.eventlog(address)
return new Promise(async (resolve, reject) => {
let count = 0
db.events.on('load.progress', (address, hash, entry) => {
count ++
try {
assert.equal(address, db.address.toString())
describe('load from snapshot', function() {
beforeEach(async () => {
const dbName = new Date().getTime().toString()
const entryArr = []
const { progress, max } = db.replicationStatus
assert.equal(max, entryCount)
assert.equal(progress, count)
for (let i = 0; i < entryCount; i ++)
entryArr.push(i)
assert.notEqual(hash, null)
assert.notEqual(entry, null)
if (progress === entryCount && count === entryCount) {
resolve()
}
} catch (e) {
reject(e)
}
})
// Start loading the database
db = await orbitdb1.eventlog(dbName)
address = db.address.toString()
await mapSeries(entryArr, (i) => db.add('hello' + i))
await db.saveSnapshot()
await db.close()
db = null
})
afterEach(async () => {
await db.drop()
})
it('loads database from snapshot', async () => {
db = await orbitdb1.eventlog(address)
await db.loadFromSnapshot()
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount)
assert.equal(items[0].payload.value, 'hello0')
assert.equal(items[entryCount - 1].payload.value, 'hello' + (entryCount - 1))
})
it('load, add one and save snapshot several times', async () => {
const amount = 4
for (let i = 0; i < amount; i ++) {
db = await orbitdb1.eventlog(address)
await db.loadFromSnapshot()
await db.add('hello' + (entryCount + i))
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount + i + 1)
assert.equal(items[0].payload.value, 'hello0')
assert.equal(items[items.length - 1].payload.value, 'hello' + (entryCount + i))
await db.saveSnapshot()
await db.close()
}
})
it('throws an error when trying to load a missing snapshot', async () => {
db = await orbitdb1.eventlog(address)
await db.drop()
db = null
db = await orbitdb1.eventlog(address)
let err
try {
await db.loadFromSnapshot()
} catch (e) {
err = e.toString()
}
assert.equal(err, `Error: Snapshot for ${address} not found!`)
})
it('loading a database emits \'ready\' event', async () => {
db = await orbitdb1.eventlog(address)
return new Promise(async (resolve) => {
db.events.on('ready', () => {
const items = db.iterator({ limit: -1 }).collect()
assert.equal(items.length, entryCount)
assert.equal(items[0].payload.value, 'hello0')
assert.equal(items[entryCount - 1].payload.value, 'hello' + (entryCount - 1))
resolve()
})
await db.loadFromSnapshot()
})
})
it('loading a database emits \'load.progress\' event', async () => {
db = await orbitdb1.eventlog(address)
return new Promise(async (resolve, reject) => {
let count = 0
db.events.on('load.progress', (address, hash, entry) => {
count ++
try {
assert.equal(address, db.address.toString())
const { progress, max } = db.replicationStatus
assert.equal(max, entryCount)
assert.equal(progress, count)
assert.notEqual(hash, null)
assert.notEqual(entry, null)
if (progress === entryCount && count === entryCount) {
resolve()
}
} catch (e) {
reject(e)
}
})
// Start loading the database
await db.loadFromSnapshot()
})
})
})
})

View File

@@ -0,0 +1,9 @@
const OrbitDbCache = require('orbit-db-cache/Cache.js')
const localdown = require('localstorage-down')
/**
* A custom cache example. To create a differing custom example, orbitdb cache was
* used with another abstract-leveldown compliant storage, localdown as an example
*/
module.exports = OrbitDbCache(localdown)

44
test/utils/databases.js Normal file
View File

@@ -0,0 +1,44 @@
const databases = [
{
type: 'eventlog',
create: (orbitdb, name, options) => orbitdb.eventlog(name, options),
tryInsert: (db) => db.add('hello'),
query: (db) => db.iterator({ limit: -1 }).collect(),
getTestValue: (db) => db.iterator({ limit: -1 }).collect()[0].payload.value,
expectedValue: 'hello',
},
{
type: 'feed',
create: (orbitdb, name, options) => orbitdb.feed(name, options),
tryInsert: (db) => db.add('hello'),
query: (db) => db.iterator({ limit: -1 }).collect(),
getTestValue: (db) => db.iterator({ limit: -1 }).collect()[0].payload.value,
expectedValue: 'hello',
},
{
type: 'key-value',
create: (orbitdb, name, options) => orbitdb.kvstore(name, options),
tryInsert: (db) => db.set('one', 'hello'),
query: (db) => [],
getTestValue: (db) => db.get('one'),
expectedValue: 'hello',
},
{
type: 'documents',
create: (orbitdb, name, options) => orbitdb.docstore(name, options),
tryInsert: (db) => db.put({ _id: 'hello world', doc: 'all the things'}),
query: (db) => [],
getTestValue: (db) => db.get('hello world'),
expectedValue: [{ _id: 'hello world', doc: 'all the things'}],
},
{
type: 'counter',
create: (orbitdb, name, options) => orbitdb.counter(name, options),
tryInsert: (db) => db.inc(8),
query: (db) => [],
getTestValue: (db) => db.value,
expectedValue: 8,
},
]
module.exports = databases

View File

@@ -6,3 +6,5 @@ exports.waitForPeers = require('./wait-for-peers')
exports.connectPeers = require('./connect-peers')
exports.MemStore = require('./mem-store')
exports.CustomTestKeystore = require('./custom-test-keystore')
exports.CustomTestCache = require('./custom-test-cache')
exports.databases = require('./databases')

View File

@@ -5,59 +5,17 @@ const rmrf = require('rimraf')
const OrbitDB = require('../src/OrbitDB')
// Include test utilities
const {
config,
startIpfs,
stopIpfs,
testAPIs,
const {
config,
startIpfs,
stopIpfs,
testAPIs,
databases,
} = require('./utils')
const dbPath = './orbitdb/tests/write-permissions'
const ipfsPath = './orbitdb/tests/write-permissions/ipfs'
const databases = [
{
type: 'eventlog',
create: (orbitdb, name, options) => orbitdb.eventlog(name, options),
tryInsert: (db) => db.add('hello'),
query: (db) => db.iterator({ limit: -1 }).collect(),
getTestValue: (db) => db.iterator({ limit: -1 }).collect()[0].payload.value,
expectedValue: 'hello',
},
{
type: 'feed',
create: (orbitdb, name, options) => orbitdb.feed(name, options),
tryInsert: (db) => db.add('hello'),
query: (db) => db.iterator({ limit: -1 }).collect(),
getTestValue: (db) => db.iterator({ limit: -1 }).collect()[0].payload.value,
expectedValue: 'hello',
},
{
type: 'key-value',
create: (orbitdb, name, options) => orbitdb.kvstore(name, options),
tryInsert: (db) => db.set('one', 'hello'),
query: (db) => [],
getTestValue: (db) => db.get('one'),
expectedValue: 'hello',
},
{
type: 'documents',
create: (orbitdb, name, options) => orbitdb.docstore(name, options),
tryInsert: (db) => db.put({ _id: 'hello world', doc: 'all the things'}),
query: (db) => [],
getTestValue: (db) => db.get('hello world'),
expectedValue: [{ _id: 'hello world', doc: 'all the things'}],
},
{
type: 'counter',
create: (orbitdb, name, options) => orbitdb.counter(name, options),
tryInsert: (db) => db.inc(8),
query: (db) => [],
getTestValue: (db) => db.value,
expectedValue: 8,
},
]
Object.keys(testAPIs).forEach(API => {
describe(`orbit-db - Write Permissions (${API})`, function() {
this.timeout(20000)
@@ -75,10 +33,10 @@ Object.keys(testAPIs).forEach(API => {
})
after(async () => {
if(orbitdb1)
if(orbitdb1)
await orbitdb1.stop()
if(orbitdb2)
if(orbitdb2)
await orbitdb2.stop()
if (ipfsd)
@@ -88,10 +46,10 @@ Object.keys(testAPIs).forEach(API => {
describe('allows multiple peers to write to the databases', function() {
databases.forEach(async (database) => {
it(database.type + ' allows multiple writers', async () => {
let options = {
let options = {
// Set write access for both clients
write: [
orbitdb1.key.getPublic('hex'),
orbitdb1.key.getPublic('hex'),
orbitdb2.key.getPublic('hex')
],
}
@@ -115,10 +73,10 @@ Object.keys(testAPIs).forEach(API => {
describe('syncs databases', function() {
databases.forEach(async (database) => {
it(database.type + ' syncs', async () => {
let options = {
let options = {
// Set write access for both clients
write: [
orbitdb1.key.getPublic('hex'),
orbitdb1.key.getPublic('hex'),
orbitdb2.key.getPublic('hex')
],
}
@@ -148,7 +106,7 @@ Object.keys(testAPIs).forEach(API => {
describe('syncs databases that anyone can write to', function() {
databases.forEach(async (database) => {
it(database.type + ' syncs', async () => {
let options = {
let options = {
// Set write permission for everyone
write: ['*'],
}
@@ -179,7 +137,7 @@ Object.keys(testAPIs).forEach(API => {
databases.forEach(async (database) => {
it(database.type + ' doesn\'t sync', async () => {
let options = {
let options = {
// Only peer 1 can write
write: [orbitdb1.key.getPublic('hex')],
}
@@ -228,7 +186,7 @@ Object.keys(testAPIs).forEach(API => {
describe('throws an error if peer is not allowed to write to the database', function() {
databases.forEach(async (database) => {
it(database.type + ' throws an error', async () => {
let options = {
let options = {
// No write access (only creator of the database can write)
write: [],
}