@fireproof/core 0.8.0-dev → 0.8.0-dev.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,144 @@
1
+ import * as CBW from '@ipld/car/buffer-writer';
2
+ import * as raw from 'multiformats/codecs/raw';
3
+ import { encrypt, decrypt } from '../crypto.js';
4
+ import { parse } from 'multiformats/link';
5
+ import { sha256 } from 'multiformats/hashes/sha2';
6
+ import * as Block from 'multiformats/block';
7
+ import { Buffer } from 'buffer';
8
+ // @ts-ignore
9
+ import { bf } from 'prolly-trees/utils';
10
+ // @ts-ignore
11
+ import { nocache as cache } from 'prolly-trees/cache';
12
+ const chunker = bf(30);
13
+ export async function getEmptyLoader() {
14
+ const theseWriteableBlocks = new VMemoryBlockstore();
15
+ return {
16
+ blocks: theseWriteableBlocks,
17
+ put: async (cid, bytes) => {
18
+ return await theseWriteableBlocks.put(cid, bytes);
19
+ },
20
+ get: async (cid) => {
21
+ const got = await theseWriteableBlocks.get(cid);
22
+ return got.bytes;
23
+ }
24
+ };
25
+ }
26
+ export class VMemoryBlockstore {
27
+ /** @type {Map<string, Uint8Array>} */
28
+ blocks = new Map();
29
+ instanceId = Math.random().toString(36).slice(2);
30
+ async get(cid) {
31
+ const bytes = this.blocks.get(cid.toString());
32
+ if (!bytes)
33
+ throw new Error('block not found ' + cid.toString());
34
+ return { cid, bytes };
35
+ }
36
+ /**
37
+ * @param {any} cid
38
+ * @param {Uint8Array} bytes
39
+ */
40
+ async put(cid, bytes) {
41
+ this.blocks.set(cid.toString(), bytes);
42
+ }
43
+ *entries() {
44
+ for (const [str, bytes] of this.blocks) {
45
+ yield { cid: parse(str), bytes };
46
+ }
47
+ }
48
+ }
49
+ export const blocksToCarBlock = async (rootCids, blocks) => {
50
+ // console.log('blocksToCarBlock', rootCids, blocks.constructor.name)
51
+ let size = 0;
52
+ if (!Array.isArray(rootCids)) {
53
+ rootCids = [rootCids];
54
+ }
55
+ const headerSize = CBW.headerLength({ roots: rootCids });
56
+ size += headerSize;
57
+ if (!Array.isArray(blocks)) {
58
+ blocks = Array.from(blocks.entries());
59
+ }
60
+ for (const { cid, bytes } of blocks) {
61
+ // console.log(cid, bytes)
62
+ size += CBW.blockLength({ cid, bytes });
63
+ }
64
+ const buffer = new Uint8Array(size);
65
+ const writer = await CBW.createWriter(buffer, { headerSize });
66
+ for (const cid of rootCids) {
67
+ writer.addRoot(cid);
68
+ }
69
+ for (const { cid, bytes } of blocks) {
70
+ writer.write({ cid, bytes });
71
+ }
72
+ await writer.close();
73
+ return await Block.encode({ value: writer.bytes, hasher: sha256, codec: raw });
74
+ };
75
+ export const blocksToEncryptedCarBlock = async (innerBlockStoreClockRootCid, blocks, keyMaterial, cids) => {
76
+ const encryptionKey = Buffer.from(keyMaterial, 'hex');
77
+ const encryptedBlocks = [];
78
+ const theCids = cids;
79
+ // console.trace('blocksToEncryptedCarBlock', blocks)
80
+ // for (const { cid } of blocks.entries()) {
81
+ // theCids.push(cid.toString())
82
+ // }
83
+ // console.log(
84
+ // 'encrypting',
85
+ // theCids.length,
86
+ // 'blocks',
87
+ // theCids.includes(innerBlockStoreClockRootCid.toString()),
88
+ // keyMaterial
89
+ // )
90
+ // console.log('cids', theCids, innerBlockStoreClockRootCid.toString())
91
+ let last;
92
+ for await (const block of encrypt({
93
+ cids: theCids,
94
+ get: async (cid) => {
95
+ // console.log('getencrypt', cid)
96
+ const got = blocks.get(cid);
97
+ // console.log('got', got)
98
+ return got.block ? ({ cid, bytes: got.block }) : got;
99
+ },
100
+ key: encryptionKey,
101
+ hasher: sha256,
102
+ chunker,
103
+ cache,
104
+ // codec: dagcbor, // should be crypto?
105
+ root: innerBlockStoreClockRootCid
106
+ })) {
107
+ encryptedBlocks.push(block);
108
+ last = block;
109
+ }
110
+ // console.log('last', last.cid.toString(), 'for clock', innerBlockStoreClockRootCid.toString())
111
+ const encryptedCar = await blocksToCarBlock(last.cid, encryptedBlocks);
112
+ return encryptedCar;
113
+ };
114
+ // { root, get, key, cache, chunker, hasher }
115
+ const memoizeDecryptedCarBlocks = new Map();
116
+ export const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
117
+ if (memoizeDecryptedCarBlocks.has(cid.toString())) {
118
+ return memoizeDecryptedCarBlocks.get(cid.toString());
119
+ }
120
+ else {
121
+ const blocksPromise = (async () => {
122
+ const decryptionKey = Buffer.from(keyMaterial, 'hex');
123
+ // console.log('decrypting', keyMaterial, cid.toString())
124
+ const cids = new Set();
125
+ const decryptedBlocks = [];
126
+ for await (const block of decrypt({
127
+ root: cid,
128
+ get,
129
+ key: decryptionKey,
130
+ chunker,
131
+ hasher: sha256,
132
+ cache
133
+ // codec: dagcbor
134
+ })) {
135
+ // console.log('decrypted', block.cid.toString())
136
+ decryptedBlocks.push(block);
137
+ cids.add(block.cid.toString());
138
+ }
139
+ return { blocks: decryptedBlocks, cids };
140
+ })();
141
+ memoizeDecryptedCarBlocks.set(cid.toString(), blocksPromise);
142
+ return blocksPromise;
143
+ }
144
+ };
@@ -52,7 +52,7 @@ export class Browser extends Base {
52
52
  if (this.config.readonly)
53
53
  return;
54
54
  try {
55
- return localStorage.setItem(this.headerKey(branch), this.prepareHeader(header));
55
+ return localStorage.setItem(this.headerKey(branch), header);
56
56
  }
57
57
  catch (e) { }
58
58
  }
