GoodDocs
Search…
User Storage
Describes how GoodDAPP uses Gun p2p database, to store user owned data.

Source

GunDB

We chose Gun since it's a javascript based, decentralized (p2p) with built in encryption database. Check out their website for more info

Usage

You can import the UserStorage instance anywhere in the code by doing
1
import userStorage from '/lib/gundb/UserStorage'
2
//you can make sure its initialized by waiting on 'ready'
3
await userStorage.ready
Copied!

Initialization

The constructor calls init(), which logs in the user into Gun. The user's password+pass are generated deterministically by signing a message with one of his HD wallet private keys.
source

Profile

The profile holds information of the user (signed with Gun SEA, so only he can modify it) such as:
    Name
    Email
    Mobile
    Wallet Address
    Username

Structure

Each profile field is an object of type:
1
export type ProfileField = {
2
value: EncryptedField,
3
display: string,
4
privacy: FieldPrivacy
5
}
Copied!
    value - is the SEA encrypted value, so only the user can read it.
    display - is the string displayed on the DAPP to other users
    privacy - is the privacy level of the field (masked, public, private)
Fields such email and mobile can be set to be public, private or masked, this is in order to let the user control what information he wishes to disclose. If they are public then users will be able to send funds to the user directly by simply typing his mobile, email or username in the app.

Index

We keep the users' profiles indexed by email, mobile (in case they are public), wallet address and username. This enables to connect blockchain transactions to user profiles. Specifically it is used in the user feed and in the "Send" flow to enable directly sending GoodDollars by mobile, email and username.
https://github.com/GoodDollar/GoodDAPP/blob/472b22a24dafac154409c2579dbbfcf4cf4e9922/src/lib/gundb/UserStorage.js#L504-L544
1
/**
2
* Generates index by field if privacy is public, or empty index if it's not public
3
*
4
* @param {string} field - Profile attribute
5
* @param {string} value - Profile attribute value
6
* @param {string} privacy - (private | public | masked)
7
* @returns Gun result promise after index is generated
8
* @todo This is world writable so theoritically a malicious user could delete the indexes
9
* need to develop for gundb immutable keys to non first user
10
*/
11
async indexProfileField(field: string, value: string, privacy: FieldPrivacy): Promise<ACK> {
12
if (!UserStorage.indexableFields[field]) return Promise.resolve({ err: 'Not indexable field', ok: 0 })
13
const cleanValue = UserStorage.cleanFieldForIndex(field, value)
14
if (!cleanValue) return Promise.resolve({ err: 'Indexable field cannot be null or empty', ok: 0 })
15
16
const indexNode = gun.get(`users/by${field}`).get(cleanValue)
17
logger.debug('indexProfileField', { field, cleanValue, value, privacy, indexNode })
Copied!
An issue with the index is that currently any user can overwrite any entry in the index, since nodes in GunDB are writable by everyone. We are working on an extension to GunDB to create append only nodes so an index key, once set by a user can not be changed by anyone else besides him
GitHub - GoodDollar/gun-appendOnly: Append only nodes for gundb
GitHub

examples

2
let value:string = await userStorage.getProfileFieldValue('email')
3
//the gun way
4
let field:ProfileField = await userStorage.profile.get('email').then()
Copied!

Feed

The feed holds all the blockchain transactions the user did but also other system and messaging events.

Feed Indexes

We keep 3 indexes for easy access and display purposes:
Text
Text
Text
Text
By ID
Fast access to event details
Each events is encrypted
gun.user().get('feed'). get('byid').get(<eventId>)
FeedEvent
Text
Text
Text
Text
By Date(daily)
Display sorted by time to user with a reasonable paging scheme
gun.user().get('feed') .get(<date granularity day>)
Array<[<datetime>,<eventId>]
Text
Text
Text
Text
Events count by date
Helper for pager to fetch next X events
gun.user().get('feed') .get('index').get(<date granularity day>)
Number
The in memory index is updated on every change to 'index' by add a gundb listener in the method initFeed: https://github.com/GoodDollar/GoodDAPP/blob/472b22a24dafac154409c2579dbbfcf4cf4e9922/src/lib/gundb/UserStorage.js#L339

Good first Issues

Last modified 14d ago