autobee 2.11.0 → 2.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -6,7 +6,6 @@ const Hyperbee = require('hyperbee2')
6
6
  const ID = require('hypercore-id-encoding')
7
7
  const rrp = require('resolve-reject-promise')
8
8
  const Signal = require('signal-promise')
9
- const { AutobeeEncryption, WriterEncryption, ViewEncryption } = require('autobee-encryption')
10
9
  const AutobeeWakeup = require('autobee-wakeup')
11
10
  const Hypercore = require('hypercore')
12
11
  const crypto = require('hypercore-crypto')
@@ -16,6 +15,7 @@ const boot = require('./lib/boot.js')
16
15
  const { DEFAULT_MANIFEST_VERSION } = require('./lib/constants.js')
17
16
  const { resolveWeight, currentWeight } = require('./lib/witness.js')
18
17
  const encoding = require('./lib/encoding.js')
18
+ const { AutobeeEncryption, WriterEncryption, ViewEncryption } = require('./lib/encryption.js')
19
19
  const FastForward = require('./lib/fast-forward.js')
20
20
  const System = require('./lib/system.js')
21
21
  const ApplyCalls = require('./lib/apply-calls.js')
@@ -684,18 +684,16 @@ module.exports = class Autobee extends ReadyResource {
684
684
  }
685
685
 
686
686
  async compactMaybe() {
687
- if (!this.writers.writable) return 0
687
+ if (!this.writers.writable) return
688
688
 
689
- if (await this._isReindexed()) return 0
689
+ if (await this._isReindexed()) return
690
690
 
691
- const view = await this._reindexBee(this._workingBee)
692
- if (view > 0) this.bee.move(this._workingBee.head())
691
+ await this._reindexBee(this._workingBee)
692
+ this.bee.move(this._workingBee.head())
693
693
 
694
- const system = await this._reindexBee(this.system.bee)
694
+ await this._reindexBee(this.system.bee)
695
695
 
696
- if (view + system > 0) this._reindexed = true
697
-
698
- return view + system
696
+ this._reindexed = true
699
697
  }
700
698
 
