js-libp2p/test/interop.ts
Alex Potsides 199395de4d
feat: convert to typescript (#1172)
Converts this module to typescript.

- Ecosystem modules renamed from (e.g.) `libp2p-tcp` to `@libp2p/tcp`
- Ecosystem module now have named exports
- Configuration has been updated, now pass instances of modules instead of classes:
- Some configuration keys have been renamed to make them more descriptive.  `transport` -> `transports`, `connEncryption` -> `connectionEncryption`.  In general where we pass multiple things, the key is now plural, e.g. `streamMuxer` -> `streamMuxers`, `contentRouting` -> `contentRouters`, etc.  Where we are configuring a singleton the config key is singular, e.g. `connProtector` -> `connectionProtector` etc.
- Properties of the `modules` config key have been moved to the root
- Properties of the `config` config key have been moved to the root
```js
// before
import Libp2p from 'libp2p'
import TCP from 'libp2p-tcp'

await Libp2p.create({
  modules: {
    transport: [
      TCP
    ],
  }
  config: {
    transport: {
      [TCP.tag]: {
        foo: 'bar'
      }
    },
    relay: {
      enabled: true,
      hop: {
        enabled: true,
        active: true
      }
    }
  }
})
```
```js
// after
import { createLibp2p } from 'libp2p'
import { TCP } from '@libp2p/tcp'

await createLibp2p({
  transports: [
    new TCP({ foo: 'bar' })
  ],
  relay: {
    enabled: true,
    hop: {
      enabled: true,
      active: true
    }
  }
})
```
- Use of `enabled` flag has been reduced - previously you could pass a module but disable it with config.  Now if you don't want a feature, just don't pass an implementation.   Eg:
```js
// before
await Libp2p.create({
  modules: {
    transport: [
      TCP
    ],
    pubsub: Gossipsub
  },
  config: {
    pubsub: {
      enabled: false
    }
  }
})
```
```js
// after
await createLibp2p({
  transports: [
    new TCP()
  ]
})
```
- `.multiaddrs` renamed to `.getMultiaddrs()` because it's not a property accessor, work is done by that method to calculate announce addresses, observed addresses, etc
- `/p2p/${peerId}` is now appended to all addresses returned by `.getMultiaddrs()` so they can be used opaquely (every consumer has to append the peer ID to the address to actually use it otherwise).  If you need low-level unadulterated addresses, call methods on the address manager.

BREAKING CHANGE: types are no longer hand crafted, this module is now ESM only
2022-03-28 14:30:27 +01:00

160 lines
4.1 KiB
TypeScript

import { interopTests } from '@libp2p/interop'
import type { SpawnOptions, Daemon, DaemonFactory } from '@libp2p/interop'
import { createServer } from '@libp2p/daemon-server'
import { createClient } from '@libp2p/daemon-client'
import { createLibp2p, Libp2pOptions } from '../src/index.js'
import { Noise } from '@chainsafe/libp2p-noise'
import { TCP } from '@libp2p/tcp'
import { Multiaddr } from '@multiformats/multiaddr'
import { KadDHT } from '@libp2p/kad-dht'
import { path as p2pd } from 'go-libp2p'
import execa from 'execa'
import pDefer from 'p-defer'
import { logger } from '@libp2p/logger'
import { Mplex } from '@libp2p/mplex'
import fs from 'fs'
import { unmarshalPrivateKey } from '@libp2p/crypto/keys'
import type { PeerId } from '@libp2p/interfaces/peer-id'
import { peerIdFromKeys } from '@libp2p/peer-id'
import { FloodSub } from '@libp2p/floodsub'
import { Gossipsub } from '@achingbrain/libp2p-gossipsub'
// IPFS_LOGGING=debug DEBUG=libp2p*,go-libp2p:* npm run test:interop
async function createGoPeer (options: SpawnOptions): Promise<Daemon> {
const controlPort = Math.floor(Math.random() * (50000 - 10000 + 1)) + 10000
const apiAddr = new Multiaddr(`/ip4/0.0.0.0/tcp/${controlPort}`)
const log = logger(`go-libp2p:${controlPort}`)
const opts = [
`-listen=${apiAddr.toString()}`,
'-hostAddrs=/ip4/0.0.0.0/tcp/0'
]
if (options.noise === true) {
opts.push('-noise=true')
}
if (options.dht === true) {
opts.push('-dhtServer')
}
if (options.pubsub === true) {
opts.push('-pubsub')
}
if (options.pubsubRouter != null) {
opts.push(`-pubsubRouter=${options.pubsubRouter}`)
}
if (options.key != null) {
opts.push(`-id=${options.key}`)
}
const deferred = pDefer()
const proc = execa(p2pd(), opts)
proc.stdout?.on('data', (buf: Buffer) => {
const str = buf.toString()
log(str)
// daemon has started
if (str.includes('Control socket:')) {
deferred.resolve()
}
})
proc.stderr?.on('data', (buf) => {
log.error(buf.toString())
})
await deferred.promise
return {
client: createClient(apiAddr),
stop: async () => {
await proc.kill()
}
}
}
async function createJsPeer (options: SpawnOptions): Promise<Daemon> {
let peerId: PeerId | undefined
if (options.key != null) {
const keyFile = fs.readFileSync(options.key)
const privateKey = await unmarshalPrivateKey(keyFile)
peerId = await peerIdFromKeys(privateKey.public.bytes, privateKey.bytes)
}
const opts: Libp2pOptions = {
peerId,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
transports: [new TCP()],
streamMuxers: [new Mplex()],
connectionEncryption: [new Noise()]
}
if (options.dht === true) {
// go-libp2p-daemon only has the older single-table DHT instead of the dual
// lan/wan version found in recent go-ipfs versions. unfortunately it's been
// abandoned so here we simulate the older config with the js implementation
const dht = new KadDHT({
clientMode: false
})
const lan = dht.lan
const protocol = '/ipfs/kad/1.0.0'
lan.protocol = protocol
// @ts-expect-error
lan.network.protocol = protocol
// @ts-expect-error
lan.topologyListener.protocol = protocol
// @ts-expect-error
opts.dht = lan
}
if (options.pubsub === true) {
if (options.pubsubRouter === 'floodsub') {
opts.pubsub = new FloodSub()
} else {
opts.pubsub = new Gossipsub()
}
}
const node = await createLibp2p(opts)
const server = await createServer(new Multiaddr('/ip4/0.0.0.0/tcp/0'), node)
await server.start()
return {
client: createClient(server.getMultiaddr()),
stop: async () => {
await server.stop()
await node.stop()
}
}
}
async function main () {
const factory: DaemonFactory = {
async spawn (options: SpawnOptions) {
if (options.type === 'go') {
return await createGoPeer(options)
}
return await createJsPeer(options)
}
}
await interopTests(factory)
}
main().catch(err => {
console.error(err) // eslint-disable-line no-console
process.exit(1)
})