holepunchto / hypercore

Hypercore is a secure, distributed append-only log.
https://docs.holepunch.to
MIT License
2.59k stars 182 forks source link

Add unreplicate() method #356

Open gmaclennan opened 1 year ago

gmaclennan commented 1 year ago

This existed a while ago in https://github.com/holepunchto/hypercore/pull/49

It would be useful to be able to "unreplicate" a core from a hypercore protocol stream, without destroying the underlying stream. I guess closing the protomux channel would do this, and it would remove the peer. However I'm not sure if there's a way to access the protomux channel from the stream returned from core.replicate()?

gmaclennan commented 1 year ago

This is how we are currently doing "unreplicate":

export function unreplicate(core, protomux) {
  const peerToUnreplicate = core.peers.find(
    (peer) => peer.protomux === protomux
  )
  if (!peerToUnreplicate) return
  peerToUnreplicate.channel.close()
  return
}

The protomux instance can be access from the raw stream returned by const protocolStream = core.replicate(), via const protomux = protocolStream.noiseStream.userData.

This seems to work ok.

gmaclennan commented 2 months ago

Update: the function above was not working for two reasons:

  1. Needed to unpair the protomux
  2. If one peer unreplicated before the other, the other peer would remain "replicated"

Fixed unreplicate function:

/**
 * @param {import('hypercore')<'binary', any>} core Core to unreplicate. Must be ready.
 * @param {import('protomux')} protomux
 */
export function unreplicate(core, protomux) {
  assert(core.discoveryKey, 'Core should have a discovery key')
  protomux.unpair({
    protocol: 'hypercore/alpha',
    id: core.discoveryKey,
  })
  for (const channel of protomux) {
    if (channel.protocol !== 'hypercore/alpha') continue
    if (!channel.id.equals(core.discoveryKey)) continue
    channel.close()
  }
}

For our use-case, we would not need an unreplicate method if #556 was implemented.