@@ -40,12 +40,11 @@ export class Filesystem extends Base {
40
40
  return JSON.parse(header);
41
41
  }
42
42
  async writeHeader(branch, header) {
43
- // console.log('saveHeader', this.isBrowser)
43
+ // console.log('saveHeader fs', header)
44
44
  if (this.config.readonly)
45
45
  return;
46
- const pHeader = this.prepareHeader(header);
47
46
  // console.log('writeHeader fs', branch, pHeader)
48
- await writeSync(this.headerFilename(branch), pHeader);
47
+ await writeSync(this.headerFilename(branch), header);
49
48
  }
50
49
  headerFilename(branch = 'main') {
51
50
  // console.log('headerFilename', this.config.dataDir, this.name)
@@ -45,11 +45,10 @@ export class Rest extends Base {
45
45
  async writeHeader(branch, header) {
46
46
  if (this.config.readonly)
47
47
  return;
48
- const pHeader = this.prepareHeader(header);
49
48
  // console.log('writeHeader rt', branch, pHeader)
50
49
  const response = await fetch(this.headerURL(branch), {
51
50
  method: 'PUT',
52
- body: pHeader,
51
+ body: header,
53
52
  headers: { 'Content-Type': 'application/json' }
54
53
  });
55
54
  if (!response.ok)
@@ -0,0 +1,144 @@
1
+ import * as CBW from '@ipld/car/buffer-writer';
2
+ import * as raw from 'multiformats/codecs/raw';
3
+ import { encrypt, decrypt } from '../crypto.js';
4
+ import { parse } from 'multiformats/link';
5
+ import { sha256 } from 'multiformats/hashes/sha2';
6
+ import * as Block from 'multiformats/block';
7
+ import { Buffer } from 'buffer';
8
+ // @ts-ignore
9
+ import { bf } from 'prolly-trees/utils';
10
+ // @ts-ignore
11
+ import { nocache as cache } from 'prolly-trees/cache';
12
+ const chunker = bf(30);
13
+ export async function getEmptyLoader() {
14
+ const theseWriteableBlocks = new VMemoryBlockstore();
15
+ return {
16
+ blocks: theseWriteableBlocks,
17
+ put: async (cid, bytes) => {
18
+ return await theseWriteableBlocks.put(cid, bytes);
19
+ },
20
+ get: async (cid) => {
21
+ const got = await theseWriteableBlocks.get(cid);
22
+ return got.bytes;
23
+ }
24
+ };
25
+ }
26
+ export class VMemoryBlockstore {
27
+ /** @type {Map<string, Uint8Array>} */
28
+ blocks = new Map();
29
+ instanceId = Math.random().toString(36).slice(2);
30
+ async get(cid) {
31
+ const bytes = this.blocks.get(cid.toString());
32
+ if (!bytes)
33
+ throw new Error('block not found ' + cid.toString());
34
+ return { cid, bytes };
35
+ }
36
+ /**
37
+ * @param {any} cid
38
+ * @param {Uint8Array} bytes
39
+ */
40
+ async put(cid, bytes) {
41
+ this.blocks.set(cid.toString(), bytes);
42
+ }
43
+ *entries() {
44
+ for (const [str, bytes] of this.blocks) {
45
+ yield { cid: parse(str), bytes };
46
+ }
47
+ }
48
+ }
49
+ export const blocksToCarBlock = async (rootCids, blocks) => {
50
+ // console.log('blocksToCarBlock', rootCids, blocks.constructor.name)
51
+ let size = 0;
52
+ if (!Array.isArray(rootCids)) {
53
+ rootCids = [rootCids];
54
+ }
55
+ const headerSize = CBW.headerLength({ roots: rootCids });
56
+ size += headerSize;
57
+ if (!Array.isArray(blocks)) {
58
+ blocks = Array.from(blocks.entries());
59
+ }
60
+ for (const { cid, bytes } of blocks) {
61
+ // console.log(cid, bytes)
62
+ size += CBW.blockLength({ cid, bytes });
63
+ }
64
+ const buffer = new Uint8Array(size);
65
+ const writer = await CBW.createWriter(buffer, { headerSize });
66
+ for (const cid of rootCids) {
67
+ writer.addRoot(cid);
68
+ }
69
+ for (const { cid, bytes } of blocks) {
70
+ writer.write({ cid, bytes });
71
+ }
72
+ await writer.close();
73
+ return await Block.encode({ value: writer.bytes, hasher: sha256, codec: raw });
74
+ };
75
+ export const blocksToEncryptedCarBlock = async (innerBlockStoreClockRootCid, blocks, keyMaterial, cids) => {
76
+ const encryptionKey = Buffer.from(keyMaterial, 'hex');
77
+ const encryptedBlocks = [];
78
+ const theCids = cids;
79
+ // console.trace('blocksToEncryptedCarBlock', blocks)
80
+ // for (const { cid } of blocks.entries()) {
81
+ // theCids.push(cid.toString())
82
+ // }
83
+ // console.log(
84
+ // 'encrypting',
85
+ // theCids.length,
86
+ // 'blocks',
87
+ // theCids.includes(innerBlockStoreClockRootCid.toString()),
88
+ // keyMaterial
89
+ // )
90
+ // console.log('cids', theCids, innerBlockStoreClockRootCid.toString())
91
+ let last;
92
+ for await (const block of encrypt({
93
+ cids: theCids,
94
+ get: async (cid) => {
95
+ // console.log('getencrypt', cid)
96
+ const got = blocks.get(cid);
97
+ // console.log('got', got)
98
+ return got.block ? ({ cid, bytes: got.block }) : got;
99
+ },
100
+ key: encryptionKey,
101
+ hasher: sha256,
102
+ chunker,
103
+ cache,
104
+ // codec: dagcbor, // should be crypto?
105
+ root: innerBlockStoreClockRootCid
106
+ })) {
107
+ encryptedBlocks.push(block);
108
+ last = block;
109
+ }
110
+ // console.log('last', last.cid.toString(), 'for clock', innerBlockStoreClockRootCid.toString())
111
+ const encryptedCar = await blocksToCarBlock(last.cid, encryptedBlocks);
112
+ return encryptedCar;
113
+ };
114
+ // { root, get, key, cache, chunker, hasher }
115
+ const memoizeDecryptedCarBlocks = new Map();
116
+ export const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
117
+ if (memoizeDecryptedCarBlocks.has(cid.toString())) {
118
+ return memoizeDecryptedCarBlocks.get(cid.toString());
119
+ }
120
+ else {
121
+ const blocksPromise = (async () => {
122
+ const decryptionKey = Buffer.from(keyMaterial, 'hex');
123
+ // console.log('decrypting', keyMaterial, cid.toString())
124
+ const cids = new Set();
125
+ const decryptedBlocks = [];
126
+ for await (const block of decrypt({
127
+ root: cid,
128
+ get,
129
+ key: decryptionKey,
130
+ chunker,
131
+ hasher: sha256,
132
+ cache
133
+ // codec: dagcbor
134
+ })) {
135
+ // console.log('decrypted', block.cid.toString())
136
+ decryptedBlocks.push(block);
137
+ cids.add(block.cid.toString());
138
+ }
139
+ return { blocks: decryptedBlocks, cids };
140
+ })();
141
+ memoizeDecryptedCarBlocks.set(cid.toString(), blocksPromise);
142
+ return blocksPromise;
143
+ }
144
+ };
package/dist/sync.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import SimplePeer from 'simple-peer';
2
2
  import { parseCID } from './database.js';
3
3
  import { decodeEventBlock } from './clock.js';
4
- import { blocksToCarBlock, blocksToEncryptedCarBlock } from './storage/base.js';
4
+ import { blocksToCarBlock, blocksToEncryptedCarBlock } from './storage/utils.js';
5
5
  import { CarReader } from '@ipld/car';
6
6
  /**
7
7
  * @typedef {import('./database.js').Database} Database
package/dist/valet.js CHANGED
@@ -75,16 +75,22 @@ export class Valet {
75
75
  if (this.secondary) {
76
76
  // console.log('getValetBlock secondary', dataCID)
77
77
  try {
78
+ // eslint-disable-next-line
78
79
  const { block, reader } = await this.secondary.getLoaderBlock(dataCID);
80
+ const writeableCarReader = await this.primary.getWriteableCarReader(reader);
81
+ // console.log('getValetBlock secondary', dataCID, block.length)
82
+ // eslint-disable-next-line
79
83
  const cids = new Set();
80
84
  for await (const { cid } of reader.entries()) {
81
85
  // console.log(cid, bytes)
82
86
  cids.add(cid.toString());
83
87
  }
84
- reader.get = reader.gat; // some consumers prefer get
88
+ // reader.get = reader.gat // some consumers prefer get
85
89
  // console.log('replicating', reader.root)
86
- reader.lastCid = reader.root.cid;
87
- await this.primary.parkCar(reader, [...cids]);
90
+ writeableCarReader.lastCid = reader.root.cid;
91
+ writeableCarReader.head = [];
92
+ await this.primary.parkCar(writeableCarReader, cids).catch(e => console.error('parkCar error', e));
93
+ // console.log('FIX THIS', did)
88
94
  return block;
89
95
  }
90
96
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fireproof/core",
3
- "version": "0.8.0-dev",
3
+ "version": "0.8.0-dev.3",
4
4
  "description": "Live data for React, accelerated by proofs, powered by IPFS",
5
5
  "main": "dist/src/fireproof.js",
6
6
  "module": "dist/src/fireproof.mjs",
@@ -42,6 +42,8 @@
42
42
  "@ipld/car": "^5.1.0",
43
43
  "@ipld/dag-cbor": "^9.0.0",
44
44
  "@jsonlines/core": "^1.0.2",
45
+ "@web3-storage/clock": "^0.3.0",
46
+ "@web3-storage/w3up-client": "^7.0.0",
45
47
  "async": "^3.2.4",
46
48
  "charwise": "^3.0.1",
47
49
  "cross-fetch": "^3.1.6",
package/src/crypto.js CHANGED
@@ -16,7 +16,11 @@ const encrypt = async function * ({ get, cids, hasher, key, cache, chunker, root
16
16
  let eroot
17
17
  for (const string of cids) {
18
18
  const cid = CID.parse(string)
19
- const unencrypted = await get(cid)
19
+ let unencrypted = await get(cid)
20
+ if (!unencrypted.cid) {
21
+ unencrypted = { cid, bytes: unencrypted }
22
+ }
23
+ // console.log('unencrypted', unencrypted)
20
24
  const block = await encode({ ...await codec.encrypt({ ...unencrypted, key }), codec, hasher })
21
25
  // console.log(`encrypting ${string} as ${block.cid}`)
22
26
  yield block
package/src/database.js CHANGED
@@ -5,6 +5,8 @@ import charwise from 'charwise'
5
5
  import { CID } from 'multiformats'
6
6
  import { DbIndex as Index } from './db-index.js'
7
7
 
8
+ import { Remote } from './remote.js'
9
+
8
10
  // TypeScript Types
9
11
  // eslint-disable-next-line no-unused-vars
10
12
  // import { CID } from 'multiformats/dist/types/src/cid.js'
@@ -29,14 +31,15 @@ export class Database {
29
31
  indexes = new Map()
30
32
  rootCache = null
31
33
  eventsCache = new Map()
32
-
34
+ remote = null
35
+ name = ''
33
36
  constructor (name, config = {}) {
34
37
  this.name = name
35
38
  this.clock = []
36
39
  this.instanceId = `fp.${this.name}.${Math.random().toString(36).substring(2, 7)}`
37
40
  this.blocks = new TransactionBlockstore(name, config)
38
41
  this.indexBlocks = new TransactionBlockstore(name ? name + '.indexes' : null, { primary: config.index })
39
-
42
+ this.remote = new Remote(this, name, config)
40
43
  this.config = config
41
44
  // todo we can wait for index blocks elsewhere
42
45
  this.ready = Promise.all([this.blocks.ready, this.indexBlocks.ready]).then(([blocksReady, indexReady]) => {
@@ -52,7 +55,7 @@ export class Database {
52
55
  clock.add(cid)
53
56
  }
54
57
  if (header.index) {
55
- this.indexBlocks.valet.primary.setCarCidMapCarCid(header.index.car)
58
+ this.indexBlocks.valet.primary.setLastCar(header.index.car)
56
59
  this.indexBlocks.valet.primary.setKeyMaterial(header.index.key)
57
60
  }
58
61
  if (header.indexes) {
@@ -96,7 +99,7 @@ export class Database {
96
99
  name: this.name,
97
100
  index: {
98
101
  key: this.indexBlocks.valet?.primary.keyMaterial,
99
- car: this.indexBlocks.valet?.primary.valetRootCarCid?.toString()
102
+ car: this.indexBlocks.valet?.primary.lastCar?.toString()
100
103
  },
101
104
  indexes: [...this.indexes.values()].map(index => index.toJSON())
102
105
  }
@@ -112,9 +115,9 @@ export class Database {
112
115
  return (clock || this.clock).map(cid => cid.toString())
113
116
  }
114
117
 
115
- maybeSaveClock () {
118
+ async maybeSaveClock () {
116
119
  if (this.name && this.blocks.valet) {
117
- this.blocks.valet.saveHeader(this.toHeader())
120
+ await this.blocks.valet.saveHeader(this.toHeader())
118
121
  }
119
122
  }
120
123
 
package/src/remote.js ADDED
@@ -0,0 +1,113 @@
1
+ // when you call database.connect(email)
2
+ // it will return a promise that resolves when the user is logged in
3
+ // and sends you an email
4
+
5
+ import { create } from '@web3-storage/w3up-client'
6
+ import * as w3clock from '@web3-storage/clock/client'
7
+ import { CID } from 'multiformats'
8
+
9
+ export class Remote {
10
+ client = null
11
+ name = 'unset'
12
+ config = {}
13
+
14
+ constructor (database, name, config) {
15
+ this.name = name
16
+ this.config = config
17
+ this.database = database
18
+ }
19
+
20
+ async clock (cid) {
21
+ // const did = this.client.currentSpace()
22
+ const agent = this.client.agent()
23
+ const head = await w3clock.head({ issuer: agent, with: agent.did(), proofs: [] })
24
+ console.log('head', head, JSON.stringify(head.root.data.ocm.out))
25
+ const headCids = head.root.data.ocm.out.ok.head
26
+ const blocks = await Promise.all([this.database.blocks.get(CID.parse(cid)),
27
+ ...headCids.map(c => this.database.blocks.get(c))])
28
+
29
+ console.log('blocks', blocks)
30
+ const adv = await w3clock.advance({ issuer: agent, with: agent.did(), proofs: [] }, CID.parse(cid)
31
+ , { blocks }
32
+ )
33
+ console.log('adv', adv, JSON.stringify(adv.root.data.ocm.out))
34
+ return { head, adv }
35
+ }
36
+
37
+ async sync (cid) {
38
+ // fetch the remote clock headCids using w3clock.head
39
+ const agent = this.client.agent()
40
+ const head = await w3clock.head({ issuer: agent, with: agent.did(), proofs: [] })
41
+ console.log('head', head, JSON.stringify(head.root.data.ocm.out))
42
+ const headCids = head.root.data.ocm.out.ok.head
43
+ const lastSyncHead = await this.database.blocks.valet.primary.getLastSynced()
44
+ console.log('lastSyncHead', lastSyncHead)
45
+ const headSet = new Set(headCids.map(c => c.toString()))
46
+ const lastSyncSet = new Set(lastSyncHead.map(c => c.toString()))
47
+
48
+ // are they the same?
49
+ const same = headSet.size === lastSyncSet.size && [...headSet].every(value => lastSyncSet.has(value))
50
+
51
+ // if the headCids and the lastSyncHead are the same, we are in sync and can push
52
+ if (same) {
53
+ const currentHead = this.database.clock
54
+ const currentHeadSet = new Set(currentHead.map(c => c.toString()))
55
+
56
+ console.log('synced with cloud', headSet, lastSyncSet)
57
+
58
+ // are they the same?
59
+ const currentSame = headSet.size === currentHeadSet.size && [...headSet].every(value => currentHeadSet.has(value))
60
+ if (currentSame) {
61
+ // we are in sync, do nothing
62
+ return true
63
+ } else {
64
+ console.log('push to cloud', headSet, currentHeadSet)
65
+ // we are ahead of the remote, push our clock
66
+ // const lastCompact = this.database.blocks.valet.primary.getLastCompact()
67
+ // get a compact since the last sync
68
+ console.log('we are ahead of the remote, push our clock')
69
+ // const compact = this.database.blocks.valet.primary.getCompactSince(lastSyncHead)
70
+ }
71
+ } else {
72
+ // we are behind, fetch the remote
73
+ console.log('we are behind, fetch the remote')
74
+ }
75
+
76
+ // if it is the same as the local (current metadata carcid? `newValetCidCar` / sync clock), do nothing, we are in sync
77
+ // if it is the same as our previously pushed clock event, but our local clock is ahead of it, we need to push our clock
78
+ // - we can store the previous clock event cid in the metadata
79
+ // - sending our updates:
80
+ // - get the _last_sync and _last_compact values from our metadata
81
+ // - if last sync is after last compact
82
+ // - make a merged car file for the syncs
83
+ // - else
84
+ // - upload the car file for the last compact
85
+ // - make a merge car file for any uncompacted car files since the last compact, it should base its cidMap on the compact car file (as we go the sync stream will need to track it's own cidMap)
86
+ // - if there is only one car file, it is the merge car file (already based on last compact)
87
+ // - upload the merge car file
88
+ // - create a new clock block with the current w3clock.head as parent and the merge car file cid as the data
89
+ // - update the remote clock with the new clock block (it doesn't need to fetch the car file, and we dont need to store the clock blocks locally, just the most recent one)
90
+ //
91
+ // else if the remote head is not contained by our clock, it is is ahead of the local sync clock.
92
+ // - get the car file it points to from its data field
93
+ // - merge to the local clock (park that car so we have both carcid indexes)
94
+ // - calculate a new root from the merged head, and update the local clock
95
+ }
96
+
97
+ async connect (email) {
98
+ try {
99
+ const client = await create()
100
+ await client.authorize(email)
101
+ const claims = await client.capability.access.claim()
102
+ console.log('claims', claims)
103
+ const space = await client.createSpace('fp.' + this.name)
104
+ console.log('space', space)
105
+ await client.setCurrentSpace(space.did())
106
+ await client.registerSpace(email)
107
+ this.client = client
108
+ console.log('client', client)
109
+ } catch (err) {
110
+ console.error('registration failed: ', err)
111
+ }
112
+ }
113
+ }