hypercore 10.18.4 → 10.19.0
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 +12 -6
- package/lib/batch.js +193 -33
- package/lib/core.js +3 -3
- package/lib/merkle-tree.js +106 -74
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -658,6 +658,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
658
658
|
return null
|
|
659
659
|
}
|
|
660
660
|
|
|
661
|
+
createTreeBatch () {
|
|
662
|
+
return this.core.tree.batch()
|
|
663
|
+
}
|
|
664
|
+
|
|
661
665
|
findingPeers () {
|
|
662
666
|
this._findingPeers++
|
|
663
667
|
if (this.replicator !== null && !this.closing) this.replicator.findingPeers++
|
|
@@ -709,9 +713,9 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
709
713
|
return true
|
|
710
714
|
}
|
|
711
715
|
|
|
712
|
-
batch () {
|
|
716
|
+
batch ({ autoClose = true, session = true } = {}) {
|
|
713
717
|
if (this._batch !== null) throw BATCH_ALREADY_EXISTS()
|
|
714
|
-
const batch = new Batch(this)
|
|
718
|
+
const batch = new Batch(session ? this.session() : this, autoClose)
|
|
715
719
|
for (const session of this.sessions) session._batch = batch
|
|
716
720
|
return batch
|
|
717
721
|
}
|
|
@@ -878,7 +882,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
878
882
|
}
|
|
879
883
|
|
|
880
884
|
async truncate (newLength = 0, fork = -1) {
|
|
881
|
-
if (this._batch
|
|
885
|
+
if (this._batch) throw BATCH_UNFLUSHED()
|
|
886
|
+
|
|
882
887
|
if (this.opened === false) await this.opening
|
|
883
888
|
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
884
889
|
|
|
@@ -889,8 +894,9 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
889
894
|
this.replicator.updateAll()
|
|
890
895
|
}
|
|
891
896
|
|
|
892
|
-
async append (blocks) {
|
|
893
|
-
if (this._batch &&
|
|
897
|
+
async append (blocks, opts) {
|
|
898
|
+
if (this._batch && this !== this._batch.session) throw BATCH_UNFLUSHED()
|
|
899
|
+
|
|
894
900
|
if (this.opened === false) await this.opening
|
|
895
901
|
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
896
902
|
|
|
@@ -906,7 +912,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
906
912
|
}
|
|
907
913
|
}
|
|
908
914
|
|
|
909
|
-
return this.core.append(buffers, this.auth, { preappend })
|
|
915
|
+
return this.core.append(buffers, (opts && opts.auth) || this.auth, { preappend })
|
|
910
916
|
}
|
|
911
917
|
|
|
912
918
|
async treeHash (length) {
|
package/lib/batch.js
CHANGED
|
@@ -1,66 +1,196 @@
|
|
|
1
|
-
const { SESSION_NOT_WRITABLE,
|
|
1
|
+
const { SESSION_NOT_WRITABLE, BLOCK_NOT_AVAILABLE, SESSION_CLOSED } = require('hypercore-errors')
|
|
2
|
+
const EventEmitter = require('events')
|
|
3
|
+
const c = require('compact-encoding')
|
|
4
|
+
|
|
5
|
+
module.exports = class HypercoreBatch extends EventEmitter {
|
|
6
|
+
constructor (session, autoClose) {
|
|
7
|
+
super()
|
|
2
8
|
|
|
3
|
-
module.exports = class Batch {
|
|
4
|
-
constructor (session) {
|
|
5
9
|
this.session = session
|
|
6
|
-
this.
|
|
10
|
+
this.opened = false
|
|
11
|
+
this.closed = false
|
|
12
|
+
this.opening = null
|
|
13
|
+
this.closing = null
|
|
14
|
+
this.autoClose = autoClose
|
|
7
15
|
|
|
8
16
|
this._appends = []
|
|
9
17
|
this._byteLength = 0
|
|
18
|
+
this._fork = 0
|
|
19
|
+
this._sessionLength = 0
|
|
20
|
+
this._sessionByteLength = 0
|
|
21
|
+
this._flushing = null
|
|
22
|
+
|
|
23
|
+
this.opening = this.ready().catch(noop)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get id () {
|
|
27
|
+
return this.session.id
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get key () {
|
|
31
|
+
return this.session.key
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get discoveryKey () {
|
|
35
|
+
return this.session.discoveryKey
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get indexedLength () {
|
|
39
|
+
return this._sessionLength
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get indexedByteLength () {
|
|
43
|
+
return this._sessionByteLength
|
|
10
44
|
}
|
|
11
45
|
|
|
12
46
|
get length () {
|
|
13
|
-
return this.
|
|
47
|
+
return this._sessionLength + this._appends.length
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get byteLength () {
|
|
51
|
+
return this._sessionByteLength + this._byteLength
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get writable () {
|
|
55
|
+
return this.session.writable
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get core () {
|
|
59
|
+
return this.session.core
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async ready () {
|
|
63
|
+
await this.session.ready()
|
|
64
|
+
if (this.opened) return
|
|
65
|
+
this._sessionLength = this.session.length
|
|
66
|
+
this._sessionByteLength = this.session.byteLength
|
|
67
|
+
this.opened = true
|
|
68
|
+
this.emit('ready')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async update (opts) {
|
|
72
|
+
if (this.opened === false) await this.ready()
|
|
73
|
+
await this.session.update(opts)
|
|
14
74
|
}
|
|
15
75
|
|
|
16
|
-
|
|
17
|
-
return this.session.
|
|
76
|
+
setUserData (key, value, opts) {
|
|
77
|
+
return this.session.setUserData(key, value, opts)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getUserData (key, opts) {
|
|
81
|
+
return this.session.getUserData(key, opts)
|
|
18
82
|
}
|
|
19
83
|
|
|
20
84
|
async info (opts) {
|
|
21
85
|
const session = this.session
|
|
22
86
|
const info = await session.info(opts)
|
|
23
87
|
|
|
24
|
-
|
|
88
|
+
info.length = this._sessionLength
|
|
89
|
+
|
|
90
|
+
if (info.contiguousLength >= info.length) {
|
|
25
91
|
info.contiguousLength = info.length += this._appends.length
|
|
26
92
|
} else {
|
|
27
93
|
info.length += this._appends.length
|
|
28
94
|
}
|
|
29
95
|
|
|
30
|
-
info.byteLength
|
|
96
|
+
info.byteLength = this._sessionByteLength + this._byteLength
|
|
31
97
|
|
|
32
98
|
return info
|
|
33
99
|
}
|
|
34
100
|
|
|
101
|
+
async seek (bytes, opts) {
|
|
102
|
+
if (this.opened === false) await this.opening
|
|
103
|
+
if (this.closing) throw SESSION_CLOSED()
|
|
104
|
+
|
|
105
|
+
if (bytes < this._sessionByteLength) return await this.session.seek(bytes, opts)
|
|
106
|
+
|
|
107
|
+
bytes -= this._sessionByteLength
|
|
108
|
+
|
|
109
|
+
let i = 0
|
|
110
|
+
|
|
111
|
+
for (const blk of this._appends) {
|
|
112
|
+
if (bytes < blk.byteLength) return [this._sessionLength + i, bytes]
|
|
113
|
+
i++
|
|
114
|
+
bytes -= blk.byteLength
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (bytes === 0) return [this._sessionLength + i, 0]
|
|
118
|
+
|
|
119
|
+
throw BLOCK_NOT_AVAILABLE()
|
|
120
|
+
}
|
|
121
|
+
|
|
35
122
|
async get (index, opts) {
|
|
36
|
-
|
|
37
|
-
if (
|
|
123
|
+
if (this.opened === false) await this.opening
|
|
124
|
+
if (this.closing) throw SESSION_CLOSED()
|
|
38
125
|
|
|
39
|
-
const length = this.
|
|
126
|
+
const length = this._sessionLength
|
|
40
127
|
if (index < length) return this.session.get(index, opts)
|
|
41
128
|
|
|
42
|
-
|
|
129
|
+
const buffer = this._appends[index - length] || null
|
|
130
|
+
if (!buffer) throw BLOCK_NOT_AVAILABLE()
|
|
131
|
+
|
|
132
|
+
const encoding = (opts && opts.valueEncoding && c.from(opts.valueEncoding)) || this.session.valueEncoding
|
|
133
|
+
if (!encoding) return buffer
|
|
134
|
+
|
|
135
|
+
return c.decode(encoding, buffer)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async _waitForFlush () {
|
|
139
|
+
// wait for any pending flush...
|
|
140
|
+
while (this._flushing) {
|
|
141
|
+
await this._flushing
|
|
142
|
+
await Promise.resolve() // yield in case a new flush is queued
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
createTreeBatch (length, blocks = []) {
|
|
147
|
+
if (!length && length !== 0) length = this.length + blocks.length
|
|
148
|
+
|
|
149
|
+
const maxLength = this.length + blocks.length
|
|
150
|
+
const b = this.session.createTreeBatch()
|
|
151
|
+
const len = Math.min(length, this.length)
|
|
152
|
+
|
|
153
|
+
if (len < this._sessionLength || length > maxLength) return null
|
|
154
|
+
|
|
155
|
+
for (let i = 0; i < len - this._sessionLength; i++) {
|
|
156
|
+
b.append(this._appends[i])
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (len < this.length) return b
|
|
160
|
+
|
|
161
|
+
for (let i = 0; i < length - len; i++) {
|
|
162
|
+
b.append(blocks[i])
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return b
|
|
43
166
|
}
|
|
44
167
|
|
|
45
168
|
async truncate (newLength) {
|
|
46
|
-
if (this.
|
|
169
|
+
if (this.opened === false) await this.opening
|
|
170
|
+
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
171
|
+
if (this.closing) throw SESSION_CLOSED()
|
|
47
172
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (session.writable === false) throw SESSION_NOT_WRITABLE()
|
|
173
|
+
// wait for any pending flush... (prop needs a lock)
|
|
174
|
+
await this._waitForFlush()
|
|
51
175
|
|
|
52
|
-
const length =
|
|
176
|
+
const length = this._sessionLength
|
|
53
177
|
if (newLength < length) throw new Error('Cannot truncate committed blocks')
|
|
54
178
|
|
|
55
179
|
this._appends.length = newLength - length
|
|
180
|
+
this._fork++
|
|
181
|
+
|
|
182
|
+
this.emit('truncate', newLength, this.fork)
|
|
56
183
|
}
|
|
57
184
|
|
|
58
185
|
async append (blocks) {
|
|
59
|
-
if (this.flushed) throw BATCH_ALREADY_FLUSHED()
|
|
60
|
-
|
|
61
186
|
const session = this.session
|
|
62
|
-
|
|
63
|
-
if (
|
|
187
|
+
|
|
188
|
+
if (this.opened === false) await this.opening
|
|
189
|
+
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
190
|
+
if (this.closing) throw SESSION_CLOSED()
|
|
191
|
+
|
|
192
|
+
// wait for any pending flush... (prop needs a lock)
|
|
193
|
+
await this._waitForFlush()
|
|
64
194
|
|
|
65
195
|
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
66
196
|
|
|
@@ -78,37 +208,67 @@ module.exports = class Batch {
|
|
|
78
208
|
|
|
79
209
|
this._appends.push(...buffers)
|
|
80
210
|
|
|
81
|
-
const
|
|
211
|
+
const info = { length: this.length, byteLength: this.byteLength }
|
|
212
|
+
this.emit('append')
|
|
82
213
|
|
|
83
|
-
return
|
|
214
|
+
return info
|
|
84
215
|
}
|
|
85
216
|
|
|
86
|
-
async flush () {
|
|
87
|
-
if (this.
|
|
88
|
-
this.
|
|
217
|
+
async flush (length = this._appends.length, auth) {
|
|
218
|
+
if (this.opened === false) await this.opening
|
|
219
|
+
if (this.closing) throw SESSION_CLOSED()
|
|
220
|
+
|
|
221
|
+
while (this._flushing) await this._flushing
|
|
222
|
+
this._flushing = this._flush(length, auth)
|
|
89
223
|
|
|
90
224
|
try {
|
|
91
|
-
|
|
225
|
+
await this._flushing
|
|
92
226
|
} finally {
|
|
93
|
-
this.
|
|
94
|
-
this._clearAppends()
|
|
227
|
+
this._flushing = null
|
|
95
228
|
}
|
|
229
|
+
|
|
230
|
+
if (this.autoClose) await this.close()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async _flush (length, auth) { // TODO: make this safe to interact with a parallel truncate...
|
|
234
|
+
if (this._appends.length === 0) return
|
|
235
|
+
|
|
236
|
+
const flushingLength = Math.min(length, this._appends.length)
|
|
237
|
+
const info = await this.session.append(flushingLength < this._appends.length ? this._appends.slice(0, flushingLength) : this._appends, { auth })
|
|
238
|
+
const delta = info.byteLength - this._sessionByteLength
|
|
239
|
+
|
|
240
|
+
this._sessionLength = info.length
|
|
241
|
+
this._sessionByteLength = info.byteLength
|
|
242
|
+
this._appends = this._appends.slice(flushingLength)
|
|
243
|
+
this._byteLength -= delta
|
|
244
|
+
|
|
245
|
+
this.emit('flush')
|
|
96
246
|
}
|
|
97
247
|
|
|
98
|
-
|
|
99
|
-
if (this.
|
|
100
|
-
this.
|
|
248
|
+
close () {
|
|
249
|
+
if (!this.closing) this.closing = this._close()
|
|
250
|
+
return this.closing
|
|
251
|
+
}
|
|
101
252
|
|
|
253
|
+
async _close () {
|
|
102
254
|
this._clearBatch()
|
|
103
255
|
this._clearAppends()
|
|
256
|
+
|
|
257
|
+
await this.session.close()
|
|
258
|
+
|
|
259
|
+
this.closed = true
|
|
260
|
+
this.emit('close')
|
|
104
261
|
}
|
|
105
262
|
|
|
106
263
|
_clearAppends () {
|
|
107
264
|
this._appends = []
|
|
108
265
|
this._byteLength = 0
|
|
266
|
+
this._fork = 0
|
|
109
267
|
}
|
|
110
268
|
|
|
111
269
|
_clearBatch () {
|
|
112
270
|
for (const session of this.session.sessions) session._batch = null
|
|
113
271
|
}
|
|
114
272
|
}
|
|
273
|
+
|
|
274
|
+
function noop () {}
|
package/lib/core.js
CHANGED
|
@@ -232,7 +232,7 @@ module.exports = class Core {
|
|
|
232
232
|
|
|
233
233
|
try {
|
|
234
234
|
const batch = await this.tree.truncate(length, fork)
|
|
235
|
-
batch.signature = await auth.sign(batch.signable())
|
|
235
|
+
batch.signature = await auth.sign(batch.signable(), batch)
|
|
236
236
|
await this._truncate(batch, null)
|
|
237
237
|
} finally {
|
|
238
238
|
this.truncating--
|
|
@@ -322,7 +322,7 @@ module.exports = class Core {
|
|
|
322
322
|
for (const val of values) batch.append(val)
|
|
323
323
|
|
|
324
324
|
const hash = batch.hash()
|
|
325
|
-
batch.signature = await auth.sign(this._legacy ? batch.signableLegacy(hash) : batch.signable(hash))
|
|
325
|
+
batch.signature = await auth.sign(this._legacy ? batch.signableLegacy(hash) : batch.signable(hash), batch)
|
|
326
326
|
|
|
327
327
|
const entry = {
|
|
328
328
|
userData: null,
|
|
@@ -359,7 +359,7 @@ module.exports = class Core {
|
|
|
359
359
|
|
|
360
360
|
_signed (batch, hash, auth = this.defaultAuth) {
|
|
361
361
|
const signable = this._legacy ? batch.signableLegacy(hash) : batch.signable(hash)
|
|
362
|
-
return auth.verify(signable, batch.signature)
|
|
362
|
+
return auth.verify(signable, batch.signature, batch)
|
|
363
363
|
}
|
|
364
364
|
|
|
365
365
|
async _verifyExclusive ({ batch, bitfield, value, from }) {
|
package/lib/merkle-tree.js
CHANGED
|
@@ -85,6 +85,34 @@ class MerkleTreeBatch {
|
|
|
85
85
|
return caps.treeSignableLegacy(hash, this.length, this.fork)
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
get (index) {
|
|
89
|
+
if (index >= this.length * 2) {
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (index < this.treeLength * 2) {
|
|
94
|
+
return this.tree.get(index)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const n of this.nodes) {
|
|
98
|
+
if (n.index === index) return n
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
proof ({ block, hash, seek, upgrade }) {
|
|
105
|
+
return generateProof(this, block, hash, seek, upgrade)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
verifyUpgrade (proof) {
|
|
109
|
+
const unverified = verifyTree(proof, this.tree.crypto, this.nodes)
|
|
110
|
+
|
|
111
|
+
if (!proof.upgrade) throw INVALID_OPERATION('Expected upgrade proof')
|
|
112
|
+
|
|
113
|
+
return verifyUpgrade(proof, unverified, this)
|
|
114
|
+
}
|
|
115
|
+
|
|
88
116
|
append (buf) {
|
|
89
117
|
const head = this.length * 2
|
|
90
118
|
const ite = flat.iterator(head)
|
|
@@ -640,80 +668,8 @@ module.exports = class MerkleTree {
|
|
|
640
668
|
return batch
|
|
641
669
|
}
|
|
642
670
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
// and finalise being called, otherwise there will be lingering promises in the background
|
|
646
|
-
|
|
647
|
-
const fork = this.fork
|
|
648
|
-
const signature = this.signature
|
|
649
|
-
const head = 2 * this.length
|
|
650
|
-
const from = upgrade ? upgrade.start * 2 : 0
|
|
651
|
-
const to = upgrade ? from + upgrade.length * 2 : head
|
|
652
|
-
const node = normalizeIndexed(block, hash)
|
|
653
|
-
|
|
654
|
-
if (from >= to || to > head) {
|
|
655
|
-
throw INVALID_OPERATION('Invalid upgrade')
|
|
656
|
-
}
|
|
657
|
-
if (seek && upgrade && node !== null && node.index >= from) {
|
|
658
|
-
throw INVALID_OPERATION('Cannot both do a seek and block/hash request when upgrading')
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
let subTree = head
|
|
662
|
-
|
|
663
|
-
const p = {
|
|
664
|
-
node: null,
|
|
665
|
-
seek: null,
|
|
666
|
-
upgrade: null,
|
|
667
|
-
additionalUpgrade: null
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
if (node !== null && (!upgrade || node.lastIndex < upgrade.start)) {
|
|
671
|
-
subTree = nodesToRoot(node.index, node.nodes, to)
|
|
672
|
-
const seekRoot = seek ? await seekUntrustedTree(this, subTree, seek.bytes) : head
|
|
673
|
-
blockAndSeekProof(this, node, seek, seekRoot, subTree, p)
|
|
674
|
-
} else if ((node || seek) && upgrade) {
|
|
675
|
-
subTree = seek ? await seekFromHead(this, to, seek.bytes) : node.index
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
if (upgrade) {
|
|
679
|
-
upgradeProof(this, node, seek, from, to, subTree, p)
|
|
680
|
-
if (head > to) additionalUpgradeProof(this, to, head, p)
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
const [pNode, pSeek, pUpgrade, pAdditional] = await settleProof(p)
|
|
684
|
-
const result = { fork, block: null, hash: null, seek: null, upgrade: null }
|
|
685
|
-
|
|
686
|
-
if (block) {
|
|
687
|
-
result.block = {
|
|
688
|
-
index: block.index,
|
|
689
|
-
value: null, // populated upstream, alloc it here for simplicity
|
|
690
|
-
nodes: pNode
|
|
691
|
-
}
|
|
692
|
-
} else if (hash) {
|
|
693
|
-
result.hash = {
|
|
694
|
-
index: hash.index,
|
|
695
|
-
nodes: pNode
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
if (seek && pSeek !== null) {
|
|
700
|
-
result.seek = {
|
|
701
|
-
bytes: seek.bytes,
|
|
702
|
-
nodes: pSeek
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
if (upgrade) {
|
|
707
|
-
result.upgrade = {
|
|
708
|
-
start: upgrade.start,
|
|
709
|
-
length: upgrade.length,
|
|
710
|
-
nodes: pUpgrade,
|
|
711
|
-
additionalNodes: pAdditional || [],
|
|
712
|
-
signature
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
return result
|
|
671
|
+
proof ({ block, hash, seek, upgrade }) {
|
|
672
|
+
return generateProof(this, block, hash, seek, upgrade)
|
|
717
673
|
}
|
|
718
674
|
|
|
719
675
|
// Successor to .nodes()
|
|
@@ -1216,3 +1172,79 @@ async function settleProof (p) {
|
|
|
1216
1172
|
throw err
|
|
1217
1173
|
}
|
|
1218
1174
|
}
|
|
1175
|
+
|
|
1176
|
+
// tree can be either the merkle tree or a merkle tree batch
|
|
1177
|
+
async function generateProof (tree, block, hash, seek, upgrade) {
|
|
1178
|
+
// Important that this does not throw inbetween making the promise arrays
|
|
1179
|
+
// and finalise being called, otherwise there will be lingering promises in the background
|
|
1180
|
+
|
|
1181
|
+
const fork = tree.fork
|
|
1182
|
+
const signature = tree.signature
|
|
1183
|
+
const head = 2 * tree.length
|
|
1184
|
+
const from = upgrade ? upgrade.start * 2 : 0
|
|
1185
|
+
const to = upgrade ? from + upgrade.length * 2 : head
|
|
1186
|
+
const node = normalizeIndexed(block, hash)
|
|
1187
|
+
if (from >= to || to > head) {
|
|
1188
|
+
throw INVALID_OPERATION('Invalid upgrade')
|
|
1189
|
+
}
|
|
1190
|
+
if (seek && upgrade && node !== null && node.index >= from) {
|
|
1191
|
+
throw INVALID_OPERATION('Cannot both do a seek and block/hash request when upgrading')
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
let subTree = head
|
|
1195
|
+
|
|
1196
|
+
const p = {
|
|
1197
|
+
node: null,
|
|
1198
|
+
seek: null,
|
|
1199
|
+
upgrade: null,
|
|
1200
|
+
additionalUpgrade: null
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
if (node !== null && (!upgrade || node.lastIndex < upgrade.start)) {
|
|
1204
|
+
subTree = nodesToRoot(node.index, node.nodes, to)
|
|
1205
|
+
const seekRoot = seek ? await seekUntrustedTree(tree, subTree, seek.bytes) : head
|
|
1206
|
+
blockAndSeekProof(tree, node, seek, seekRoot, subTree, p)
|
|
1207
|
+
} else if ((node || seek) && upgrade) {
|
|
1208
|
+
subTree = seek ? await seekFromHead(tree, to, seek.bytes) : node.index
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
if (upgrade) {
|
|
1212
|
+
upgradeProof(tree, node, seek, from, to, subTree, p)
|
|
1213
|
+
if (head > to) additionalUpgradeProof(tree, to, head, p)
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
const [pNode, pSeek, pUpgrade, pAdditional] = await settleProof(p)
|
|
1217
|
+
const result = { fork, block: null, hash: null, seek: null, upgrade: null }
|
|
1218
|
+
|
|
1219
|
+
if (block) {
|
|
1220
|
+
result.block = {
|
|
1221
|
+
index: block.index,
|
|
1222
|
+
value: null, // populated upstream, alloc it here for simplicity
|
|
1223
|
+
nodes: pNode
|
|
1224
|
+
}
|
|
1225
|
+
} else if (hash) {
|
|
1226
|
+
result.hash = {
|
|
1227
|
+
index: hash.index,
|
|
1228
|
+
nodes: pNode
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
if (seek && pSeek !== null) {
|
|
1233
|
+
result.seek = {
|
|
1234
|
+
bytes: seek.bytes,
|
|
1235
|
+
nodes: pSeek
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
if (upgrade) {
|
|
1240
|
+
result.upgrade = {
|
|
1241
|
+
start: upgrade.start,
|
|
1242
|
+
length: upgrade.length,
|
|
1243
|
+
nodes: pUpgrade,
|
|
1244
|
+
additionalNodes: pAdditional || [],
|
|
1245
|
+
signature
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
return result
|
|
1250
|
+
}
|