@@ -3,17 +3,16 @@ import Corestore from 'corestore'
3
3
import { debounce } from 'throttle-debounce'
4
4
import assert from 'node:assert/strict'
5
5
import { sql , eq } from 'drizzle-orm'
6
- import { discoveryKey } from 'hypercore-crypto'
7
- import Hypercore from 'hypercore'
8
6
9
7
import { HaveExtension , ProjectExtension } from '../generated/extensions.js'
10
8
import { Logger } from '../logger.js'
11
9
import { NAMESPACES } from '../constants.js'
12
- import { keyToId , noop } from '../utils.js'
10
+ import { noop } from '../utils.js'
13
11
import { coresTable } from '../schema/project.js'
14
12
import * as rle from './bitfield-rle.js'
15
13
import { CoreIndex } from './core-index.js'
16
14
15
+ /** @import Hypercore from 'hypercore' */
17
16
/** @import { HypercorePeer, Namespace } from '../types.js' */
18
17
19
18
const WRITER_CORE_PREHAVES_DEBOUNCE_DELAY = 1000
@@ -46,12 +45,6 @@ export class CoreManager extends TypedEmitter {
46
45
#haveExtension
47
46
#deviceId
48
47
#l
49
- /**
50
- * We use this to reduce network traffic caused by requesting the same key
51
- * from multiple clients.
52
- * TODO: Remove items from this set after a max age
53
- */
54
- #keyRequests = new TrackedKeyRequests ( )
55
48
#autoDownload
56
49
57
50
static get namespaces ( ) {
@@ -152,8 +145,8 @@ export class CoreManager extends TypedEmitter {
152
145
'mapeo/project' ,
153
146
{
154
147
encoding : ProjectExtensionCodec ,
155
- onmessage : ( msg , peer ) => {
156
- this . #handleProjectMessage( msg , peer )
148
+ onmessage : ( msg ) => {
149
+ this . #handleProjectMessage( msg )
157
150
} ,
158
151
}
159
152
)
@@ -169,12 +162,7 @@ export class CoreManager extends TypedEmitter {
169
162
this . #sendHaves( peer , this . #coreIndex) . catch ( ( ) => {
170
163
this . #l. log ( 'Failed to send pre-haves to newly-connected peer' )
171
164
} )
172
- } )
173
- this . #creatorCore. on ( 'peer-remove' , ( peer ) => {
174
- // When a peer is removed we clean up any unanswered key requests, so that
175
- // we will request from a different peer, and to avoid the tracking of key
176
- // requests growing without bounds.
177
- this . #keyRequests. deleteByPeerKey ( peer . remotePublicKey )
165
+ this . #sendAuthCoreKeys( peer )
178
166
} )
179
167
180
168
this . #ready = Promise . all (
@@ -251,7 +239,6 @@ export class CoreManager extends TypedEmitter {
251
239
*/
252
240
async close ( ) {
253
241
this . #state = 'closing'
254
- this . #keyRequests. clear ( )
255
242
const promises = [ ]
256
243
for ( const { core } of this . #coreIndex) {
257
244
promises . push ( core . close ( ) )
@@ -268,6 +255,7 @@ export class CoreManager extends TypedEmitter {
268
255
* @returns {CoreRecord }
269
256
*/
270
257
addCore ( key , namespace ) {
258
+ this . #l. log ( 'Adding remote core %k to %s' , key , namespace )
271
259
return this . #addCore( { publicKey : key } , namespace , true )
272
260
}
273
261
@@ -359,69 +347,31 @@ export class CoreManager extends TypedEmitter {
359
347
}
360
348
361
349
/**
362
- * Send an extension message over the project creator core replication stream
363
- * requesting a core key for the given discovery key.
350
+ * We only add auth cores from the project extension messages. Cores in other
351
+ * namespaces are added by the sync API from the core ownership docs
364
352
*
365
- * @param {Buffer } peerKey
366
- * @param {Buffer } discoveryKey
353
+ * @param {ProjectExtension } msg
367
354
*/
368
- requestCoreKey ( peerKey , discoveryKey ) {
369
- // No-op if we already have this core
370
- if ( this . getCoreByDiscoveryKey ( discoveryKey ) ) return
371
- const peer = this . #creatorCore. peers . find ( ( peer ) => {
372
- return peer . remotePublicKey . equals ( peerKey )
373
- } )
374
- if ( ! peer ) {
375
- // This should not happen because this is only called from SyncApi, which
376
- // checks the peer exists before calling this method.
377
- this . #l. log (
378
- 'Attempted to request core key for %h, but no connected peer %h' ,
379
- discoveryKey ,
380
- peerKey
381
- )
382
- return
355
+ #handleProjectMessage( { authCoreKeys } ) {
356
+ for ( const authCoreKey of authCoreKeys ) {
357
+ // Use public method - these must be persisted (private method defaults to persisted=false)
358
+ this . addCore ( authCoreKey , 'auth' )
383
359
}
384
- // Only request a key once, e.g. from the peer we first receive it from (we
385
- // can assume that a peer must have the key if we see the discovery key in
386
- // the protomux). This is necessary to reduce network traffic for many newly
387
- // connected peers - otherwise duplicate requests will be sent to every peer
388
- if ( this . #keyRequests. has ( discoveryKey ) ) return
389
- this . #keyRequests. set ( discoveryKey , peerKey )
390
-
391
- this . #l. log (
392
- 'Requesting core key for discovery key %h from peer %h' ,
393
- discoveryKey ,
394
- peerKey
395
- )
396
- const message = ProjectExtension . fromPartial ( {
397
- wantCoreKeys : [ discoveryKey ] ,
398
- } )
399
- this . #projectExtension. send ( message , peer )
400
360
}
401
361
402
362
/**
403
- * @param {ProjectExtension } msg
363
+ * Sends auth core keys to the given peer, skipping any keys that we know the
364
+ * peer has already (depends on the peer having already replicated the auth
365
+ * cores it has)
366
+ *
404
367
* @param {HypercorePeer } peer
405
368
*/
406
- #handleProjectMessage ( { wantCoreKeys , ... coreKeys } , peer ) {
369
+ #sendAuthCoreKeys ( peer ) {
407
370
const message = ProjectExtension . create ( )
408
- let hasKeys = false
409
- for ( const discoveryKey of wantCoreKeys ) {
410
- const coreRecord = this . getCoreByDiscoveryKey ( discoveryKey )
411
- if ( ! coreRecord ) continue
412
- message [ `${ coreRecord . namespace } CoreKeys` ] . push ( coreRecord . key )
413
- hasKeys = true
414
- }
415
- if ( hasKeys ) {
416
- this . #projectExtension. send ( message , peer )
417
- }
418
- for ( const namespace of NAMESPACES ) {
419
- for ( const coreKey of coreKeys [ `${ namespace } CoreKeys` ] ) {
420
- // Use public method - these must be persisted (private method defaults to persisted=false)
421
- this . addCore ( coreKey , namespace )
422
- this . #keyRequests. deleteByDiscoveryKey ( discoveryKey ( coreKey ) )
423
- }
371
+ for ( const { key } of this . getCores ( 'auth' ) ) {
372
+ message . authCoreKeys . push ( key )
424
373
}
374
+ this . #projectExtension. send ( message , peer )
425
375
}
426
376
427
377
/**
@@ -478,21 +428,14 @@ export class CoreManager extends TypedEmitter {
478
428
* ONLY FOR TESTING
479
429
* Replicate all cores in core manager
480
430
*
431
+ * NB: Remote peers need to be manually added when unit testing core manager
432
+ * without the Sync API
433
+ *
481
434
* @param {Parameters<Corestore['replicate']>[0] } stream
482
435
*/
483
436
[ kCoreManagerReplicate ] ( stream ) {
484
- const protocolStream = Hypercore . createProtocolStream ( stream , {
485
- ondiscoverykey : async ( discoveryKey ) => {
486
- const peer = await findPeer (
487
- this . creatorCore ,
488
- // @ts -ignore
489
- protocolStream . noiseStream . remotePublicKey
490
- )
491
- if ( ! peer ) return
492
- this . requestCoreKey ( peer . remotePublicKey , discoveryKey )
493
- } ,
494
- } )
495
- return this . #corestore. replicate ( stream )
437
+ const protocolStream = this . #corestore. replicate ( stream )
438
+ return protocolStream
496
439
}
497
440
498
441
/**
@@ -559,93 +502,3 @@ const HaveExtensionCodec = {
559
502
}
560
503
} ,
561
504
}
562
-
563
- class TrackedKeyRequests {
564
- /** @type {Map<string, string> } */
565
- #byDiscoveryId = new Map ( )
566
- /** @type {Map<string, Set<string>> } */
567
- #byPeerId = new Map ( )
568
-
569
- /**
570
- * @param {Buffer } discoveryKey
571
- * @param {Buffer } peerKey
572
- */
573
- set ( discoveryKey , peerKey ) {
574
- const discoveryId = keyToId ( discoveryKey )
575
- const peerId = keyToId ( peerKey )
576
- const existingForPeer = this . #byPeerId. get ( peerId ) || new Set ( )
577
- this . #byDiscoveryId. set ( discoveryId , peerId )
578
- existingForPeer . add ( discoveryId )
579
- this . #byPeerId. set ( peerId , existingForPeer )
580
- return this
581
- }
582
- /**
583
- * @param {Buffer } discoveryKey
584
- */
585
- has ( discoveryKey ) {
586
- const discoveryId = keyToId ( discoveryKey )
587
- return this . #byDiscoveryId. has ( discoveryId )
588
- }
589
- /**
590
- * @param {Buffer } discoveryKey
591
- */
592
- deleteByDiscoveryKey ( discoveryKey ) {
593
- const discoveryId = keyToId ( discoveryKey )
594
- const peerId = this . #byDiscoveryId. get ( discoveryId )
595
- if ( ! peerId ) return false
596
- this . #byDiscoveryId. delete ( discoveryId )
597
- const existingForPeer = this . #byPeerId. get ( peerId )
598
- if ( existingForPeer ) {
599
- existingForPeer . delete ( discoveryId )
600
- }
601
- return true
602
- }
603
- /**
604
- * @param {Buffer } peerKey
605
- */
606
- deleteByPeerKey ( peerKey ) {
607
- const peerId = keyToId ( peerKey )
608
- const existingForPeer = this . #byPeerId. get ( peerId )
609
- if ( ! existingForPeer ) return
610
- for ( const discoveryId of existingForPeer ) {
611
- this . #byDiscoveryId. delete ( discoveryId )
612
- }
613
- this . #byPeerId. delete ( peerId )
614
- }
615
- clear ( ) {
616
- this . #byDiscoveryId. clear ( )
617
- this . #byPeerId. clear ( )
618
- }
619
- }
620
-
621
- /**
622
- * @param {Hypercore<"binary", Buffer> } core
623
- * @param {Buffer } publicKey
624
- * @param {{ timeout?: number } } [opts]
625
- */
626
- function findPeer ( core , publicKey , { timeout = 200 } = { } ) {
627
- const peer = core . peers . find ( ( peer ) => {
628
- return peer . remotePublicKey . equals ( publicKey )
629
- } )
630
- if ( peer ) return peer
631
- // This is called from the from the handleDiscoveryId event, which can
632
- // happen before the peer connection is fully established, so we wait for
633
- // the `peer-add` event, with a timeout in case the peer never gets added
634
- return new Promise ( function ( res ) {
635
- const timeoutId = setTimeout ( function ( ) {
636
- core . off ( 'peer-add' , onPeer )
637
- res ( null )
638
- } , timeout )
639
-
640
- core . on ( 'peer-add' , onPeer )
641
-
642
- /** @param {HypercorePeer } peer */
643
- function onPeer ( peer ) {
644
- if ( peer . remotePublicKey . equals ( publicKey ) ) {
645
- clearTimeout ( timeoutId )
646
- core . off ( 'peer-add' , onPeer )
647
- res ( peer )
648
- }
649
- }
650
- } )
651
- }
0 commit comments