701
699
  async _isReindexed() {
@@ -711,11 +709,16 @@ module.exports = class Autobee extends ReadyResource {
711
709
  async _reindexBee(bee) {
712
710
  const head = bee.head()
713
711
  const local = bee.context.local
714
- if (head === null) return 0
715
- if ((await this._shouldReindex(local.key)) && local.length > 0) return 0
716
- if (!(await this._shouldReindex(head.key))) return 0
712
+ if (head === null) return
713
+ if (await this._shouldReindex(local.key)) return
714
+ if (!(await this._shouldReindex(head.key))) return
715
+
716
+ // a non-empty local core is a completed reindex whose head was never stored
717
+ if (local.length === 0) {
718
+ await bee.reindex(async (change) => !(await this._shouldReindex(change.head.key)))
719
+ }
717
720
 
718
- return bee.reindex(async (change) => !(await this._shouldReindex(change.head.key)))
721
+ bee.move({ key: local.key, length: local.length })
719
722
  }
720
723
 
721
724
  async _shouldReindex(key, { unknown = false, length = 0, timeout = 0 } = {}) {
package/lib/encoding.js CHANGED
@@ -1,10 +1,10 @@
1
1
  const b4a = require('b4a')
2
2
  const c = require('compact-encoding')
3
3
  const crypto = require('hypercore-crypto')
4
- const { AutobeeEncryption, WriterEncryption } = require('autobee-encryption')
5
4
 
6
5
  const { getEncoding } = require('../encoding/spec/autobee')
7
- const { LEGACY_OPLOG_VERSION, OPLOG_VERSION } = require('./constants')
6
+ const { LEGACY_OPLOG_VERSION, OPLOG_VERSION } = require('./constants.js')
7
+ const { AutobeeEncryption, WriterEncryption } = require('./encryption.js')
8
8
 
9
9
  const Oplog = getEncoding('@autobee/oplog')
10
10
  const Link = getEncoding('@autobee/link')
@@ -0,0 +1,259 @@
1
+ const HypercoreEncryption = require('hypercore/lib/default-encryption.js')
2
+ const sodium = require('sodium-native')
3
+ const crypto = require('hypercore-crypto')
4
+ const c = require('compact-encoding')
5
+ const b4a = require('b4a')
6
+
7
+ const { DEFAULT_MANIFEST_VERSION } = require('./constants.js')
8
+
9
+ const ManifestData = {
10
+ preencode(state, m) {
11
+ c.uint.preencode(state, m.version)
12
+ state.end++ // max flag is 2 so always one byte
13
+
14
+ if (m.legacyBlocks) c.uint.preencode(state, m.legacyBlocks)
15
+ if (m.namespace) c.fixed32.preencode(state, m.namespace)
16
+ },
17
+ encode(state, m) {
18
+ const flags = (m.legacyBlocks ? 1 : 0) | (m.namespace ? 2 : 0)
19
+
20
+ c.uint.encode(state, m.version)
21
+ c.uint.encode(state, flags)
22
+
23
+ if (m.legacyBlocks) c.uint.encode(state, m.legacyBlocks)
24
+ if (m.namespace) c.fixed32.encode(state, m.namespace)
25
+ },
26
+ decode(state) {
27
+ const r0 = c.uint.decode(state)
28
+ const flags = c.uint.decode(state)
29
+
30
+ return {
31
+ version: r0,
32
+ legacyBlocks: (flags & 1) !== 0 ? c.uint.decode(state) : 0,
33
+ namespace: (flags & 2) !== 0 ? c.fixed32.decode(state) : null
34
+ }
35
+ }
36
+ }
37
+
38
+ const [, NS_VIEW_BLOCK_KEY, NS_HASH_KEY] = crypto.namespace('autobase', 4)
39
+
40
+ const [GENESIS_ENTROPY] = crypto.namespace('autobase/entropy', 2)
41
+
42
+ const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
43
+ const hash = nonce.subarray(0, sodium.crypto_generichash_BYTES_MIN)
44
+
45
+ class AutobeeEncryption {
46
+ static PADDING = 8
47
+
48
+ constructor(auto) {
49
+ this.auto = auto
50
+ }
51
+
52
+ padding() {
53
+ return AutobeeEncryption.PADDING
54
+ }
55
+
56
+ isCompat(ctx) {
57
+ return ctx.manifest.version <= 1
58
+ }
59
+
60
+ async getKeys(id, ctx) {
61
+ if (!this.auto.encryptionKey) return null
62
+
63
+ const entropy = GENESIS_ENTROPY
64
+ const block = this.blockKey(entropy, ctx)
65
+ const hash = crypto.hash([NS_HASH_KEY, block])
66
+
67
+ return {
68
+ id,
69
+ block,
70
+ hash
71
+ }
72
+ }
73
+
74
+ blockKey(entropy, ctx) {
75
+ return getBlockKey(this.auto.key, this.auto.encryptionKey, entropy, ctx.key)
76
+ }
77
+
78
+ compatKeys() {
79
+ throw new Error('Compatibility method is not specified')
80
+ }
81
+
82
+ async encrypt(index, block, fork, ctx) {
83
+ // writer cores are long lasting, but view cores should be up to date
84
+ if (this instanceof ViewEncryption && ctx.manifest.version < DEFAULT_MANIFEST_VERSION) {
85
+ throw new Error(`Unexpected manifest: version < ${DEFAULT_MANIFEST_VERSION}`)
86
+ }
87
+
88
+ if (this.isCompat(ctx, index)) {
89
+ const compat = this.compatKeys(ctx)
90
+ return HypercoreEncryption.encrypt(index, block, fork, compat.block, compat.blinding)
91
+ }
92
+
93
+ const keys = await this.getKeys(0, ctx)
94
+
95
+ encryptBlock(index, block, keys.id, keys.block, keys.hash)
96
+ }
97
+
98
+ async decrypt(index, block, ctx) {
99
+ if (this.isCompat(ctx, index)) {
100
+ return HypercoreEncryption.decrypt(index, block, this.compatKeys(ctx).block)
101
+ }
102
+
103
+ const padding = block.subarray(0, AutobeeEncryption.PADDING)
104
+ block = block.subarray(AutobeeEncryption.PADDING)
105
+
106
+ const type = padding[0]
107
+ switch (type) {
108
+ case 0:
109
+ return block // unencrypted
110
+
111
+ case 1:
112
+ break
113
+
114
+ default:
115
+ throw new Error('Unrecognised encryption type')
116
+ }
117
+
118
+ const id = c.uint32.decode({ start: 4, end: 8, buffer: padding })
119
+
120
+ const keys = await this.getKeys(id, ctx)
121
+
122
+ c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
123
+ nonce.set(padding, 8, 16)
124
+
125
+ // Decrypt the block using the full nonce
126
+ decrypt(block, nonce, keys.block)
127
+ }
128
+
129
+ static encryptAnchor(block, bootstrap, encryptionKey, namespace) {
130
+ const entropy = GENESIS_ENTROPY
131
+
132
+ const blockKey = getBlockKey(bootstrap, encryptionKey, entropy, namespace)
133
+ const hashKey = crypto.hash([NS_HASH_KEY, block])
134
+
135
+ encryptBlock(0, block, 0, blockKey, hashKey)
136
+ }
137
+
138
+ static async setSystemEncryption(bootstrap, encryptionKey, core, opts) {
139
+ await core.ready()
140
+ if (core.manifest.version === 1) {
141
+ const key = getCompatBlockKey(bootstrap, encryptionKey, '_system')
142
+ core.setEncryptionKey(key, { block: true })
143
+ return
144
+ }
145
+
146
+ const info = {
147
+ key: bootstrap,
148
+ encryptionKey
149
+ }
150
+
151
+ return core.setEncryption(new ViewEncryption(info, '_system'))
152
+ }
153
+
154
+ static getSystemEncryption(bootstrap, encryptionKey) {
155
+ return AutobeeEncryption.getViewEncryption(bootstrap, encryptionKey, '_system')
156
+ }
157
+
158
+ static getViewEncryption(bootstrap, encryptionKey, name) {
159
+ const info = { key: bootstrap, encryptionKey }
160
+ return new ViewEncryption(info, name)
161
+ }
162
+
163
+ static getWriterEncryption(bootstrap, encryptionKey) {
164
+ return new WriterEncryption({ key: bootstrap, encryptionKey })
165
+ }
166
+ }
167
+
168
+ class ViewEncryption extends AutobeeEncryption {
169
+ constructor(auto, name) {
170
+ super(auto)
171
+ this.name = name
172
+ }
173
+
174
+ compatKeys() {
175
+ const block = getCompatBlockKey(this.auto.key, this.auto.encryptionKey, this.name)
176
+ return {
177
+ block,
178
+ blinding: crypto.hash(block)
179
+ }
180
+ }
181
+
182
+ blockKey(entropy, ctx) {
183
+ // compat encryption
184
+ if (ctx.manifest.version < DEFAULT_MANIFEST_VERSION) {
185
+ return getCompatBlockKey(this.auto.key, entropy, this.name)
186
+ }
187
+
188
+ return getBlockKey(this.auto.key, this.auto.encryptionKey, entropy, ctx.key)
189
+ }
190
+ }
191
+
192
+ class WriterEncryption extends AutobeeEncryption {
193
+ compatKeys(ctx) {
194
+ return HypercoreEncryption.deriveKeys(this.auto.encryptionKey, ctx.key)
195
+ }
196
+
197
+ blockKey(entropy, ctx) {
198
+ // used for anchors
199
+ if (ctx.manifest.userData) {
200
+ const userData = c.decode(ManifestData, ctx.manifest.userData)
201
+ if (userData.namespace !== null) {
202
+ const b = getBlockKey(this.auto.key, this.auto.encryptionKey, entropy, userData.namespace)
203
+ return b
204
+ }
205
+ }
206
+
207
+ return getBlockKey(this.auto.key, this.auto.encryptionKey, entropy, ctx.key)
208
+ }
209
+ }
210
+
211
+ module.exports = {
212
+ AutobeeEncryption,
213
+ WriterEncryption,
214
+ ViewEncryption
215
+ }
216
+
217
+ function encrypt(block, nonce, key) {
218
+ sodium.crypto_stream_xor(block, block, nonce, key)
219
+ }
220
+
221
+ function decrypt(block, nonce, key) {
222
+ return encrypt(block, nonce, key) // symmetric
223
+ }
224
+
225
+ function getBlockKey(bootstrap, encryptionKey, entropy, hypercoreKey) {
226
+ return (
227
+ encryptionKey &&
228
+ crypto.hash([NS_VIEW_BLOCK_KEY, bootstrap, encryptionKey, entropy, hypercoreKey])
229
+ )
230
+ }
231
+
232
+ function getCompatBlockKey(bootstrap, encryptionKey, name) {
233
+ if (typeof name === 'string') return getCompatBlockKey(bootstrap, encryptionKey, b4a.from(name))
234
+ return encryptionKey && crypto.hash([NS_VIEW_BLOCK_KEY, bootstrap, encryptionKey, name])
235
+ }
236
+
237
+ function blockhash(block, padding, hashKey) {
238
+ sodium.crypto_generichash(hash, block, hashKey)
239
+ padding.set(hash.subarray(0, 8)) // copy first 8 bytes of hash
240
+ hash.fill(0) // clear nonce buffer
241
+ }
242
+
243
+ function encryptBlock(index, block, id, blockKey, hashKey) {
244
+ const padding = block.subarray(0, AutobeeEncryption.PADDING)
245
+ block = block.subarray(AutobeeEncryption.PADDING)
246
+
247
+ blockhash(block, padding, hashKey)
248
+ c.uint32.encode({ start: 4, end: 8, buffer: padding }, id)
249
+
250
+ c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
251
+
252
+ padding[0] = 1 // version in plaintext
253
+
254
+ nonce.set(padding, 8, 16)
255
+
256
+ // The combination of index, key id, fork id and block hash is very likely
257
+ // to be unique for a given Hypercore and therefore our nonce is suitable
258
+ encrypt(block, nonce, blockKey)
259
+ }
package/lib/migrations.js CHANGED
@@ -1,9 +1,9 @@
1
1
  const c = require('compact-encoding')
2
2
  const b4a = require('b4a')
3
3
  const crypto = require('hypercore-crypto')
4
- const { AutobeeEncryption } = require('autobee-encryption')
5
4
  const { decodeBlock } = require('hyperbee2/lib/encoding.js')
6
5
 
6
+ const { AutobeeEncryption } = require('./encryption.js')
7
7
  const encoding = require('./encoding.js')
8
8
  const { assert, bail } = require('./asserts.js')
9
9
 
package/lib/writers.js CHANGED
@@ -3,7 +3,7 @@ const encoding = require('./encoding.js')
3
3
  const Triggers = require('./triggers.js')
4
4
  const { LEGACY_OPLOG_VERSION } = require('./constants.js')
5
5
  const { resolveWeight } = require('./witness.js')
6
- const { WriterEncryption } = require('autobee-encryption')
6
+ const { WriterEncryption } = require('./encryption.js')
7
7
  const ReadWriteLock = require('read-write-mutexify')
8
8
 
9
9
  const WRITER_PREFETCH = 10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autobee",
3
- "version": "2.11.0",
3
+ "version": "2.11.1",
4
4
  "description": "Bee based autobase",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Holepunch Inc.",
@@ -23,11 +23,10 @@
23
23
  "test:generate": "brittle -r test/all.js test/*.js"
24
24
  },
25
25
  "dependencies": {
26
- "autobee-encryption": "^0.1.2",
27
26
  "autobee-wakeup": "^1.1.3",
28
27
  "b4a": "^1.8.0",
29
- "compact-encoding": "^3.3.0",
30
- "hyperbee2": "^2.17.0",
28
+ "compact-encoding": "^3.4.0",
29
+ "hyperbee2": "^2.17.1",
31
30
  "hypercore": "^11.37.0",
32
31
  "hypercore-id-encoding": "^1.3.0",
33
32
  "hyperschema": "^1.26.2",