hypercore 10.22.2 → 10.23.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 +72 -46
- package/lib/batch.js +30 -19
- package/lib/caps.js +8 -14
- package/lib/core.js +95 -68
- package/lib/manifest.js +218 -0
- package/lib/merkle-tree.js +6 -6
- package/lib/messages.js +91 -5
- package/lib/multisig.js +123 -0
- package/lib/replicator.js +47 -21
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -15,6 +15,7 @@ const BlockEncryption = require('./lib/block-encryption')
|
|
|
15
15
|
const Info = require('./lib/info')
|
|
16
16
|
const Download = require('./lib/download')
|
|
17
17
|
const Batch = require('./lib/batch')
|
|
18
|
+
const { manifestHash, defaultSignerManifest, createVerifier, createManifest, isCompat } = require('./lib/manifest')
|
|
18
19
|
const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
|
|
19
20
|
const {
|
|
20
21
|
BAD_ARGUMENT,
|
|
@@ -69,7 +70,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
69
70
|
|
|
70
71
|
this.id = null
|
|
71
72
|
this.key = key || null
|
|
72
|
-
this.keyPair = null
|
|
73
|
+
this.keyPair = opts.keyPair || null
|
|
73
74
|
this.readable = true
|
|
74
75
|
this.writable = false
|
|
75
76
|
this.opened = false
|
|
@@ -77,22 +78,23 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
77
78
|
this.snapshotted = !!opts.snapshot
|
|
78
79
|
this.sparse = opts.sparse !== false
|
|
79
80
|
this.sessions = opts._sessions || [this]
|
|
80
|
-
this.auth = opts.auth || null
|
|
81
81
|
this.autoClose = !!opts.autoClose
|
|
82
82
|
this.onwait = opts.onwait || null
|
|
83
83
|
this.wait = opts.wait !== false
|
|
84
84
|
this.timeout = opts.timeout || 0
|
|
85
|
-
this._clone = opts.clone || null
|
|
86
|
-
this._readonly = opts.writable === false
|
|
87
|
-
|
|
88
85
|
this.closing = null
|
|
89
|
-
this.opening =
|
|
90
|
-
this.opening.catch(noop)
|
|
86
|
+
this.opening = null
|
|
91
87
|
|
|
88
|
+
this._manifest = opts.manifest || null
|
|
89
|
+
this._clone = opts.clone || null
|
|
90
|
+
this._readonly = opts.writable === false
|
|
92
91
|
this._preappend = preappend.bind(this)
|
|
93
92
|
this._snapshot = null
|
|
94
93
|
this._batch = opts._batch || null
|
|
95
94
|
this._findingPeers = 0
|
|
95
|
+
|
|
96
|
+
this.opening = this._openSession(key, storage, opts)
|
|
97
|
+
this.opening.catch(noop)
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
[inspect] (depth, opts) {
|
|
@@ -139,6 +141,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
139
141
|
indent + ')'
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
static key (manifest, { compat } = {}) {
|
|
145
|
+
return compat ? manifest.signer.publicKey : manifestHash(manifest)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static discoveryKey (key) {
|
|
149
|
+
return hypercoreCrypto.discoveryKey(key)
|
|
150
|
+
}
|
|
151
|
+
|
|
142
152
|
static getProtocolMuxer (stream) {
|
|
143
153
|
return stream.noiseStream.userData
|
|
144
154
|
}
|
|
@@ -245,20 +255,19 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
245
255
|
}
|
|
246
256
|
|
|
247
257
|
setKeyPair (keyPair) {
|
|
248
|
-
this.
|
|
249
|
-
this.writable =
|
|
258
|
+
this.keyPair = keyPair
|
|
259
|
+
this.writable = this._isWritable()
|
|
250
260
|
}
|
|
251
261
|
|
|
252
262
|
_passCapabilities (o) {
|
|
253
|
-
if (!this.
|
|
254
|
-
|
|
263
|
+
if (!this.keyPair) this.keyPair = o.keyPair
|
|
255
264
|
this.crypto = o.crypto
|
|
256
265
|
this.id = o.id
|
|
257
266
|
this.key = o.key
|
|
258
267
|
this.core = o.core
|
|
259
268
|
this.replicator = o.replicator
|
|
260
269
|
this.encryption = o.encryption
|
|
261
|
-
this.writable =
|
|
270
|
+
this.writable = this._isWritable()
|
|
262
271
|
this.autoClose = o.autoClose
|
|
263
272
|
|
|
264
273
|
if (this.snapshotted && this.core && !this._snapshot) this._updateSnapshot()
|
|
@@ -288,14 +297,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
288
297
|
if (!isFirst) await opts._opening
|
|
289
298
|
if (opts.preload) opts = { ...opts, ...(await this._retryPreload(opts.preload)) }
|
|
290
299
|
|
|
291
|
-
const keyPair = opts.keyPair
|
|
292
|
-
|
|
293
|
-
if (opts.auth) {
|
|
294
|
-
this.auth = opts.auth
|
|
295
|
-
} else if (keyPair && keyPair.secretKey) {
|
|
296
|
-
this.setKeyPair(keyPair)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
300
|
if (isFirst) {
|
|
300
301
|
await this._openCapabilities(key, storage, opts)
|
|
301
302
|
// Only the root session should pass capabilities to other sessions.
|
|
@@ -306,15 +307,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
306
307
|
|
|
307
308
|
// copy state over
|
|
308
309
|
if (this._clone) {
|
|
309
|
-
const { from,
|
|
310
|
+
const { from, signature } = this._clone
|
|
310
311
|
await from.opening
|
|
311
|
-
await this.core.copyFrom(from.core,
|
|
312
|
+
await this.core.copyFrom(from.core, signature)
|
|
312
313
|
this._clone = null
|
|
313
314
|
}
|
|
314
315
|
}
|
|
315
316
|
|
|
316
|
-
|
|
317
|
-
this.writable = !this._readonly && !!this.auth && !!this.auth.sign
|
|
317
|
+
this.writable = this._isWritable()
|
|
318
318
|
|
|
319
319
|
if (opts.valueEncoding) {
|
|
320
320
|
this.valueEncoding = c.from(opts.valueEncoding)
|
|
@@ -353,7 +353,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
353
353
|
this.storage = Hypercore.defaultStorage(opts.storage || storage, { unlocked, writable: !unlocked })
|
|
354
354
|
|
|
355
355
|
this.core = await Core.open(this.storage, {
|
|
356
|
-
compat: opts.compat
|
|
356
|
+
compat: opts.compat,
|
|
357
357
|
force: opts.force,
|
|
358
358
|
createIfMissing: opts.createIfMissing,
|
|
359
359
|
readonly: unlocked,
|
|
@@ -362,7 +362,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
362
362
|
keyPair: opts.keyPair,
|
|
363
363
|
crypto: this.crypto,
|
|
364
364
|
legacy: opts.legacy,
|
|
365
|
-
|
|
365
|
+
manifest: opts.manifest,
|
|
366
366
|
onupdate: this._oncoreupdate.bind(this),
|
|
367
367
|
onconflict: this._oncoreconflict.bind(this)
|
|
368
368
|
})
|
|
@@ -417,6 +417,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
417
417
|
return prev.length !== next.length || prev.fork !== next.fork
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
+
_isWritable () {
|
|
421
|
+
return !this._readonly && !!(this.keyPair && this.keyPair.secretKey)
|
|
422
|
+
}
|
|
423
|
+
|
|
420
424
|
close (err) {
|
|
421
425
|
if (this.closing) return this.closing
|
|
422
426
|
this.closing = this._close(err || null)
|
|
@@ -465,23 +469,22 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
465
469
|
this.emit('close', true)
|
|
466
470
|
}
|
|
467
471
|
|
|
468
|
-
clone (storage, opts = {}) {
|
|
472
|
+
clone (keyPair, storage, opts = {}) {
|
|
469
473
|
// TODO: current limitation is no forking
|
|
470
474
|
if ((opts.fork && opts.fork !== 0) || this.fork !== 0) {
|
|
471
475
|
throw BAD_ARGUMENT('Cannot clone a fork')
|
|
472
476
|
}
|
|
473
477
|
|
|
474
|
-
const
|
|
475
|
-
const
|
|
478
|
+
const manifest = opts.manifest || defaultSignerManifest(keyPair.publicKey)
|
|
479
|
+
const key = opts.key || (opts.compat !== false ? manifest.signer.publicKey : manifestHash(manifest))
|
|
476
480
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
auth = opts.auth
|
|
480
|
-
} else if (keyPair && keyPair.secretKey) {
|
|
481
|
-
auth = Core.createAuth(this.crypto, { keyPair, manifest: { signer: { publicKey: keyPair.publicKey } } })
|
|
481
|
+
if (b4a.equals(key, this.key)) {
|
|
482
|
+
throw BAD_ARGUMENT('Clone cannot share verification information')
|
|
482
483
|
}
|
|
483
484
|
|
|
484
|
-
const
|
|
485
|
+
const signature = opts.signature === undefined
|
|
486
|
+
? createVerifier(createManifest(manifest), { compat: isCompat(key, manifest) }).sign(this.core.tree.batch(), keyPair)
|
|
487
|
+
: opts.signature
|
|
485
488
|
|
|
486
489
|
const sparse = opts.sparse === false ? false : this.sparse
|
|
487
490
|
const wait = opts.wait === false ? false : this.wait
|
|
@@ -497,11 +500,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
497
500
|
wait,
|
|
498
501
|
onwait,
|
|
499
502
|
timeout,
|
|
500
|
-
|
|
503
|
+
manifest,
|
|
501
504
|
overwrite: true,
|
|
502
505
|
clone: {
|
|
503
506
|
from: this,
|
|
504
|
-
|
|
507
|
+
signature
|
|
505
508
|
}
|
|
506
509
|
})
|
|
507
510
|
}
|
|
@@ -542,6 +545,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
542
545
|
return this.replicator === null ? null : this.replicator.discoveryKey
|
|
543
546
|
}
|
|
544
547
|
|
|
548
|
+
get manifest () {
|
|
549
|
+
return this._manifest || (this.core === null ? null : this.core.header.manifest)
|
|
550
|
+
}
|
|
551
|
+
|
|
545
552
|
get length () {
|
|
546
553
|
if (this._snapshot) return this._snapshot.length
|
|
547
554
|
if (this.core === null) return 0
|
|
@@ -633,6 +640,13 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
633
640
|
this.replicator.onupgrade()
|
|
634
641
|
}
|
|
635
642
|
|
|
643
|
+
if (status & 0b10000) {
|
|
644
|
+
for (let i = 0; i < this.sessions.length; i++) {
|
|
645
|
+
const s = this.sessions[i]
|
|
646
|
+
if (!s._manifest) s.emit('manifest')
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
636
650
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
637
651
|
const s = this.sessions[i]
|
|
638
652
|
|
|
@@ -930,24 +944,36 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
930
944
|
// Do nothing for now
|
|
931
945
|
}
|
|
932
946
|
|
|
933
|
-
async truncate (newLength = 0,
|
|
934
|
-
if (this._batch) throw BATCH_UNFLUSHED()
|
|
935
|
-
|
|
947
|
+
async truncate (newLength = 0, opts = {}) {
|
|
936
948
|
if (this.opened === false) await this.opening
|
|
937
|
-
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
938
949
|
|
|
939
|
-
|
|
940
|
-
|
|
950
|
+
const {
|
|
951
|
+
fork = this.core.tree.fork + 1,
|
|
952
|
+
force = false,
|
|
953
|
+
keyPair = this.keyPair,
|
|
954
|
+
signature = null
|
|
955
|
+
} = typeof opts === 'number' ? { fork: opts } : opts
|
|
956
|
+
|
|
957
|
+
const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
|
|
958
|
+
|
|
959
|
+
if (writable === false) throw SESSION_NOT_WRITABLE()
|
|
960
|
+
if (this._batch && !force) throw BATCH_UNFLUSHED()
|
|
961
|
+
|
|
962
|
+
await this.core.truncate(newLength, fork, { keyPair, signature })
|
|
941
963
|
|
|
942
964
|
// TODO: Should propagate from an event triggered by the oplog
|
|
943
965
|
this.replicator.updateAll()
|
|
944
966
|
}
|
|
945
967
|
|
|
946
|
-
async append (blocks, opts) {
|
|
968
|
+
async append (blocks, opts = {}) {
|
|
947
969
|
if (this._batch && this !== this._batch.session) throw BATCH_UNFLUSHED()
|
|
948
970
|
|
|
949
971
|
if (this.opened === false) await this.opening
|
|
950
|
-
|
|
972
|
+
|
|
973
|
+
const { keyPair = this.keyPair, signature = null } = opts
|
|
974
|
+
const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
|
|
975
|
+
|
|
976
|
+
if (writable === false) throw SESSION_NOT_WRITABLE()
|
|
951
977
|
|
|
952
978
|
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
953
979
|
|
|
@@ -961,7 +987,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
961
987
|
}
|
|
962
988
|
}
|
|
963
989
|
|
|
964
|
-
return this.core.append(buffers,
|
|
990
|
+
return this.core.append(buffers, { keyPair, signature, preappend })
|
|
965
991
|
}
|
|
966
992
|
|
|
967
993
|
async treeHash (length) {
|
package/lib/batch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { BLOCK_NOT_AVAILABLE, SESSION_CLOSED } = require('hypercore-errors')
|
|
2
2
|
const EventEmitter = require('events')
|
|
3
3
|
const c = require('compact-encoding')
|
|
4
4
|
|
|
@@ -11,11 +11,12 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
11
11
|
this.closed = false
|
|
12
12
|
this.opening = null
|
|
13
13
|
this.closing = null
|
|
14
|
+
this.writable = true // always writable...
|
|
14
15
|
this.autoClose = autoClose
|
|
16
|
+
this.fork = 0
|
|
15
17
|
|
|
16
18
|
this._appends = []
|
|
17
19
|
this._byteLength = 0
|
|
18
|
-
this._fork = 0
|
|
19
20
|
this._sessionLength = 0
|
|
20
21
|
this._sessionByteLength = 0
|
|
21
22
|
this._flushing = null
|
|
@@ -51,16 +52,12 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
51
52
|
return this._sessionByteLength + this._byteLength
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
get writable () {
|
|
55
|
-
return this.session.writable
|
|
56
|
-
}
|
|
57
|
-
|
|
58
55
|
get core () {
|
|
59
56
|
return this.session.core
|
|
60
57
|
}
|
|
61
58
|
|
|
62
|
-
get
|
|
63
|
-
return this.session.
|
|
59
|
+
get manifest () {
|
|
60
|
+
return this.session.manifest
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
async ready () {
|
|
@@ -68,6 +65,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
68
65
|
if (this.opened) return
|
|
69
66
|
this._sessionLength = this.session.length
|
|
70
67
|
this._sessionByteLength = this.session.byteLength
|
|
68
|
+
this.fork = this.session.fork
|
|
71
69
|
this.opened = true
|
|
72
70
|
this.emit('ready')
|
|
73
71
|
}
|
|
@@ -169,19 +167,30 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
169
167
|
return b
|
|
170
168
|
}
|
|
171
169
|
|
|
172
|
-
async truncate (newLength) {
|
|
170
|
+
async truncate (newLength = 0, opts = {}) {
|
|
173
171
|
if (this.opened === false) await this.opening
|
|
174
|
-
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
175
172
|
if (this.closing) throw SESSION_CLOSED()
|
|
176
173
|
|
|
177
174
|
// wait for any pending flush... (prop needs a lock)
|
|
178
175
|
await this._waitForFlush()
|
|
179
176
|
|
|
177
|
+
if (typeof opts === 'number') opts = { fork: opts }
|
|
178
|
+
const { fork = this.fork + 1, force = false } = opts
|
|
179
|
+
|
|
180
180
|
const length = this._sessionLength
|
|
181
|
-
if (newLength < length)
|
|
181
|
+
if (newLength < length) {
|
|
182
|
+
if (!force) throw new Error('Cannot truncate committed blocks')
|
|
183
|
+
this._appends.length = 0
|
|
184
|
+
this._byteLength = 0
|
|
185
|
+
await this.session.truncate(newLength, { fork, force: true, ...opts })
|
|
186
|
+
this._sessionLength = this.session.length
|
|
187
|
+
this._sessionByteLength = this.session.byteLength
|
|
188
|
+
} else {
|
|
189
|
+
for (let i = newLength - length; i < this._appends.length; i++) this._byteLength -= this._appends[i].byteLength
|
|
190
|
+
this._appends.length = newLength - length
|
|
191
|
+
}
|
|
182
192
|
|
|
183
|
-
this.
|
|
184
|
-
this._fork++
|
|
193
|
+
this.fork = fork
|
|
185
194
|
|
|
186
195
|
this.emit('truncate', newLength, this.fork)
|
|
187
196
|
}
|
|
@@ -190,7 +199,6 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
190
199
|
const session = this.session
|
|
191
200
|
|
|
192
201
|
if (this.opened === false) await this.opening
|
|
193
|
-
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
194
202
|
if (this.closing) throw SESSION_CLOSED()
|
|
195
203
|
|
|
196
204
|
// wait for any pending flush... (prop needs a lock)
|
|
@@ -218,12 +226,14 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
218
226
|
return info
|
|
219
227
|
}
|
|
220
228
|
|
|
221
|
-
async flush (
|
|
229
|
+
async flush (opts = {}) {
|
|
222
230
|
if (this.opened === false) await this.opening
|
|
223
231
|
if (this.closing) throw SESSION_CLOSED()
|
|
224
232
|
|
|
233
|
+
const { length = this._appends.length, keyPair = this.session.keyPair, signature = null } = opts
|
|
234
|
+
|
|
225
235
|
while (this._flushing) await this._flushing
|
|
226
|
-
this._flushing = this._flush(length,
|
|
236
|
+
this._flushing = this._flush(length, keyPair, signature)
|
|
227
237
|
|
|
228
238
|
try {
|
|
229
239
|
await this._flushing
|
|
@@ -234,11 +244,12 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
234
244
|
if (this.autoClose) await this.close()
|
|
235
245
|
}
|
|
236
246
|
|
|
237
|
-
async _flush (length,
|
|
247
|
+
async _flush (length, keyPair, signature) { // TODO: make this safe to interact with a parallel truncate...
|
|
238
248
|
if (this._appends.length === 0) return
|
|
239
249
|
|
|
240
250
|
const flushingLength = Math.min(length, this._appends.length)
|
|
241
|
-
const
|
|
251
|
+
const blocks = flushingLength < this._appends.length ? this._appends.slice(0, flushingLength) : this._appends
|
|
252
|
+
const info = await this.session.append(blocks, { keyPair, signature })
|
|
242
253
|
const delta = info.byteLength - this._sessionByteLength
|
|
243
254
|
|
|
244
255
|
this._sessionLength = info.length
|
|
@@ -267,7 +278,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
267
278
|
_clearAppends () {
|
|
268
279
|
this._appends = []
|
|
269
280
|
this._byteLength = 0
|
|
270
|
-
this.
|
|
281
|
+
this.fork = 0
|
|
271
282
|
}
|
|
272
283
|
|
|
273
284
|
_clearBatch () {
|
package/lib/caps.js
CHANGED
|
@@ -8,6 +8,7 @@ const c = require('compact-encoding')
|
|
|
8
8
|
|
|
9
9
|
const [TREE, REPLICATE_INITIATOR, REPLICATE_RESPONDER, MANIFEST, DEFAULT_NAMESPACE] = crypto.namespace('hypercore', 5)
|
|
10
10
|
|
|
11
|
+
exports.MANIFEST = MANIFEST
|
|
11
12
|
exports.DEFAULT_NAMESPACE = DEFAULT_NAMESPACE
|
|
12
13
|
|
|
13
14
|
exports.replicate = function (isInitiator, key, handshakeHash) {
|
|
@@ -16,27 +17,20 @@ exports.replicate = function (isInitiator, key, handshakeHash) {
|
|
|
16
17
|
return out
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
exports.
|
|
20
|
-
const state = { start: 0, end:
|
|
21
|
-
state.buffer = b4a.allocUnsafe(state.end)
|
|
22
|
-
c.raw.encode(state, MANIFEST)
|
|
23
|
-
c.raw.encode(state, manifest)
|
|
24
|
-
const out = b4a.allocUnsafe(32)
|
|
25
|
-
sodium.crypto_generichash(out, state.buffer)
|
|
26
|
-
return out
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
exports.treeSignable = function (hash, length, fork) {
|
|
30
|
-
const state = { start: 0, end: 80, buffer: b4a.allocUnsafe(80) }
|
|
20
|
+
exports.treeSignable = function (namespace, hash, length, fork) {
|
|
21
|
+
const state = { start: 0, end: 112, buffer: b4a.allocUnsafe(112) }
|
|
31
22
|
c.raw.encode(state, TREE)
|
|
23
|
+
c.raw.encode(state, namespace)
|
|
32
24
|
c.raw.encode(state, hash)
|
|
33
25
|
c.uint64.encode(state, length)
|
|
34
26
|
c.uint64.encode(state, fork)
|
|
35
27
|
return state.buffer
|
|
36
28
|
}
|
|
37
29
|
|
|
38
|
-
exports.
|
|
39
|
-
const
|
|
30
|
+
exports.treeSignableCompat = function (hash, length, fork, noHeader) {
|
|
31
|
+
const end = noHeader ? 48 : 80
|
|
32
|
+
const state = { start: 0, end, buffer: b4a.allocUnsafe(end) }
|
|
33
|
+
if (!noHeader) c.raw.encode(state, TREE) // ultra legacy mode, kill in future major
|
|
40
34
|
c.raw.encode(state, hash)
|
|
41
35
|
c.uint64.encode(state, length)
|
|
42
36
|
c.uint64.encode(state, fork)
|