chore: add typedefs (#802)

This commit is contained in:
Vasco Santos 2020-12-10 14:48:14 +01:00
parent 117f2b97b0
commit 64124f17ed
59 changed files with 1258 additions and 768 deletions

67
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,67 @@
name: ci
on:
push:
branches:
- master
pull_request:
branches:
- '**'
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: yarn lint
- uses: gozala/typescript-error-reporter-action@v1.0.8
- run: yarn build
- run: yarn aegir dep-check
- uses: ipfs/aegir/actions/bundle-size@master
name: size
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
test-node:
needs: check
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
node: [12, 14]
fail-fast: true
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- run: yarn
- run: npx nyc --reporter=lcov aegir test -t node -- --bail
- uses: codecov/codecov-action@v1
test-chrome:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: npx aegir test -t browser -t webworker --bail
test-firefox:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: npx aegir test -t browser -t webworker --bail -- --browsers FirefoxHeadless
test-interop:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd node_modules/interop-libp2p && yarn && LIBP2P_JS=${GITHUB_WORKSPACE}/src/index.js npx aegir test -t node --bail
test-auto-relay-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- auto-relay

View File

@ -1,58 +0,0 @@
language: node_js
cache: npm
stages:
- check
- test
- cov
node_js:
- 'lts/*'
- '14'
os:
- linux
- osx
script: npx nyc -s npm run test:node -- --bail
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
jobs:
include:
- stage: check
script:
- npx aegir build --bundlesize
# Remove pull libs once ping is async
- npx aegir dep-check -- -i pull-handshake -i pull-stream
- npm run lint
- stage: test
name: chrome
addons:
chrome: stable
script:
- npx aegir test -t browser -t webworker
- stage: test
name: firefox
addons:
firefox: latest
script:
- npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless
- stage: test
name: interop
script:
- cd node_modules/interop-libp2p
- npm install
- LIBP2P_JS=${TRAVIS_BUILD_DIR}/src/index.js npx aegir test -t node --bail
- stage: test
if: type = pull_request
name: example - auto-relay
script:
- cd examples
- npm install
- npm run test -- auto-relay
notifications:
email: false

View File

@ -4,6 +4,15 @@
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js",
"types": "dist/src/index.d.ts",
"typesVersions": {
"*": {
"src/*": [
"dist/src/*",
"dist/src/*/index"
]
}
},
"files": [
"dist",
"src"
@ -53,7 +62,7 @@
"events": "^3.1.0",
"hashlru": "^2.3.0",
"interface-datastore": "^2.0.0",
"ipfs-utils": "^2.2.0",
"ipfs-utils": "^5.0.1",
"it-all": "^1.0.1",
"it-buffer": "^0.1.2",
"it-handshake": "^1.0.1",
@ -61,7 +70,7 @@
"it-pipe": "^1.1.0",
"it-protocol-buffers": "^0.2.0",
"libp2p-crypto": "^0.18.0",
"libp2p-interfaces": "^0.7.2",
"libp2p-interfaces": "^0.8.0",
"libp2p-utils": "^0.2.2",
"mafmt": "^8.0.0",
"merge-options": "^2.0.0",
@ -88,7 +97,7 @@
"devDependencies": {
"@nodeutils/defaults-deep": "^1.1.0",
"abortable-iterator": "^3.0.0",
"aegir": "^27.0.0",
"aegir": "^29.2.0",
"chai-bytes": "^0.1.2",
"chai-string": "^1.5.0",
"delay": "^4.3.0",

View File

@ -1,23 +1,25 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:addresses')
log.error = debug('libp2p:addresses:error')
const multiaddr = require('multiaddr')
/**
* Responsible for managing the peer addresses.
* Peers can specify their listen and announce addresses.
* The listen addresses will be used by the libp2p transports to listen for new connections,
* while the announce addresses will be used for the peer addresses' to other peers in the network.
* @typedef {import('multiaddr')} Multiaddr
*/
/**
* @typedef {Object} AddressManagerOptions
* @property {string[]} [listen = []] - list of multiaddrs string representation to listen.
* @property {string[]} [announce = []] - list of multiaddrs string representation to announce.
*/
class AddressManager {
/**
* Responsible for managing the peer addresses.
* Peers can specify their listen and announce addresses.
* The listen addresses will be used by the libp2p transports to listen for new connections,
* while the announce addresses will be used for the peer addresses' to other peers in the network.
*
* @class
* @param {object} [options]
* @param {Array<string>} [options.listen = []] - list of multiaddrs string representation to listen.
* @param {Array<string>} [options.announce = []] - list of multiaddrs string representation to announce.
* @param {AddressManagerOptions} [options]
*/
constructor ({ listen = [], announce = [] } = {}) {
this.listen = new Set(listen)
@ -27,7 +29,7 @@ class AddressManager {
/**
* Get peer listen multiaddrs.
*
* @returns {Array<Multiaddr>}
* @returns {Multiaddr[]}
*/
getListenAddrs () {
return Array.from(this.listen).map((a) => multiaddr(a))
@ -36,7 +38,7 @@ class AddressManager {
/**
* Get peer announcing multiaddrs.
*
* @returns {Array<Multiaddr>}
* @returns {Multiaddr[]}
*/
getAnnounceAddrs () {
return Array.from(this.announce).map((a) => multiaddr(a))

View File

@ -1,8 +1,9 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:auto-relay')
log.error = debug('libp2p:auto-relay:error')
const log = Object.assign(debug('libp2p:auto-relay'), {
error: debug('libp2p:auto-relay:err')
})
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayToString = require('uint8arrays/to-string')
@ -19,14 +20,25 @@ const {
RELAY_RENDEZVOUS_NS
} = require('./constants')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('../peer-store/address-book').Address} Address
*/
/**
* @typedef {Object} AutoRelayProperties
* @property {import('../')} libp2p
*
* @typedef {Object} AutoRelayOptions
* @property {number} [maxListeners = 1] - maximum number of relays to listen.
*/
class AutoRelay {
/**
* Creates an instance of AutoRelay.
*
* @class
* @param {object} props
* @param {Libp2p} props.libp2p
* @param {number} [props.maxListeners = 1] - maximum number of relays to listen.
* @param {AutoRelayProperties & AutoRelayOptions} props
*/
constructor ({ libp2p, maxListeners = 1 }) {
this._libp2p = libp2p
@ -58,7 +70,7 @@ class AutoRelay {
*
* @param {Object} props
* @param {PeerId} props.peerId
* @param {Array<string>} props.protocols
* @param {string[]} props.protocols
* @returns {Promise<void>}
*/
async _onProtocolChange ({ peerId, protocols }) {
@ -78,6 +90,9 @@ class AutoRelay {
// If protocol, check if can hop, store info in the metadataBook and listen on it
try {
const connection = this._connectionManager.get(peerId)
if (!connection) {
return
}
// Do not hop on a relayed connection
if (connection.remoteAddr.protoCodes().includes(CIRCUIT_PROTO_CODE)) {
@ -171,7 +186,7 @@ class AutoRelay {
* 2. Dial and try to listen on the peers we know that support hop but are not connected.
* 3. Search the network.
*
* @param {Array<string>} [peersToIgnore]
* @param {string[]} [peersToIgnore]
* @returns {Promise<void>}
*/
async _listenOnAvailableHopRelays (peersToIgnore = []) {

View File

@ -1,22 +1,42 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:circuit:hop')
log.error = debug('libp2p:circuit:hop:error')
const log = Object.assign(debug('libp2p:circuit:hop'), {
error: debug('libp2p:circuit:hop:err')
})
const errCode = require('err-code')
const PeerId = require('peer-id')
const { validateAddrs } = require('./utils')
const StreamHandler = require('./stream-handler')
const { CircuitRelay: CircuitPB } = require('../protocol')
const pipe = require('it-pipe')
const errCode = require('err-code')
const { pipe } = require('it-pipe')
const { codes: Errors } = require('../../errors')
const { stop } = require('./stop')
const multicodec = require('./../multicodec')
module.exports.handleHop = async function handleHop ({
/**
* @typedef {import('../../types').CircuitRequest} CircuitRequest
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('./stream-handler')<CircuitRequest>} StreamHandlerT
* @typedef {import('../transport')} Transport
*/
/**
* @typedef {Object} HopRequest
* @property {Connection} connection
* @property {CircuitRequest} request
* @property {StreamHandlerT} streamHandler
* @property {Transport} circuit
*/
/**
* @param {HopRequest} options
* @returns {Promise<void>}
*/
async function handleHop ({
connection,
request,
streamHandler,
@ -51,6 +71,9 @@ module.exports.handleHop = async function handleHop ({
}
// TODO: Handle being an active relay
if (!destinationConnection) {
return
}
// Handle the incoming HOP request by performing a STOP request
const stopRequest = {
@ -63,8 +86,7 @@ module.exports.handleHop = async function handleHop ({
try {
destinationStream = await stop({
connection: destinationConnection,
request: stopRequest,
circuit
request: stopRequest
})
} catch (err) {
return log.error(err)
@ -91,10 +113,10 @@ module.exports.handleHop = async function handleHop ({
*
* @param {object} options
* @param {Connection} options.connection - Connection to the relay
* @param {*} options.request
* @param {CircuitRequest} options.request
* @returns {Promise<Connection>}
*/
module.exports.hop = async function hop ({
async function hop ({
connection,
request
}) {
@ -123,7 +145,7 @@ module.exports.hop = async function hop ({
* @param {Connection} options.connection - Connection to the relay
* @returns {Promise<boolean>}
*/
module.exports.canHop = async function canHop ({
async function canHop ({
connection
}) {
// Create a new stream to the relay
@ -149,11 +171,11 @@ module.exports.canHop = async function canHop ({
*
* @param {Object} options
* @param {Connection} options.connection
* @param {StreamHandler} options.streamHandler
* @param {Circuit} options.circuit
* @param {StreamHandlerT} options.streamHandler
* @param {Transport} options.circuit
* @private
*/
module.exports.handleCanHop = function handleCanHop ({
function handleCanHop ({
connection,
streamHandler,
circuit
@ -165,3 +187,10 @@ module.exports.handleCanHop = function handleCanHop ({
code: canHop ? CircuitPB.Status.SUCCESS : CircuitPB.Status.HOP_CANT_SPEAK_RELAY
})
}
module.exports = {
handleHop,
hop,
canHop,
handleCanHop
}

View File

@ -1,23 +1,31 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:circuit:stop'), {
error: debug('libp2p:circuit:stop:err')
})
const { CircuitRelay: CircuitPB } = require('../protocol')
const multicodec = require('../multicodec')
const StreamHandler = require('./stream-handler')
const { validateAddrs } = require('./utils')
const debug = require('debug')
const log = debug('libp2p:circuit:stop')
log.error = debug('libp2p:circuit:stop:error')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
* @typedef {import('../../types').CircuitRequest} CircuitRequest
* @typedef {import('./stream-handler')<CircuitRequest>} StreamHandlerT
*/
/**
* Handles incoming STOP requests
*
* @private
* @param {*} options
* @param {Object} options
* @param {Connection} options.connection
* @param {*} options.request - The CircuitRelay protobuf request (unencoded)
* @param {StreamHandler} options.streamHandler
* @returns {Promise<*>} Resolves a duplex iterable
* @param {CircuitRequest} options.request - The CircuitRelay protobuf request (unencoded)
* @param {StreamHandlerT} options.streamHandler
* @returns {Promise<MuxedStream>|void} Resolves a duplex iterable
*/
module.exports.handleStop = function handleStop ({
connection,
@ -44,10 +52,10 @@ module.exports.handleStop = function handleStop ({
* Creates a STOP request
*
* @private
* @param {*} options
* @param {Object} options
* @param {Connection} options.connection
* @param {*} options.request - The CircuitRelay protobuf request (unencoded)
* @returns {Promise<*>} Resolves a duplex iterable
* @param {CircuitRequest} options.request - The CircuitRelay protobuf request (unencoded)
* @returns {Promise<MuxedStream|void>} Resolves a duplex iterable
*/
module.exports.stop = async function stop ({
connection,

View File

@ -1,20 +1,29 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:circuit:stream-handler'), {
error: debug('libp2p:circuit:stream-handler:err')
})
const lp = require('it-length-prefixed')
const handshake = require('it-handshake')
const { CircuitRelay: CircuitPB } = require('../protocol')
const debug = require('debug')
const log = debug('libp2p:circuit:stream-handler')
log.error = debug('libp2p:circuit:stream-handler:error')
/**
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
*/
/**
* @template T
*/
class StreamHandler {
/**
* Create a stream handler for connection
*
* @class
* @param {object} options
* @param {*} options.stream - A duplex iterable
* @param {number} options.maxLength - max bytes length of message
* @param {MuxedStream} options.stream - A duplex iterable
* @param {number} [options.maxLength = 4096] - max bytes length of message
*/
constructor ({ stream, maxLength = 4096 }) {
this.stream = stream
@ -27,7 +36,7 @@ class StreamHandler {
* Read and decode message
*
* @async
* @returns {void}
* @returns {Promise<T|undefined>}
*/
async read () {
const msg = await this.decoder.next()
@ -45,10 +54,12 @@ class StreamHandler {
/**
* Encode and write array of buffers
*
* @param {*} msg - An unencoded CircuitRelay protobuf message
* @param {CircuitPB} msg - An unencoded CircuitRelay protobuf message
* @returns {void}
*/
write (msg) {
log('write message type %s', msg.type)
// @ts-ignore lp.encode expects type type 'Buffer | BufferList', not 'Uint8Array'
this.shake.write(lp.encode.single(CircuitPB.encode(msg)))
}

View File

@ -3,11 +3,16 @@
const multiaddr = require('multiaddr')
const { CircuitRelay } = require('../protocol')
/**
* @typedef {import('./stream-handler')} StreamHandler
* @typedef {import('../../types').CircuitStatus} CircuitStatus
*/
/**
* Write a response
*
* @param {StreamHandler} streamHandler
* @param {CircuitRelay.Status} status
* @param {CircuitStatus} status
*/
function writeResponse (streamHandler, status) {
streamHandler.write({

View File

@ -1,8 +1,9 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:relay')
log.error = debug('libp2p:relay:error')
const log = Object.assign(debug('libp2p:relay'), {
error: debug('libp2p:relay:err')
})
const {
setDelayedInterval,
@ -17,6 +18,23 @@ const {
RELAY_RENDEZVOUS_NS
} = require('./constants')
/**
* @typedef {import('../')} Libp2p
*
* @typedef {Object} RelayAdvertiseOptions
* @property {number} [bootDelay = ADVERTISE_BOOT_DELAY]
* @property {boolean} [enabled = true]
* @property {number} [ttl = ADVERTISE_TTL]
*
* @typedef {Object} HopOptions
* @property {boolean} [enabled = false]
* @property {boolean} [active = false]
*
* @typedef {Object} AutoRelayOptions
* @property {number} [maxListeners = 2] - maximum number of relays to listen.
* @property {boolean} [enabled = false]
*/
class Relay {
/**
* Creates an instance of Relay.

View File

@ -1,37 +1,27 @@
'use strict'
const EventEmitter = require('events')
const { EventEmitter } = require('events')
const multiaddr = require('multiaddr')
const debug = require('debug')
const log = debug('libp2p:circuit:listener')
log.err = debug('libp2p:circuit:error:listener')
/**
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('libp2p-interfaces/src/transport/types').Listener} Listener
*/
/**
* @param {Libp2p} libp2p
* @param {import('../')} libp2p
* @returns {Listener} a transport listener
*/
module.exports = (libp2p) => {
const listener = new EventEmitter()
const listeningAddrs = new Map()
// Remove listeningAddrs when a peer disconnects
libp2p.connectionManager.on('peer:disconnect', (connection) => {
const deleted = listeningAddrs.delete(connection.remotePeer.toB58String())
if (deleted) {
// Announce listen addresses change
listener.emit('close')
}
})
/**
* Add swarm handler and listen for incoming connections
*
* @param {Multiaddr} addr
* @returns {void}
* @returns {Promise<void>}
*/
listener.listen = async (addr) => {
async function listen (addr) {
const addrString = String(addr).split('/p2p-circuit').find(a => a !== '')
const relayConn = await libp2p.dial(multiaddr(addrString))
@ -41,13 +31,6 @@ module.exports = (libp2p) => {
listener.emit('listening')
}
/**
* TODO: Remove the peers from our topology
*
* @returns {void}
*/
listener.close = () => {}
/**
* Get fixed up multiaddrs
*
@ -64,7 +47,7 @@ module.exports = (libp2p) => {
*
* @returns {Multiaddr[]}
*/
listener.getAddrs = () => {
function getAddrs () {
const addrs = []
for (const addr of listeningAddrs.values()) {
addrs.push(addr)
@ -72,5 +55,22 @@ module.exports = (libp2p) => {
return addrs
}
/** @type Listener */
const listener = Object.assign(new EventEmitter(), {
close: () => Promise.resolve(),
listen,
getAddrs
})
// Remove listeningAddrs when a peer disconnects
libp2p.connectionManager.on('peer:disconnect', (connection) => {
const deleted = listeningAddrs.delete(connection.remotePeer.toB58String())
if (deleted) {
// Announce listen addresses change
listener.emit('close')
}
})
return listener
}

View File

@ -1,5 +1,7 @@
'use strict'
const protobuf = require('protons')
/** @type {{CircuitRelay: import('../../types').CircuitMessageProto}} */
module.exports = protobuf(`
message CircuitRelay {

View File

@ -1,13 +1,13 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:circuit')
log.error = debug('libp2p:circuit:error')
const log = Object.assign(debug('libp2p:circuit'), {
error: debug('libp2p:circuit:err')
})
const mafmt = require('mafmt')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const withIs = require('class-is')
const { CircuitRelay: CircuitPB } = require('./protocol')
const toConnection = require('libp2p-utils/src/stream-to-ma-conn')
@ -18,14 +18,23 @@ const { handleCanHop, handleHop, hop } = require('./circuit/hop')
const { handleStop } = require('./circuit/stop')
const StreamHandler = require('./circuit/stream-handler')
const transportSymbol = Symbol.for('@libp2p/js-libp2p-circuit/circuit')
/**
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
* @typedef {import('../types').CircuitRequest} CircuitRequest
*/
class Circuit {
/**
* Creates an instance of the Circuit Transport.
*
* @class
* @param {object} options
* @param {Libp2p} options.libp2p
* @param {Upgrader} options.upgrader
* @param {import('../')} options.libp2p
* @param {import('../upgrader')} options.upgrader
*/
constructor ({ libp2p, upgrader }) {
this._dialer = libp2p.dialer
@ -39,7 +48,13 @@ class Circuit {
this._registrar.handle(multicodec, this._onProtocol.bind(this))
}
/**
* @param {Object} props
* @param {Connection} props.connection
* @param {MuxedStream} props.stream
*/
async _onProtocol ({ connection, stream }) {
/** @type {import('./circuit/stream-handler')<CircuitRequest>} */
const streamHandler = new StreamHandler({ stream })
const request = await streamHandler.read()
@ -71,8 +86,7 @@ class Circuit {
virtualConnection = await handleStop({
connection,
request,
streamHandler,
circuit
streamHandler
})
break
}
@ -89,7 +103,7 @@ class Circuit {
remoteAddr,
localAddr
})
const type = CircuitPB.Type === CircuitPB.Type.HOP ? 'relay' : 'inbound'
const type = request.type === CircuitPB.Type.HOP ? 'relay' : 'inbound'
log('new %s connection %s', type, maConn.remoteAddr)
const conn = await this._upgrader.upgradeInbound(maConn)
@ -101,10 +115,10 @@ class Circuit {
/**
* Dial a peer over a relay
*
* @param {multiaddr} ma - the multiaddr of the peer to dial
* @param {Multiaddr} ma - the multiaddr of the peer to dial
* @param {Object} options - dial options
* @param {AbortSignal} [options.signal] - An optional abort signal
* @returns {Connection} - the connection
* @returns {Promise<Connection>} - the connection
*/
async dial (ma, options) {
// Check the multiaddr to see if it contains a relay and a destination peer
@ -124,7 +138,6 @@ class Circuit {
try {
const virtualConnection = await hop({
connection: relayConnection,
circuit: this,
request: {
type: CircuitPB.Type.HOP,
srcPeer: {
@ -159,7 +172,7 @@ class Circuit {
*
* @param {any} options
* @param {Function} handler
* @returns {listener}
* @returns {import('libp2p-interfaces/src/transport/types').Listener}
*/
createListener (options, handler) {
if (typeof options === 'function') {
@ -170,14 +183,14 @@ class Circuit {
// Called on successful HOP and STOP requests
this.handler = handler
return createListener(this._libp2p, options)
return createListener(this._libp2p)
}
/**
* Filter check for all Multiaddrs that this transport can dial on
*
* @param {Array<Multiaddr>} multiaddrs
* @returns {Array<Multiaddr>}
* @param {Multiaddr[]} multiaddrs
* @returns {Multiaddr[]}
*/
filter (multiaddrs) {
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
@ -186,9 +199,20 @@ class Circuit {
return mafmt.Circuit.matches(ma)
})
}
get [Symbol.toStringTag] () {
return 'Circuit'
}
/**
* Checks if the given value is a Transport instance.
*
* @param {any} other
* @returns {other is Transport}
*/
static isTransport (other) {
return Boolean(other && other[transportSymbol])
}
}
/**
* @type {Circuit}
*/
module.exports = withIs(Circuit, { className: 'Circuit', symbolName: '@libp2p/js-libp2p-circuit/circuit' })
module.exports = Circuit

View File

@ -3,6 +3,8 @@
const CID = require('cids')
const multihashing = require('multihashing-async')
const TextEncoder = require('ipfs-utils/src/text-encoder')
/**
* Convert a namespace string into a cid.
*

View File

@ -1,8 +1,9 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:connection-manager')
log.error = debug('libp2p:connection-manager:error')
const log = Object.assign(debug('libp2p:connection-manager'), {
error: debug('libp2p:connection-manager:err')
})
const errcode = require('err-code')
const mergeOptions = require('merge-options')
@ -14,7 +15,7 @@ const { EventEmitter } = require('events')
const PeerId = require('peer-id')
const {
ERR_INVALID_PARAMETERS
codes: { ERR_INVALID_PARAMETERS }
} = require('../errors')
const defaultOptions = {
@ -31,29 +32,39 @@ const defaultOptions = {
}
/**
* Responsible for managing known connections.
* @typedef {import('../')} Libp2p
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
*/
/**
* @typedef {Object} ConnectionManagerOptions
* @property {number} [maxConnections = Infinity] - The maximum number of connections allowed.
* @property {number} [minConnections = 0] - The minimum number of connections to avoid pruning.
* @property {number} [maxData = Infinity] - The max data (in and out), per average interval to allow.
* @property {number} [maxSentData = Infinity] - The max outgoing data, per average interval to allow.
* @property {number} [maxReceivedData = Infinity] - The max incoming data, per average interval to allow.
* @property {number} [maxEventLoopDelay = Infinity] - The upper limit the event loop can take to run.
* @property {number} [pollInterval = 2000] - How often, in milliseconds, metrics and latency should be checked.
* @property {number} [movingAverageInterval = 60000] - How often, in milliseconds, to compute averages.
* @property {number} [defaultPeerValue = 1] - The value of the peer.
* @property {boolean} [autoDial = true] - Should preemptively guarantee connections are above the low watermark.
* @property {number} [autoDialInterval = 10000] - How often, in milliseconds, it should preemptively guarantee connections are above the low watermark.
*/
/**
*
* @fires ConnectionManager#peer:connect Emitted when a new peer is connected.
* @fires ConnectionManager#peer:disconnect Emitted when a peer is disconnected.
*/
class ConnectionManager extends EventEmitter {
/**
* Responsible for managing known connections.
*
* @class
* @param {Libp2p} libp2p
* @param {object} options
* @param {number} options.maxConnections - The maximum number of connections allowed. Default=Infinity
* @param {number} options.minConnections - The minimum number of connections to avoid pruning. Default=0
* @param {number} options.maxData - The max data (in and out), per average interval to allow. Default=Infinity
* @param {number} options.maxSentData - The max outgoing data, per average interval to allow. Default=Infinity
* @param {number} options.maxReceivedData - The max incoming data, per average interval to allow.. Default=Infinity
* @param {number} options.maxEventLoopDelay - The upper limit the event loop can take to run. Default=Infinity
* @param {number} options.pollInterval - How often, in milliseconds, metrics and latency should be checked. Default=2000
* @param {number} options.movingAverageInterval - How often, in milliseconds, to compute averages. Default=60000
* @param {number} options.defaultPeerValue - The value of the peer. Default=1
* @param {boolean} options.autoDial - Should preemptively guarantee connections are above the low watermark. Default=true
* @param {number} options.autoDialInterval - How often, in milliseconds, it should preemptively guarantee connections are above the low watermark. Default=10000
* @param {ConnectionManagerOptions} options
*/
constructor (libp2p, options) {
constructor (libp2p, options = {}) {
super()
this._libp2p = libp2p
@ -66,8 +77,6 @@ class ConnectionManager extends EventEmitter {
log('options: %j', this._options)
this._libp2p = libp2p
/**
* Map of peer identifiers to their peer value for pruning connections.
*
@ -78,7 +87,7 @@ class ConnectionManager extends EventEmitter {
/**
* Map of connections per peer
*
* @type {Map<string, Array<conn>>}
* @type {Map<string, Connection[]>}
*/
this.connections = new Map()
@ -159,15 +168,13 @@ class ConnectionManager extends EventEmitter {
*
* @param {PeerId} peerId
* @param {number} value - A number between 0 and 1
* @returns {void}
*/
setPeerValue (peerId, value) {
if (value < 0 || value > 1) {
throw new Error('value should be a number between 0 and 1')
}
if (peerId.toB58String) {
peerId = peerId.toB58String()
}
this._peerValues.set(peerId, value)
this._peerValues.set(peerId.toB58String(), value)
}
/**
@ -177,21 +184,24 @@ class ConnectionManager extends EventEmitter {
* @private
*/
_checkMetrics () {
const movingAverages = this._libp2p.metrics.global.movingAverages
const received = movingAverages.dataReceived[this._options.movingAverageInterval].movingAverage()
this._checkMaxLimit('maxReceivedData', received)
const sent = movingAverages.dataSent[this._options.movingAverageInterval].movingAverage()
this._checkMaxLimit('maxSentData', sent)
const total = received + sent
this._checkMaxLimit('maxData', total)
log('metrics update', total)
this._timer = retimer(this._checkMetrics, this._options.pollInterval)
if (this._libp2p.metrics) {
const movingAverages = this._libp2p.metrics.global.movingAverages
const received = movingAverages.dataReceived[this._options.movingAverageInterval].movingAverage()
this._checkMaxLimit('maxReceivedData', received)
const sent = movingAverages.dataSent[this._options.movingAverageInterval].movingAverage()
this._checkMaxLimit('maxSentData', sent)
const total = received + sent
this._checkMaxLimit('maxData', total)
log('metrics update', total)
this._timer = retimer(this._checkMetrics, this._options.pollInterval)
}
}
/**
* Tracks the incoming connection and check the connection limit
*
* @param {Connection} connection
* @returns {void}
*/
onConnect (connection) {
const peerId = connection.remotePeer
@ -218,6 +228,7 @@ class ConnectionManager extends EventEmitter {
* Removes the connection from tracking
*
* @param {Connection} connection
* @returns {void}
*/
onDisconnect (connection) {
const peerId = connection.remotePeer.toB58String()
@ -237,7 +248,7 @@ class ConnectionManager extends EventEmitter {
* Get a connection with a peer.
*
* @param {PeerId} peerId
* @returns {Connection}
* @returns {Connection|null}
*/
get (peerId) {
const connections = this.getAll(peerId)
@ -251,7 +262,7 @@ class ConnectionManager extends EventEmitter {
* Get all open connections with a peer.
*
* @param {PeerId} peerId
* @returns {Array<Connection>}
* @returns {Connection[]}
*/
getAll (peerId) {
if (!PeerId.isPeerId(peerId)) {

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict'
/**
@ -6,7 +7,7 @@
/* global window */
const globalThis = require('ipfs-utils/src/globalthis')
const EventEmitter = require('events')
const { EventEmitter } = require('events')
const VisibilityChangeEmitter = require('./visibility-change-emitter')
const debug = require('debug')('latency-monitor:LatencyMonitor')
@ -17,6 +18,12 @@ const debug = require('debug')('latency-monitor:LatencyMonitor')
* @property {number} maxMS What was the max time for a cb to be called
* @property {number} avgMs What was the average time for a cb to be called
* @property {number} lengthMs How long this interval was in ms
*
* @typedef {Object} LatencyMonitorOptions
* @property {number} [latencyCheckIntervalMs=500] - How often to add a latency check event (ms)
* @property {number} [dataEmitIntervalMs=5000] - How often to summarize latency check events. null or 0 disables event firing
* @property {Function} [asyncTestFn] - What cb-style async function to use
* @property {number} [latencyRandomPercentage=5] - What percent (+/-) of latencyCheckIntervalMs should we randomly use? This helps avoid alignment to other events.
*/
/**
@ -24,6 +31,8 @@ const debug = require('debug')('latency-monitor:LatencyMonitor')
* the asyncTestFn and timing how long it takes the callback to be called. It can also periodically emit stats about this.
* This can be disabled and stats can be pulled via setting dataEmitIntervalMs = 0.
*
* @extends {EventEmitter}
*
* The default implementation is an event loop latency monitor. This works by firing periodic events into the event loop
* and timing how long it takes to get back.
*
@ -37,11 +46,8 @@ const debug = require('debug')('latency-monitor:LatencyMonitor')
*/
class LatencyMonitor extends EventEmitter {
/**
* @param {object} [options]
* @param {number} [options.latencyCheckIntervalMs=500] - How often to add a latency check event (ms)
* @param {number} [options.dataEmitIntervalMs=5000] - How often to summarize latency check events. null or 0 disables event firing
* @param {Function} [options.asyncTestFn] - What cb-style async function to use
* @param {number} [options.latencyRandomPercentage=5] - What percent (+/-) of latencyCheckIntervalMs should we randomly use? This helps avoid alignment to other events.
* @class
* @param {LatencyMonitorOptions} [options]
*/
constructor ({ latencyCheckIntervalMs, dataEmitIntervalMs, asyncTestFn, latencyRandomPercentage } = {}) {
super()
@ -91,6 +97,7 @@ class LatencyMonitor extends EventEmitter {
// See: http://stackoverflow.com/questions/6032429/chrome-timeouts-interval-suspended-in-background-tabs
if (isBrowser()) {
that._visibilityChangeEmitter = new VisibilityChangeEmitter()
that._visibilityChangeEmitter.on('visibilityChange', (pageInFocus) => {
if (pageInFocus) {
that._startTimers()

View File

@ -1,10 +1,12 @@
// @ts-nocheck
/* global document */
/**
* This code is based on `latency-monitor` (https://github.com/mlucool/latency-monitor) by `mlucool` (https://github.com/mlucool), available under Apache License 2.0 (https://github.com/mlucool/latency-monitor/blob/master/LICENSE)
*/
'use strict'
const EventEmitter = require('events')
const { EventEmitter } = require('events')
const debug = require('debug')('latency-monitor:VisibilityChangeEmitter')
@ -29,12 +31,12 @@ const debug = require('debug')('latency-monitor:VisibilityChangeEmitter')
* });
* // To access the visibility state directly, call:
* console.log('Am I focused now? ' + myVisibilityEmitter.isVisible());
*
* @class VisibilityChangeEmitter
*/
module.exports = class VisibilityChangeEmitter extends EventEmitter {
class VisibilityChangeEmitter extends EventEmitter {
/**
* Creates a VisibilityChangeEmitter
*
* @class
*/
constructor () {
super()
@ -119,3 +121,5 @@ module.exports = class VisibilityChangeEmitter extends EventEmitter {
this.emit('visibilityChange', visible)
}
}
module.exports = VisibilityChangeEmitter

View File

@ -6,111 +6,130 @@ const { messages, codes } = require('./errors')
const all = require('it-all')
const pAny = require('p-any')
module.exports = (node) => {
const routers = node._modules.contentRouting || []
const dht = node._dht
/**
* @typedef {import('peer-id')} PeerId
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('cids')} CID
*/
// If we have the dht, make it first
if (dht) {
routers.unshift(dht)
}
/**
* @typedef {Object} GetData
* @property {PeerId} from
* @property {Uint8Array} val
*/
return {
/**
* Iterates over all content routers in series to find providers of the given key.
* Once a content router succeeds, iteration will stop.
*
* @param {CID} key - The CID key of the content to find
* @param {object} [options]
* @param {number} [options.timeout] - How long the query should run
* @param {number} [options.maxNumProviders] - maximum number of providers to find
* @returns {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>}
*/
async * findProviders (key, options) {
if (!routers.length) {
throw errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE')
}
class ContentRouting {
/**
* @class
* @param {import('./')} libp2p
*/
constructor (libp2p) {
this.libp2p = libp2p
this.routers = libp2p._modules.contentRouting || []
this.dht = libp2p._dht
const result = await pAny(
routers.map(async (router) => {
const provs = await all(router.findProviders(key, options))
if (!provs || !provs.length) {
throw errCode(new Error('not found'), 'NOT_FOUND')
}
return provs
})
)
for (const peer of result) {
yield peer
}
},
/**
* Iterates over all content routers in parallel to notify it is
* a provider of the given key.
*
* @param {CID} key - The CID key of the content to find
* @returns {Promise<void>}
*/
async provide (key) { // eslint-disable-line require-await
if (!routers.length) {
throw errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE')
}
return Promise.all(routers.map((router) => router.provide(key)))
},
/**
* Store the given key/value pair in the DHT.
*
* @param {Uint8Array} key
* @param {Uint8Array} value
* @param {Object} [options] - put options
* @param {number} [options.minPeers] - minimum number of peers required to successfully put
* @returns {Promise<void>}
*/
async put (key, value, options) { // eslint-disable-line require-await
if (!node.isStarted() || !dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return dht.put(key, value, options)
},
/**
* Get the value to the given key.
* Times out after 1 minute by default.
*
* @param {Uint8Array} key
* @param {Object} [options] - get options
* @param {number} [options.timeout] - optional timeout (default: 60000)
* @returns {Promise<{from: PeerId, val: Uint8Array}>}
*/
async get (key, options) { // eslint-disable-line require-await
if (!node.isStarted() || !dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return dht.get(key, options)
},
/**
* Get the `n` values to the given key without sorting.
*
* @param {Uint8Array} key
* @param {number} nVals
* @param {Object} [options] - get options
* @param {number} [options.timeout] - optional timeout (default: 60000)
* @returns {Promise<Array<{from: PeerId, val: Uint8Array}>>}
*/
async getMany (key, nVals, options) { // eslint-disable-line require-await
if (!node.isStarted() || !dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return dht.getMany(key, nVals, options)
// If we have the dht, make it first
if (this.dht) {
this.routers.unshift(this.dht)
}
}
/**
* Iterates over all content routers in series to find providers of the given key.
* Once a content router succeeds, iteration will stop.
*
* @param {CID} key - The CID key of the content to find
* @param {object} [options]
* @param {number} [options.timeout] - How long the query should run
* @param {number} [options.maxNumProviders] - maximum number of providers to find
* @returns {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>}
*/
async * findProviders (key, options) {
if (!this.routers.length) {
throw errCode(new Error('No content this.routers available'), 'NO_ROUTERS_AVAILABLE')
}
const result = await pAny(
this.routers.map(async (router) => {
const provs = await all(router.findProviders(key, options))
if (!provs || !provs.length) {
throw errCode(new Error('not found'), 'NOT_FOUND')
}
return provs
})
)
for (const peer of result) {
yield peer
}
}
/**
* Iterates over all content routers in parallel to notify it is
* a provider of the given key.
*
* @param {CID} key - The CID key of the content to find
* @returns {Promise<void>}
*/
async provide (key) {
if (!this.routers.length) {
throw errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE')
}
await Promise.all(this.routers.map((router) => router.provide(key)))
}
/**
* Store the given key/value pair in the DHT.
*
* @param {Uint8Array} key
* @param {Uint8Array} value
* @param {Object} [options] - put options
* @param {number} [options.minPeers] - minimum number of peers required to successfully put
* @returns {Promise<void>}
*/
put (key, value, options) {
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return this.dht.put(key, value, options)
}
/**
* Get the value to the given key.
* Times out after 1 minute by default.
*
* @param {Uint8Array} key
* @param {Object} [options] - get options
* @param {number} [options.timeout] - optional timeout (default: 60000)
* @returns {Promise<GetData>}
*/
get (key, options) {
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return this.dht.get(key, options)
}
/**
* Get the `n` values to the given key without sorting.
*
* @param {Uint8Array} key
* @param {number} nVals
* @param {Object} [options] - get options
* @param {number} [options.timeout] - optional timeout (default: 60000)
* @returns {Promise<GetData[]>}
*/
async getMany (key, nVals, options) { // eslint-disable-line require-await
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return this.dht.getMany(key, nVals, options)
}
}
module.exports = ContentRouting

View File

@ -1,14 +1,27 @@
'use strict'
const AbortController = require('abort-controller')
const anySignal = require('any-signal')
const debug = require('debug')
const errCode = require('err-code')
const log = debug('libp2p:dialer:request')
log.error = debug('libp2p:dialer:request:error')
const AbortController = require('abort-controller').default
const anySignal = require('any-signal')
const FIFO = require('p-fifo')
const pAny = require('p-any')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('./')} Dialer
* @typedef {import('multiaddr')} Multiaddr
*/
/**
* @typedef {Object} DialOptions
* @property {AbortSignal} signal
*
* @typedef {Object} DialRequestOptions
* @property {Multiaddr[]} addrs
* @property {(m: Multiaddr, options: DialOptions) => Promise<Connection>} dialAction
* @property {Dialer} dialer
*/
class DialRequest {
/**
* Manages running the `dialAction` on multiple provided `addrs` in parallel
@ -17,10 +30,8 @@ class DialRequest {
* started using `DialRequest.run(options)`. Once a single dial has succeeded,
* all other dials in the request will be cancelled.
*
* @param {object} options
* @param {Multiaddr[]} options.addrs
* @param {function(Multiaddr):Promise<Connection>} options.dialAction
* @param {Dialer} options.dialer
* @class
* @param {DialRequestOptions} options
*/
constructor ({
addrs,
@ -34,11 +45,11 @@ class DialRequest {
/**
* @async
* @param {object} options
* @param {AbortSignal} options.signal - An AbortController signal
* @returns {Connection}
* @param {object} [options]
* @param {AbortSignal} [options.signal] - An AbortController signal
* @returns {Promise<Connection>}
*/
async run (options) {
async run (options = {}) {
const tokens = this.dialer.getTokens(this.addrs.length)
// If no tokens are available, throw
if (tokens.length < 1) {
@ -78,4 +89,4 @@ class DialRequest {
}
}
module.exports.DialRequest = DialRequest
module.exports = DialRequest

View File

@ -1,14 +1,15 @@
'use strict'
const multiaddr = require('multiaddr')
const debug = require('debug')
const log = Object.assign(debug('libp2p:dialer'), {
error: debug('libp2p:dialer:err')
})
const errCode = require('err-code')
const multiaddr = require('multiaddr')
const TimeoutController = require('timeout-abort-controller')
const anySignal = require('any-signal')
const debug = require('debug')
const log = debug('libp2p:dialer')
log.error = debug('libp2p:dialer:error')
const { DialRequest } = require('./dial-request')
const DialRequest = require('./dial-request')
const { publicAddressesFirst } = require('libp2p-utils/src/address-sort')
const getPeer = require('../get-peer')
@ -19,17 +20,44 @@ const {
MAX_PER_PEER_DIALS
} = require('../constants')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('peer-id')} PeerId
* @typedef {import('../peer-store')} PeerStore
* @typedef {import('../peer-store/address-book').Address} Address
* @typedef {import('../transport-manager')} TransportManager
*/
/**
* @typedef {Object} DialerProperties
* @property {PeerStore} peerStore
* @property {TransportManager} transportManager
*
* @typedef {(addr:Multiaddr) => Promise<string[]>} Resolver
*
* @typedef {Object} DialerOptions
* @property {(addresses: Address[]) => Address[]} [options.addressSorter = publicAddressesFirst] - Sort the known addresses of a peer before trying to dial.
* @property {number} [concurrency = MAX_PARALLEL_DIALS] - Number of max concurrent dials.
* @property {number} [perPeerLimit = MAX_PER_PEER_DIALS] - Number of max concurrent dials per peer.
* @property {number} [timeout = DIAL_TIMEOUT] - How long a dial attempt is allowed to take.
* @property {Record<string, Resolver>} [resolvers = {}] - multiaddr resolvers to use when dialing
*
* @typedef DialTarget
* @property {string} id
* @property {Multiaddr[]} addrs
*
* @typedef PendingDial
* @property {DialRequest} dialRequest
* @property {TimeoutController} controller
* @property {Promise} promise
* @property {function():void} destroy
*/
class Dialer {
/**
* @class
* @param {object} options
* @param {TransportManager} options.transportManager
* @param {Peerstore} options.peerStore
* @param {(addresses: Array<Address) => Array<Address>} [options.addressSorter = publicAddressesFirst] - Sort the known addresses of a peer before trying to dial.
* @param {number} [options.concurrency = MAX_PARALLEL_DIALS] - Number of max concurrent dials.
* @param {number} [options.perPeerLimit = MAX_PER_PEER_DIALS] - Number of max concurrent dials per peer.
* @param {number} [options.timeout = DIAL_TIMEOUT] - How long a dial attempt is allowed to take.
* @param {object} [options.resolvers = {}] - multiaddr resolvers to use when dialing
* @param {DialerProperties & DialerOptions} options
*/
constructor ({
transportManager,
@ -102,12 +130,6 @@ class Dialer {
}
}
/**
* @typedef DialTarget
* @property {string} id
* @property {Multiaddr[]} addrs
*/
/**
* Creates a DialTarget. The DialTarget is used to create and track
* the DialRequest to a given peer.
@ -145,14 +167,6 @@ class Dialer {
}
}
/**
* @typedef PendingDial
* @property {DialRequest} dialRequest
* @property {TimeoutController} controller
* @property {Promise} promise
* @property {function():void} destroy
*/
/**
* Creates a PendingDial that wraps the underlying DialRequest
*
@ -162,7 +176,7 @@ class Dialer {
* @param {AbortSignal} [options.signal] - An AbortController signal
* @returns {PendingDial}
*/
_createPendingDial (dialTarget, options) {
_createPendingDial (dialTarget, options = {}) {
const dialAction = (addr, options) => {
if (options.signal.aborted) throw errCode(new Error('already aborted'), codes.ERR_ALREADY_ABORTED)
return this.transportManager.dial(addr, options)
@ -211,7 +225,7 @@ class Dialer {
* Resolve multiaddr recursively.
*
* @param {Multiaddr} ma
* @returns {Promise<Array<Multiaddr>>}
* @returns {Promise<Multiaddr[]>}
*/
async _resolve (ma) {
// TODO: recursive logic should live in multiaddr once dns4/dns6 support is in place
@ -228,19 +242,20 @@ class Dialer {
return this._resolve(nm)
}))
return recursiveMultiaddrs.flat().reduce((array, newM) => {
const addrs = recursiveMultiaddrs.flat()
return addrs.reduce((array, newM) => {
if (!array.find(m => m.equals(newM))) {
array.push(newM)
}
return array
}, []) // Unique addresses
}, /** @type {Multiaddr[]} */([]))
}
/**
* Resolve a given multiaddr. If this fails, an empty array will be returned
*
* @param {Multiaddr} ma
* @returns {Promise<Array<Multiaddr>>}
* @returns {Promise<Multiaddr[]>}
*/
async _resolveRecord (ma) {
try {

View File

@ -6,12 +6,16 @@ const errCode = require('err-code')
const { codes } = require('./errors')
/**
* @typedef {import('multiaddr')} Multiaddr
*/
/**
* Converts the given `peer` to a `Peer` object.
* If a multiaddr is received, the addressBook is updated.
*
* @param {PeerId|Multiaddr|string} peer
* @returns {{ id: PeerId, multiaddrs: Array<Multiaddr> }}
* @returns {{ id: PeerId, multiaddrs: Multiaddr[]|undefined }}
*/
function getPeer (peer) {
if (typeof peer === 'string') {

View File

@ -1,5 +1,6 @@
'use strict'
// @ts-ignore file not listed within the file list of projects
const libp2pVersion = require('../../package.json').version
module.exports.PROTOCOL_VERSION = 'ipfs/0.1.0'

View File

@ -1,13 +1,13 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:identify')
log.error = debug('libp2p:identify:error')
const log = Object.assign(debug('libp2p:identify'), {
error: debug('libp2p:identify:err')
})
const errCode = require('err-code')
const pb = require('it-protocol-buffers')
const lp = require('it-length-prefixed')
const pipe = require('it-pipe')
const { pipe } = require('it-pipe')
const { collect, take, consume } = require('streaming-iterables')
const uint8ArrayFromString = require('uint8arrays/from-string')
@ -29,49 +29,22 @@ const {
const { codes } = require('../errors')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
*/
class IdentifyService {
/**
* Takes the `addr` and converts it to a Multiaddr if possible
*
* @param {Uint8Array | string} addr
* @returns {Multiaddr|null}
*/
static getCleanMultiaddr (addr) {
if (addr && addr.length > 0) {
try {
return multiaddr(addr)
} catch (_) {
return null
}
}
return null
}
/**
* @class
* @param {object} options
* @param {Libp2p} options.libp2p
* @param {Object} options
* @param {import('../')} options.libp2p
*/
constructor ({ libp2p }) {
/**
* @property {PeerStore}
*/
this.peerStore = libp2p.peerStore
/**
* @property {ConnectionManager}
*/
this.connectionManager = libp2p.connectionManager
/**
* @property {PeerId}
*/
this.peerId = libp2p.peerId
/**
* @property {AddressManager}
*/
this._libp2p = libp2p
this.peerStore = libp2p.peerStore
this.connectionManager = libp2p.connectionManager
this.peerId = libp2p.peerId
this.handleMessage = this.handleMessage.bind(this)
@ -84,6 +57,7 @@ class IdentifyService {
this.peerStore.metadataBook.set(this.peerId, 'AgentVersion', uint8ArrayFromString(this._host.agentVersion))
this.peerStore.metadataBook.set(this.peerId, 'ProtocolVersion', uint8ArrayFromString(this._host.protocolVersion))
// When a new connection happens, trigger identify
this.connectionManager.on('peer:connect', (connection) => {
this.identify(connection).catch(log.error)
})
@ -106,8 +80,8 @@ class IdentifyService {
/**
* Send an Identify Push update to the list of connections
*
* @param {Array<Connection>} connections
* @returns {Promise<void>}
* @param {Connection[]} connections
* @returns {Promise<void[]>}
*/
async push (connections) {
const signedPeerRecord = await this.peerStore.addressBook.getRawEnvelope(this.peerId)
@ -234,11 +208,11 @@ class IdentifyService {
/**
* A handler to register with Libp2p to process identify messages.
*
* @param {object} options
* @param {string} options.protocol
* @param {*} options.stream
* @param {Object} options
* @param {Connection} options.connection
* @returns {Promise<void>}
* @param {MuxedStream} options.stream
* @param {string} options.protocol
* @returns {Promise<void>|undefined}
*/
handleMessage ({ connection, stream, protocol }) {
switch (protocol) {
@ -256,9 +230,10 @@ class IdentifyService {
* to the requesting peer over the given `connection`
*
* @private
* @param {object} options
* @param {*} options.stream
* @param {Object} options
* @param {MuxedStream} options.stream
* @param {Connection} options.connection
* @returns {Promise<void>}
*/
async _handleIdentify ({ connection, stream }) {
let publicKey = new Uint8Array(0)
@ -296,8 +271,9 @@ class IdentifyService {
*
* @private
* @param {object} options
* @param {*} options.stream
* @param {MuxedStream} options.stream
* @param {Connection} options.connection
* @returns {Promise<void>}
*/
async _handlePush ({ connection, stream }) {
let message
@ -337,16 +313,36 @@ class IdentifyService {
// Update the protocols
this.peerStore.protoBook.set(id, message.protocols)
}
/**
* Takes the `addr` and converts it to a Multiaddr if possible
*
* @param {Uint8Array | string} addr
* @returns {multiaddr|null}
*/
static getCleanMultiaddr (addr) {
if (addr && addr.length > 0) {
try {
return multiaddr(addr)
} catch (_) {
return null
}
}
return null
}
}
module.exports.IdentifyService = IdentifyService
/**
* The protocols the IdentifyService supports
*
* @property multicodecs
*/
module.exports.multicodecs = {
const multicodecs = {
IDENTIFY: MULTICODEC_IDENTIFY,
IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH
}
module.exports.Message = Message
IdentifyService.multicodecs = multicodecs
IdentifyService.Messsage = Message
module.exports = IdentifyService

View File

@ -1,16 +1,17 @@
'use strict'
const { EventEmitter } = require('events')
const debug = require('debug')
const log = Object.assign(debug('libp2p'), {
error: debug('libp2p:err')
})
const { EventEmitter } = require('events')
const globalThis = require('ipfs-utils/src/globalthis')
const log = debug('libp2p')
log.error = debug('libp2p:error')
const errCode = require('err-code')
const PeerId = require('peer-id')
const PeerRouting = require('./peer-routing')
const contentRouting = require('./content-routing')
const ContentRouting = require('./content-routing')
const getPeer = require('./get-peer')
const { validate: validateConfig } = require('./config')
const { codes, messages } = require('./errors')
@ -29,22 +30,95 @@ const PubsubAdapter = require('./pubsub-adapter')
const PersistentPeerStore = require('./peer-store/persistent')
const Registrar = require('./registrar')
const ping = require('./ping')
const {
IdentifyService,
multicodecs: IDENTIFY_PROTOCOLS
} = require('./identify')
const IdentifyService = require('./identify')
const IDENTIFY_PROTOCOLS = IdentifyService.multicodecs
/**
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
* @typedef {import('libp2p-interfaces/src/transport/types').TransportFactory} TransportFactory
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxerFactory} MuxerFactory
* @typedef {import('libp2p-interfaces/src/crypto/types').Crypto} Crypto
* @typedef {import('libp2p-interfaces/src/pubsub')} Pubsub
*/
/**
* @typedef {Object} PeerStoreOptions
* @property {boolean} persistence
*
* @typedef {Object} PeerDiscoveryOptions
* @property {boolean} autoDial
*
* @typedef {Object} RelayOptions
* @property {boolean} enabled
* @property {import('./circuit').RelayAdvertiseOptions} advertise
* @property {import('./circuit').HopOptions} hop
* @property {import('./circuit').AutoRelayOptions} autoRelay
*
* @typedef {Object} Libp2pConfig
* @property {Object} [dht] dht module options
* @property {PeerDiscoveryOptions} [peerDiscovery]
* @property {Pubsub} [pubsub] pubsub module options
* @property {RelayOptions} [relay]
* @property {Record<string, Object>} [transport] transport options indexed by transport key
*
* @typedef {Object} Libp2pModules
* @property {TransportFactory[]} transport
* @property {MuxerFactory[]} streamMuxer
* @property {Crypto[]} connEncryption
*
* @typedef {Object} Libp2pOptions
* @property {Libp2pModules} modules libp2p modules to use
* @property {import('./address-manager').AddressManagerOptions} [addresses]
* @property {import('./connection-manager').ConnectionManagerOptions} [connectionManager]
* @property {import('./dialer').DialerOptions} [dialer]
* @property {import('./metrics').MetricsOptions} [metrics]
* @property {Object} [keychain]
* @property {import('./transport-manager').TransportManagerOptions} [transportManager]
* @property {PeerStoreOptions & import('./peer-store/persistent').PersistentPeerStoreOptions} [peerStore]
* @property {Libp2pConfig} [config]
* @property {PeerId} peerId
*
* @typedef {Object} CreateOptions
* @property {PeerId} peerId
*
* @extends {EventEmitter}
* @fires Libp2p#error Emitted when an error occurs
* @fires Libp2p#peer:discovery Emitted when a peer is discovered
*/
class Libp2p extends EventEmitter {
/**
* Like `new Libp2p(options)` except it will create a `PeerId`
* instance if one is not provided in options.
*
* @param {Libp2pOptions & CreateOptions} options - Libp2p configuration options
* @returns {Promise<Libp2p>}
*/
static async create (options) {
if (options.peerId) {
return new Libp2p(options)
}
const peerId = await PeerId.create()
options.peerId = peerId
return new Libp2p(options)
}
/**
* Libp2p node.
*
* @class
* @param {Libp2pOptions} _options
*/
constructor (_options) {
super()
// validateConfig will ensure the config is correct,
// and add default values where appropriate
this._options = validateConfig(_options)
/** @type {PeerId} */
this.peerId = this._options.peerId
this.datastore = this._options.datastore
@ -147,6 +221,7 @@ class Libp2p extends EventEmitter {
})
if (this._config.relay.enabled) {
// @ts-ignore Circuit prototype
this.transportManager.add(Circuit.prototype[Symbol.toStringTag], Circuit)
this.relay = new Relay(this)
}
@ -188,13 +263,14 @@ class Libp2p extends EventEmitter {
if (this._modules.pubsub) {
const Pubsub = this._modules.pubsub
// using pubsub adapter with *DEPRECATED* handlers functionality
/** @type {Pubsub} */
this.pubsub = PubsubAdapter(Pubsub, this, this._config.pubsub)
}
// Attach remaining APIs
// peer and content routing will automatically get modules from _modules and _dht
this.peerRouting = new PeerRouting(this)
this.contentRouting = contentRouting(this)
this.contentRouting = new ContentRouting(this)
// Mount default protocols
ping.mount(this)
@ -208,13 +284,16 @@ class Libp2p extends EventEmitter {
*
* @param {string} eventName
* @param {...any} args
* @returns {void}
* @returns {boolean}
*/
emit (eventName, ...args) {
// TODO: do we still need this?
// @ts-ignore _events does not exist in libp2p
if (eventName === 'error' && !this._events.error) {
log.error(...args)
log.error(args)
return false
} else {
super.emit(eventName, ...args)
return super.emit(eventName, ...args)
}
}
@ -288,9 +367,13 @@ class Libp2p extends EventEmitter {
* Imports the private key as 'self', if needed.
*
* @async
* @returns {void}
* @returns {Promise<void>}
*/
async loadKeychain () {
if (!this.keychain) {
return
}
try {
await this.keychain.findKeyByName('self')
} catch (err) {
@ -317,12 +400,12 @@ class Libp2p extends EventEmitter {
* peer will be added to the nodes `peerStore`
*
* @param {PeerId|Multiaddr|string} peer - The peer to dial
* @param {object} options
* @param {object} [options]
* @param {AbortSignal} [options.signal]
* @returns {Promise<Connection>}
*/
dial (peer, options) {
return this.dialProtocol(peer, null, options)
return this.dialProtocol(peer, [], options)
}
/**
@ -333,7 +416,7 @@ class Libp2p extends EventEmitter {
* @async
* @param {PeerId|Multiaddr|string} peer - The peer to dial
* @param {string[]|string} protocols
* @param {object} options
* @param {object} [options]
* @param {AbortSignal} [options.signal]
* @returns {Promise<Connection|*>}
*/
@ -353,7 +436,7 @@ class Libp2p extends EventEmitter {
}
// If a protocol was provided, create a new stream
if (protocols) {
if (protocols && protocols.length) {
return connection.newStream(protocols)
}
@ -365,7 +448,7 @@ class Libp2p extends EventEmitter {
* by transports to listen with the announce addresses.
* Duplicated addresses and noAnnounce addresses are filtered out.
*
* @returns {Array<Multiaddr>}
* @returns {Multiaddr[]}
*/
get multiaddrs () {
const announceAddrs = this.addressManager.getAnnounceAddrs()
@ -382,7 +465,7 @@ class Libp2p extends EventEmitter {
/**
* Disconnects all connections to the given `peer`
*
* @param {PeerId|multiaddr|string} peer - the peer to close connections to
* @param {PeerId|Multiaddr|string} peer - the peer to close connections to
* @returns {Promise<void>}
*/
async hangUp (peer) {
@ -422,7 +505,7 @@ class Libp2p extends EventEmitter {
* Registers the `handler` for each protocol
*
* @param {string[]|string} protocols
* @param {function({ connection:*, stream:*, protocol:string })} handler
* @param {({ connection: Connection, stream: MuxedStream, protocol: string }) => void} handler
*/
handle (protocols, handler) {
protocols = Array.isArray(protocols) ? protocols : [protocols]
@ -510,7 +593,7 @@ class Libp2p extends EventEmitter {
* Known peers may be emitted.
*
* @private
* @param {{ id: PeerId, multiaddrs: Array<Multiaddr>, protocols: Array<string> }} peer
* @param {{ id: PeerId, multiaddrs: Multiaddr[], protocols: string[] }} peer
*/
_onDiscoveryPeer (peer) {
if (peer.id.toB58String() === this.peerId.toB58String()) {
@ -588,7 +671,9 @@ class Libp2p extends EventEmitter {
// Transport modules with discovery
for (const Transport of this.transportManager.getTransports()) {
// @ts-ignore Transport interface does not include discovery
if (Transport.discovery) {
// @ts-ignore Transport interface does not include discovery
setupService(Transport.discovery)
}
}
@ -597,22 +682,4 @@ class Libp2p extends EventEmitter {
}
}
/**
* Like `new Libp2p(options)` except it will create a `PeerId`
* instance if one is not provided in options.
*
* @param {object} options - Libp2p configuration options
* @returns {Promise<Libp2p>}
*/
Libp2p.create = async function create (options = {}) {
if (options.peerId) {
return new Libp2p(options)
}
const peerId = await PeerId.create()
options.peerId = peerId
return new Libp2p(options)
}
module.exports = Libp2p

View File

@ -1,21 +1,33 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:plaintext'), {
error: debug('libp2p:plaintext:err')
})
const handshake = require('it-handshake')
const lp = require('it-length-prefixed')
const PeerId = require('peer-id')
const debug = require('debug')
const log = debug('libp2p:plaintext')
log.error = debug('libp2p:plaintext:error')
const { UnexpectedPeerError, InvalidCryptoExchangeError } = require('libp2p-interfaces/src/crypto/errors')
const { Exchange, KeyType } = require('./proto')
const protocol = '/plaintext/2.0.0'
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
*/
function lpEncodeExchange (exchange) {
const pb = Exchange.encode(exchange)
return lp.encode.single(pb)
}
/**
* Encrypt connection.
*
* @param {PeerId} localId
* @param {Connection} conn
* @param {PeerId} [remoteId]
*/
async function encrypt (localId, conn, remoteId) {
const shake = handshake(conn)

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict'
require('node-forge/lib/pkcs7')
@ -21,7 +22,7 @@ class CMS {
/**
* Creates a new instance with a keychain
*
* @param {Keychain} keychain - the available keys
* @param {import('./index')} keychain - the available keys
*/
constructor (keychain) {
if (!keychain) {
@ -38,7 +39,7 @@ class CMS {
*
* @param {string} name - The local key name.
* @param {Uint8Array} plain - The data to encrypt.
* @returns {undefined}
* @returns {Promise<Uint8Array>}
*/
async encrypt (name, plain) {
if (!(plain instanceof Uint8Array)) {
@ -68,7 +69,7 @@ class CMS {
* exists, an Error is returned with the property 'missingKeys'. It is array of key ids.
*
* @param {Uint8Array} cmsData - The CMS encrypted data to decrypt.
* @returns {undefined}
* @returns {Promise<Uint8Array>}
*/
async decrypt (cmsData) {
if (!(cmsData instanceof Uint8Array)) {

View File

@ -1,3 +1,4 @@
// @ts-nocheck
/* eslint max-nested-callbacks: ["error", 5] */
'use strict'
@ -101,7 +102,8 @@ class Keychain {
* Creates a new instance of a key chain.
*
* @param {DS} store - where the key are.
* @param {object} options - ???
* @param {object} options
* @class
*/
constructor (store, options) {
if (!store) {

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict'
require('node-forge/lib/x509')

View File

@ -1,7 +1,8 @@
// @ts-nocheck
'use strict'
const mergeOptions = require('merge-options')
const pipe = require('it-pipe')
const { pipe } = require('it-pipe')
const { tap } = require('streaming-iterables')
const oldPeerLRU = require('./old-peers')
const { METRICS: defaultOptions } = require('../constants')
@ -17,15 +18,26 @@ const directionToEvent = {
out: 'dataSent'
}
/**
* @typedef {import('peer-id')} PeerId
* @typedef {import('libp2p-interfaces/src/transport/types').MultiaddrConnection} MultiaddrConnection
*/
/**
* @typedef MetricsProperties
* @property {import('../connection-manager')} connectionManager
*
* @typedef MetricsOptions
* @property {number} [computeThrottleMaxQueueSize = defaultOptions.computeThrottleMaxQueueSize]
* @property {number} [computeThrottleTimeout = defaultOptions.computeThrottleTimeout]
* @property {number[]} [movingAverageIntervals = defaultOptions.movingAverageIntervals]
* @property {number} [maxOldPeersRetention = defaultOptions.maxOldPeersRetention]
*/
class Metrics {
/**
*
* @param {object} options
* @param {ConnectionManager} options.connectionManager
* @param {number} options.computeThrottleMaxQueueSize
* @param {number} options.computeThrottleTimeout
* @param {Array<number>} options.movingAverageIntervals
* @param {number} options.maxOldPeersRetention
* @class
* @param {MetricsProperties & MetricsOptions} options
*/
constructor (options) {
this._options = mergeOptions(defaultOptions, options)
@ -76,7 +88,7 @@ class Metrics {
/**
* Returns a list of `PeerId` strings currently being tracked
*
* @returns {Array<string>}
* @returns {string[]}
*/
get peers () {
return Array.from(this._peerStats.keys())
@ -97,7 +109,7 @@ class Metrics {
/**
* Returns a list of all protocol strings currently being tracked.
*
* @returns {Array<string>}
* @returns {string[]}
*/
get protocols () {
return Array.from(this._protocolStats.keys())
@ -176,6 +188,7 @@ class Metrics {
*
* @param {PeerId} placeholder - A peerId string
* @param {PeerId} peerId
* @returns {void}
*/
updatePlaceholder (placeholder, peerId) {
if (!this._running) return
@ -205,10 +218,10 @@ class Metrics {
* with the placeholder string returned from here, and the known `PeerId`.
*
* @param {Object} options
* @param {{ sink: function(*), source: function() }} options.stream - A duplex iterable stream
* @param {MultiaddrConnection} options.stream - A duplex iterable stream
* @param {PeerId} [options.remotePeer] - The id of the remote peer that's connected
* @param {string} [options.protocol] - The protocol the stream is running
* @returns {string} The peerId string or placeholder string
* @returns {MultiaddrConnection} The peerId string or placeholder string
*/
trackStream ({ stream, remotePeer, protocol }) {
const metrics = this

View File

@ -6,9 +6,10 @@ const LRU = require('hashlru')
* Creates and returns a Least Recently Used Cache
*
* @param {number} maxSize
* @returns {LRUCache}
* @returns {any}
*/
module.exports = (maxSize) => {
// @ts-ignore LRU expression is not callable
const patched = LRU(maxSize)
patched.delete = patched.remove
return patched

View File

@ -1,17 +1,19 @@
// @ts-nocheck
'use strict'
const EventEmitter = require('events')
const { EventEmitter } = require('events')
const Big = require('bignumber.js')
const MovingAverage = require('moving-average')
const retimer = require('retimer')
/**
* A queue based manager for stat processing
*
* @param {Array<string>} initialCounters
* @param {any} options
*/
class Stats extends EventEmitter {
/**
* A queue based manager for stat processing
*
* @class
* @param {string[]} initialCounters
* @param {any} options
*/
constructor (initialCounters, options) {
super()
@ -21,6 +23,7 @@ class Stats extends EventEmitter {
this._frequencyLastTime = Date.now()
this._frequencyAccumulators = {}
this._movingAverages = {}
this._update = this._update.bind(this)
@ -68,7 +71,7 @@ class Stats extends EventEmitter {
/**
* Returns a clone of the current stats.
*
* @returns {Map<string, Stat>}
* @returns {Object}
*/
get snapshot () {
return Object.assign({}, this._stats)
@ -77,7 +80,7 @@ class Stats extends EventEmitter {
/**
* Returns a clone of the internal movingAverages
*
* @returns {Array<MovingAverage>}
* @returns {MovingAverage}
*/
get movingAverages () {
return Object.assign({}, this._movingAverages)
@ -229,7 +232,7 @@ class Stats extends EventEmitter {
* will be updated or initialized if they don't already exist.
*
* @private
* @param {Array<string, number>} op
* @param {{string, number}[]} op
* @throws {InvalidNumber}
* @returns {void}
*/
@ -238,7 +241,7 @@ class Stats extends EventEmitter {
const inc = op[1]
if (typeof inc !== 'number') {
throw new Error('invalid increment number:', inc)
throw new Error(`invalid increment number: ${inc}`)
}
let n

View File

@ -1,9 +1,10 @@
'use strict'
const errCode = require('err-code')
const debug = require('debug')
const log = debug('libp2p:peer-routing')
log.error = debug('libp2p:peer-routing:error')
const log = Object.assign(debug('libp2p:peer-routing'), {
error: debug('libp2p:peer-routing:err')
})
const errCode = require('err-code')
const all = require('it-all')
const pAny = require('p-any')
@ -13,12 +14,13 @@ const {
} = require('set-delayed-interval')
/**
* Responsible for managing the usage of the available Peer Routing modules.
* @typedef {import('peer-id')} PeerId
* @typedef {import('multiaddr')} Multiaddr
*/
class PeerRouting {
/**
* @class
* @param {Libp2p} libp2p
* @param {import('./')} libp2p
*/
constructor (libp2p) {
this._peerId = libp2p.peerId

View File

@ -1,9 +1,10 @@
'use strict'
const errcode = require('err-code')
const debug = require('debug')
const log = debug('libp2p:peer-store:address-book')
log.error = debug('libp2p:peer-store:address-book:error')
const log = Object.assign(debug('libp2p:peer-store:address-book'), {
error: debug('libp2p:peer-store:address-book:err')
})
const errcode = require('err-code')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
@ -17,35 +18,31 @@ const {
const Envelope = require('../record/envelope')
/**
* The AddressBook is responsible for keeping the known multiaddrs
* of a peer.
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('./')} PeerStore
*/
/**
* @typedef {Object} Address
* @property {Multiaddr} multiaddr peer multiaddr.
* @property {boolean} isCertified obtained from a signed peer record.
*
* @typedef {Object} CertifiedRecord
* @property {Uint8Array} raw raw envelope.
* @property {number} seqNumber seq counter.
*
* @typedef {Object} Entry
* @property {Address[]} addresses peer Addresses.
* @property {CertifiedRecord} record certified peer record.
*/
/**
* @extends {Book}
*/
class AddressBook extends Book {
/**
* Address object
* The AddressBook is responsible for keeping the known multiaddrs of a peer.
*
* @typedef {Object} Address
* @property {Multiaddr} multiaddr peer multiaddr.
* @property {boolean} isCertified obtained from a signed peer record.
*/
/**
* CertifiedRecord object
*
* @typedef {Object} CertifiedRecord
* @property {Uint8Array} raw raw envelope.
* @property {number} seqNumber seq counter.
*/
/**
* Entry object for the addressBook
*
* @typedef {Object} Entry
* @property {Array<Address>} addresses peer Addresses.
* @property {CertifiedRecord} record certified peer record.
*/
/**
* @class
* @param {PeerStore} peerStore
*/
@ -70,7 +67,7 @@ class AddressBook extends Book {
/**
* Map known peers to their known Address Entries.
*
* @type {Map<string, Array<Entry>>}
* @type {Map<string, Entry>}
*/
this.data = new Map()
}
@ -105,7 +102,7 @@ class AddressBook extends Book {
const peerId = peerRecord.peerId
const id = peerId.toB58String()
const entry = this.data.get(id) || {}
const entry = this.data.get(id) || { record: undefined }
const storedRecord = entry.record
// ensure seq is greater than, or equal to, the last received
@ -151,7 +148,7 @@ class AddressBook extends Book {
* Returns undefined if no record exists.
*
* @param {PeerId} peerId
* @returns {Promise<Envelope|void>}
* @returns {Promise<Envelope|void>|undefined}
*/
getPeerRecord (peerId) {
const raw = this.getRawEnvelope(peerId)
@ -171,7 +168,7 @@ class AddressBook extends Book {
*
* @override
* @param {PeerId} peerId
* @param {Array<Multiaddr>} multiaddrs
* @param {Multiaddr[]} multiaddrs
* @returns {AddressBook}
*/
set (peerId, multiaddrs) {
@ -181,22 +178,22 @@ class AddressBook extends Book {
}
const addresses = this._toAddresses(multiaddrs)
const id = peerId.toB58String()
const entry = this.data.get(id) || {}
const rec = entry.addresses
// Not replace multiaddrs
if (!addresses.length) {
return this
}
const id = peerId.toB58String()
const entry = this.data.get(id)
// Already knows the peer
if (rec && rec.length === addresses.length) {
const intersection = rec.filter((addr) => addresses.some((newAddr) => addr.multiaddr.equals(newAddr.multiaddr)))
if (entry && entry.addresses && entry.addresses.length === addresses.length) {
const intersection = entry.addresses.filter((addr) => addresses.some((newAddr) => addr.multiaddr.equals(newAddr.multiaddr)))
// Are new addresses equal to the old ones?
// If yes, no changes needed!
if (intersection.length === rec.length) {
if (intersection.length === entry.addresses.length) {
log(`the addresses provided to store are equal to the already stored for ${id}`)
return this
}
@ -204,12 +201,12 @@ class AddressBook extends Book {
this._setData(peerId, {
addresses,
record: entry.record
record: entry && entry.record
})
log(`stored provided multiaddrs for ${id}`)
// Notify the existance of a new peer
if (!rec) {
if (!entry) {
this._ps.emit('peer', peerId)
}
@ -221,7 +218,7 @@ class AddressBook extends Book {
* If the peer is not known, it is set with the given addresses.
*
* @param {PeerId} peerId
* @param {Array<Multiaddr>} multiaddrs
* @param {Multiaddr[]} multiaddrs
* @returns {AddressBook}
*/
add (peerId, multiaddrs) {
@ -233,32 +230,33 @@ class AddressBook extends Book {
const addresses = this._toAddresses(multiaddrs)
const id = peerId.toB58String()
const entry = this.data.get(id) || {}
const rec = entry.addresses || []
const entry = this.data.get(id)
// Add recorded uniquely to the new array (Union)
rec.forEach((addr) => {
if (!addresses.find(r => r.multiaddr.equals(addr.multiaddr))) {
addresses.push(addr)
if (entry && entry.addresses) {
// Add recorded uniquely to the new array (Union)
entry.addresses.forEach((addr) => {
if (!addresses.find(r => r.multiaddr.equals(addr.multiaddr))) {
addresses.push(addr)
}
})
// If the recorded length is equal to the new after the unique union
// The content is the same, no need to update.
if (entry.addresses.length === addresses.length) {
log(`the addresses provided to store are already stored for ${id}`)
return this
}
})
// If the recorded length is equal to the new after the unique union
// The content is the same, no need to update.
if (rec && rec.length === addresses.length) {
log(`the addresses provided to store are already stored for ${id}`)
return this
}
this._setData(peerId, {
addresses,
record: entry.record
record: entry && entry.record
})
log(`added provided multiaddrs for ${id}`)
// Notify the existance of a new peer
if (!entry.addresses) {
if (!(entry && entry.addresses)) {
this._ps.emit('peer', peerId)
}
@ -270,7 +268,7 @@ class AddressBook extends Book {
*
* @override
* @param {PeerId} peerId
* @returns {Array<Address>|undefined}
* @returns {Address[]|undefined}
*/
get (peerId) {
if (!PeerId.isPeerId(peerId)) {
@ -286,9 +284,9 @@ class AddressBook extends Book {
* Transforms received multiaddrs into Address.
*
* @private
* @param {Array<Multiaddr>} multiaddrs
* @param {Multiaddr[]} multiaddrs
* @param {boolean} [isCertified]
* @returns {Array<Address>}
* @returns {Address[]}
*/
_toAddresses (multiaddrs, isCertified = false) {
if (!multiaddrs) {
@ -319,8 +317,8 @@ class AddressBook extends Book {
* Returns `undefined` if there are no known multiaddrs for the given peer.
*
* @param {PeerId} peerId
* @param {(addresses: Array<Address) => Array<Address>} [addressSorter]
* @returns {Array<Multiaddr>|undefined}
* @param {(addresses: Address[]) => Address[]} [addressSorter]
* @returns {Multiaddr[]|undefined}
*/
getMultiaddrsForPeer (peerId, addressSorter = (ms) => ms) {
if (!PeerId.isPeerId(peerId)) {

View File

@ -10,16 +10,19 @@ const {
const passthrough = data => data
/**
* The Book is the skeleton for the PeerStore books.
* @typedef {import('./')} PeerStore
*/
class Book {
/**
* The Book is the skeleton for the PeerStore books.
*
* @class
* @param {Object} properties
* @param {PeerStore} properties.peerStore - PeerStore instance.
* @param {string} properties.eventName - Name of the event to emit by the PeerStore.
* @param {string} properties.eventProperty - Name of the property to emit by the PeerStore.
* @param {Function} [properties.eventTransformer] - Transformer function of the provided data for being emitted.
* @param {(data: any) => any[]} [properties.eventTransformer] - Transformer function of the provided data for being emitted.
*/
constructor ({ peerStore, eventName, eventProperty, eventTransformer = passthrough }) {
this._ps = peerStore
@ -30,7 +33,7 @@ class Book {
/**
* Map known peers to their data.
*
* @type {Map<string, Array<Data>}
* @type {Map<string, any[]|any>}
*/
this.data = new Map()
}
@ -39,7 +42,7 @@ class Book {
* Set known data of a provided peer.
*
* @param {PeerId} peerId
* @param {Array<Data>|Data} data
* @param {any[]|any} data
*/
set (peerId, data) {
throw errcode(new Error('set must be implemented by the subclass'), 'ERR_NOT_IMPLEMENTED')
@ -48,9 +51,9 @@ class Book {
/**
* Set data into the datastructure, persistence and emit it using the provided transformers.
*
* @private
* @protected
* @param {PeerId} peerId - peerId of the data to store
* @param {*} data - data to store.
* @param {any} data - data to store.
* @param {Object} [options] - storing options.
* @param {boolean} [options.emit = true] - emit the provided data.
* @returns {void}
@ -68,9 +71,9 @@ class Book {
/**
* Emit data.
*
* @private
* @protected
* @param {PeerId} peerId
* @param {*} data
* @param {any} [data]
*/
_emit (peerId, data) {
this._ps.emit(this.eventName, {
@ -84,7 +87,7 @@ class Book {
* Returns `undefined` if there is no available data for the given peer.
*
* @param {PeerId} peerId
* @returns {Array<Data>|undefined}
* @returns {any[]|any|undefined}
*/
get (peerId) {
if (!PeerId.isPeerId(peerId)) {
@ -93,6 +96,7 @@ class Book {
const rec = this.data.get(peerId.toB58String())
// @ts-ignore
return rec ? [...rec] : undefined
}

View File

@ -1,9 +1,6 @@
'use strict'
const errcode = require('err-code')
const debug = require('debug')
const log = debug('libp2p:peer-store')
log.error = debug('libp2p:peer-store:error')
const { EventEmitter } = require('events')
const PeerId = require('peer-id')
@ -14,11 +11,15 @@ const MetadataBook = require('./metadata-book')
const ProtoBook = require('./proto-book')
const {
ERR_INVALID_PARAMETERS
codes: { ERR_INVALID_PARAMETERS }
} = require('../errors')
/**
* Responsible for managing known peers, as well as their addresses, protocols and metadata.
* @typedef {import('./address-book').Address} Address
*/
/**
* @extends {EventEmitter}
*
* @fires PeerStore#peer Emitted when a new peer is added.
* @fires PeerStore#change:protocols Emitted when a known peer supports a different set of protocols.
@ -32,12 +33,14 @@ class PeerStore extends EventEmitter {
*
* @typedef {Object} Peer
* @property {PeerId} id peer's peer-id instance.
* @property {Array<Address>} addresses peer's addresses containing its multiaddrs and metadata.
* @property {Array<string>} protocols peer's supported protocols.
* @property {Map<string, Buffer>} metadata peer's metadata map.
* @property {Address[]} addresses peer's addresses containing its multiaddrs and metadata.
* @property {string[]} protocols peer's supported protocols.
* @property {Map<string, Uint8Array>|undefined} metadata peer's metadata map.
*/
/**
* Responsible for managing known peers, as well as their addresses, protocols and metadata.
*
* @param {object} options
* @param {PeerId} options.peerId
* @class
@ -121,7 +124,7 @@ class PeerStore extends EventEmitter {
* Get the stored information of a given peer.
*
* @param {PeerId} peerId
* @returns {Peer}
* @returns {Peer|undefined}
*/
get (peerId) {
if (!PeerId.isPeerId(peerId)) {

View File

@ -1,9 +1,10 @@
'use strict'
const errcode = require('err-code')
const debug = require('debug')
const log = debug('libp2p:peer-store:key-book')
log.error = debug('libp2p:peer-store:key-book:error')
const log = Object.assign(debug('libp2p:peer-store:key-book'), {
error: debug('libp2p:peer-store:key-book:err')
})
const errcode = require('err-code')
const PeerId = require('peer-id')
@ -14,10 +15,17 @@ const {
} = require('../errors')
/**
* The KeyBook is responsible for keeping the known public keys of a peer.
* @typedef {import('./')} PeerStore
* @typedef {import('libp2p-crypto').PublicKey} PublicKey
*/
/**
* @extends {Book}
*/
class KeyBook extends Book {
/**
* The KeyBook is responsible for keeping the known public keys of a peer.
*
* @class
* @param {PeerStore} peerStore
*/
@ -42,7 +50,7 @@ class KeyBook extends Book {
*
* @override
* @param {PeerId} peerId
* @param {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey} publicKey
* @param {PublicKey} publicKey
* @returns {KeyBook}
*/
set (peerId, publicKey) {
@ -72,7 +80,7 @@ class KeyBook extends Book {
*
* @override
* @param {PeerId} peerId
* @returns {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey}
* @returns {PublicKey | undefined}
*/
get (peerId) {
if (!PeerId.isPeerId(peerId)) {

View File

@ -1,9 +1,10 @@
'use strict'
const errcode = require('err-code')
const debug = require('debug')
const log = debug('libp2p:peer-store:proto-book')
log.error = debug('libp2p:peer-store:proto-book:error')
const log = Object.assign(debug('libp2p:peer-store:proto-book'), {
error: debug('libp2p:peer-store:proto-book:err')
})
const errcode = require('err-code')
const uint8ArrayEquals = require('uint8arrays/equals')
const PeerId = require('peer-id')
@ -15,13 +16,19 @@ const {
} = require('../errors')
/**
* The MetadataBook is responsible for keeping the known supported
* protocols of a peer.
* @typedef {import('./')} PeerStore
*/
/**
* @extends {Book}
*
* @fires MetadataBook#change:metadata
*/
class MetadataBook extends Book {
/**
* The MetadataBook is responsible for keeping the known supported
* protocols of a peer.
*
* @class
* @param {PeerStore} peerStore
*/
@ -51,8 +58,9 @@ class MetadataBook extends Book {
* @param {PeerId} peerId
* @param {string} key - metadata key
* @param {Uint8Array} value - metadata value
* @returns {ProtoBook}
* @returns {MetadataBook}
*/
// @ts-ignore override with more then the parameters expected in Book
set (peerId, key, value) {
if (!PeerId.isPeerId(peerId)) {
log.error('peerId must be an instance of peer-id to store data')
@ -95,7 +103,7 @@ class MetadataBook extends Book {
* Get the known data of a provided peer.
*
* @param {PeerId} peerId
* @returns {Map<string, Uint8Array>}
* @returns {Map<string, Uint8Array>|undefined}
*/
get (peerId) {
if (!PeerId.isPeerId(peerId)) {
@ -110,7 +118,7 @@ class MetadataBook extends Book {
*
* @param {PeerId} peerId
* @param {string} key
* @returns {Uint8Array}
* @returns {Uint8Array | undefined}
*/
getValue (peerId, key) {
if (!PeerId.isPeerId(peerId)) {

View File

@ -1,9 +1,9 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:persistent-peer-store')
log.error = debug('libp2p:persistent-peer-store:error')
const log = Object.assign(debug('libp2p:persistent-peer-store'), {
error: debug('libp2p:persistent-peer-store:err')
})
const { Key } = require('interface-datastore')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
@ -21,16 +21,22 @@ const {
const Addresses = require('./pb/address-book.proto')
const Protocols = require('./pb/proto-book.proto')
/**
* @typedef {Object} PersistentPeerStoreProperties
* @property {PeerId} peerId
* @property {any} datastore
*
* @typedef {Object} PersistentPeerStoreOptions
* @property {number} [threshold = 5] - Number of dirty peers allowed before commit data.
*/
/**
* Responsible for managing the persistence of data in the PeerStore.
*/
class PersistentPeerStore extends PeerStore {
/**
* @class
* @param {Object} properties
* @param {PeerId} properties.peerId
* @param {Datastore} properties.datastore - Datastore to persist data.
* @param {number} [properties.threshold = 5] - Number of dirty peers allowed before commit data.
* @param {PersistentPeerStoreProperties & PersistentPeerStoreOptions} properties
*/
constructor ({ peerId, datastore, threshold = 5 }) {
super({ peerId })
@ -340,6 +346,7 @@ class PersistentPeerStore extends PeerStore {
case 'addrs':
decoded = Addresses.decode(value)
// @ts-ignore protected function
this.addressBook._setData(
peerId,
{
@ -357,6 +364,7 @@ class PersistentPeerStore extends PeerStore {
case 'keys':
decoded = await PeerId.createFromPubKey(value)
// @ts-ignore protected function
this.keyBook._setData(
decoded,
decoded,
@ -372,6 +380,7 @@ class PersistentPeerStore extends PeerStore {
case 'protos':
decoded = Protocols.decode(value)
// @ts-ignore protected function
this.protoBook._setData(
peerId,
new Set(decoded.protocols),

View File

@ -1,10 +1,10 @@
'use strict'
const errcode = require('err-code')
const debug = require('debug')
const log = debug('libp2p:peer-store:proto-book')
log.error = debug('libp2p:peer-store:proto-book:error')
const log = Object.assign(debug('libp2p:peer-store:proto-book'), {
error: debug('libp2p:peer-store:proto-book:err')
})
const errcode = require('err-code')
const PeerId = require('peer-id')
const Book = require('./book')
@ -14,13 +14,19 @@ const {
} = require('../errors')
/**
* The ProtoBook is responsible for keeping the known supported
* protocols of a peer.
* @typedef {import('./')} PeerStore
*/
/**
* @extends {Book}
*
* @fires ProtoBook#change:protocols
*/
class ProtoBook extends Book {
/**
* The ProtoBook is responsible for keeping the known supported
* protocols of a peer.
*
* @class
* @param {PeerStore} peerStore
*/
@ -50,7 +56,7 @@ class ProtoBook extends Book {
*
* @override
* @param {PeerId} peerId
* @param {Array<string>} protocols
* @param {string[]} protocols
* @returns {ProtoBook}
*/
set (peerId, protocols) {
@ -88,7 +94,7 @@ class ProtoBook extends Book {
* If the peer was not known before, it will be added.
*
* @param {PeerId} peerId
* @param {Array<string>} protocols
* @param {string[]} protocols
* @returns {ProtoBook}
*/
add (peerId, protocols) {
@ -123,7 +129,7 @@ class ProtoBook extends Book {
* If the protocols did not exist before, nothing will be done.
*
* @param {PeerId} peerId
* @param {Array<string>} protocols
* @param {string[]} protocols
* @returns {ProtoBook}
*/
remove (peerId, protocols) {

View File

@ -1,30 +1,39 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p-ping')
log.error = debug('libp2p-ping:error')
const log = Object.assign(debug('libp2p:ping'), {
error: debug('libp2p:ping:err')
})
const errCode = require('err-code')
const crypto = require('libp2p-crypto')
const pipe = require('it-pipe')
const { pipe } = require('it-pipe')
const { toBuffer } = require('it-buffer')
const { collect, take } = require('streaming-iterables')
const equals = require('uint8arrays/equals')
const { PROTOCOL, PING_LENGTH } = require('./constants')
/**
* @typedef {import('../')} Libp2p
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('peer-id')} PeerId
*/
/**
* Ping a given peer and wait for its response, getting the operation latency.
*
* @param {Libp2p} node
* @param {PeerId|multiaddr} peer
* @param {PeerId|Multiaddr} peer
* @returns {Promise<number>}
*/
async function ping (node, peer) {
// @ts-ignore multiaddr might not have toB58String
log('dialing %s to %s', PROTOCOL, peer.toB58String ? peer.toB58String() : peer)
const { stream } = await node.dialProtocol(peer, PROTOCOL)
const start = new Date()
const start = Date.now()
const data = crypto.randomBytes(PING_LENGTH)
const [result] = await pipe(
@ -36,7 +45,7 @@ async function ping (node, peer) {
)
const end = Date.now()
if (!data.equals(result)) {
if (!equals(data, result)) {
throw errCode(new Error('Received wrong ping ack'), 'ERR_WRONG_PING_ACK')
}

View File

@ -1,16 +1,17 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:pnet'), {
trace: debug('libp2p:pnet:trace'),
error: debug('libp2p:pnet:err')
})
const Errors = require('./errors')
const xsalsa20 = require('xsalsa20')
const KEY_LENGTH = require('./key-generator').KEY_LENGTH
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayToString = require('uint8arrays/to-string')
const log = debug('libp2p:pnet')
log.trace = debug('libp2p:pnet:trace')
log.error = debug('libp2p:pnet:err')
/**
* Creates a stream iterable to encrypt messages in a private network
*

View File

@ -1,12 +1,16 @@
'use strict'
const pipe = require('it-pipe')
const debug = require('debug')
const log = Object.assign(debug('libp2p:pnet'), {
error: debug('libp2p:pnet:err')
})
const { pipe } = require('it-pipe')
const errcode = require('err-code')
const duplexPair = require('it-pair/duplex')
const crypto = require('libp2p-crypto')
const Errors = require('./errors')
const {
ERR_INVALID_PARAMETERS
codes: { ERR_INVALID_PARAMETERS }
} = require('../errors')
const {
createBoxStream,
@ -15,16 +19,16 @@ const {
} = require('./crypto')
const handshake = require('it-handshake')
const { NONCE_LENGTH } = require('./key-generator')
const debug = require('debug')
const log = debug('libp2p:pnet')
log.error = debug('libp2p:pnet:err')
/**
* Takes a Private Shared Key (psk) and provides a `protect` method
* for wrapping existing connections in a private encryption stream
* @typedef {import('libp2p-interfaces/src/transport/types').MultiaddrConnection} MultiaddrConnection
*/
class Protector {
/**
* Takes a Private Shared Key (psk) and provides a `protect` method
* for wrapping existing connections in a private encryption stream.
*
* @param {Uint8Array} keyBuffer - The private shared key buffer
* @class
*/
@ -39,8 +43,8 @@ class Protector {
* between its two peers from the PSK the Protector instance was
* created with.
*
* @param {Connection} connection - The connection to protect
* @returns {*} A protected duplex iterable
* @param {MultiaddrConnection} connection - The connection to protect
* @returns {Promise<MultiaddrConnection>} A protected duplex iterable
*/
async protect (connection) {
if (!connection) {

View File

@ -22,6 +22,8 @@ module.exports = generate
module.exports.NONCE_LENGTH = 24
module.exports.KEY_LENGTH = KEY_LENGTH
// @ts-ignore This condition will always return 'false' since the types 'Module | undefined'
if (require.main === module) {
// @ts-ignore
generate(process.stdout)
}

View File

@ -1,42 +1,54 @@
'use strict'
/**
* @typedef {import('libp2p-interfaces/src/pubsub').InMessage} InMessage
* @typedef {import('libp2p-interfaces/src/pubsub')} PubsubRouter
*/
// Pubsub adapter to keep API with handlers while not removed.
module.exports = (PubsubRouter, libp2p, options) => {
class Pubsub extends PubsubRouter {
/**
* Subscribes to a given topic.
*
* @override
* @param {string} topic
* @param {function(msg: InMessage)} [handler]
* @returns {void}
*/
subscribe (topic, handler) {
// Bind provided handler
handler && this.on(topic, handler)
super.subscribe(topic)
function pubsubAdapter (PubsubRouter, libp2p, options) {
const pubsub = new PubsubRouter(libp2p, options)
pubsub._subscribeAdapter = pubsub.subscribe
pubsub._unsubscribeAdapter = pubsub.unsubscribe
/**
* Subscribes to a given topic.
*
* @override
* @param {string} topic
* @param {(msg: InMessage) => void} [handler]
* @returns {void}
*/
function subscribe (topic, handler) {
// Bind provided handler
handler && pubsub.on(topic, handler)
pubsub._subscribeAdapter(topic)
}
/**
* Unsubscribe from the given topic.
*
* @override
* @param {string} topic
* @param {(msg: InMessage) => void} [handler]
* @returns {void}
*/
function unsubscribe (topic, handler) {
if (!handler) {
pubsub.removeAllListeners(topic)
} else {
pubsub.removeListener(topic, handler)
}
/**
* Unsubscribe from the given topic.
*
* @override
* @param {string} topic
* @param {function(msg: InMessage)} [handler]
* @returns {void}
*/
unsubscribe (topic, handler) {
if (!handler) {
this.removeAllListeners(topic)
} else {
this.removeListener(topic, handler)
}
if (this.listenerCount(topic) === 0) {
super.unsubscribe(topic)
}
if (pubsub.listenerCount(topic) === 0) {
pubsub._unsubscribeAdapter(topic)
}
}
return new Pubsub(libp2p, options)
pubsub.subscribe = subscribe
pubsub.unsubscribe = unsubscribe
return pubsub
}
module.exports = pubsubAdapter

View File

@ -2,7 +2,8 @@
const protons = require('protons')
const message = `
/** @type {{Envelope: import('../../types').MessageProto}} */
module.exports = protons(`
message Envelope {
// public_key is the public key of the keypair the enclosed payload was
// signed with.
@ -20,6 +21,4 @@ message Envelope {
// additional security.
bytes signature = 5;
}
`
module.exports = protons(message).Envelope
`)

View File

@ -1,8 +1,5 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:envelope')
log.error = debug('libp2p:envelope:error')
const errCode = require('err-code')
const uint8arraysConcat = require('uint8arrays/concat')
const uint8arraysFromString = require('uint8arrays/from-string')
@ -15,11 +12,14 @@ const { codes } = require('../../errors')
const Protobuf = require('./envelope.proto')
/**
* The Envelope is responsible for keeping an arbitrary signed record
* by a libp2p peer.
* @typedef {import('libp2p-interfaces/src/record/types').Record} Record
*/
class Envelope {
/**
* The Envelope is responsible for keeping an arbitrary signed record
* by a libp2p peer.
*
* @class
* @param {object} params
* @param {PeerId} params.peerId
@ -49,7 +49,7 @@ class Envelope {
const publicKey = cryptoKeys.marshalPublicKey(this.peerId.pubKey)
this._marshal = Protobuf.encode({
this._marshal = Protobuf.Envelope.encode({
public_key: publicKey,
payload_type: this.payloadType,
payload: this.payload,
@ -102,14 +102,14 @@ const formatSignaturePayload = (domain, payloadType, payload) => {
// - The length of the payload field in bytes
// - The value of the payload field
domain = uint8arraysFromString(domain)
const domainLength = varint.encode(domain.byteLength)
const domainUint8Array = uint8arraysFromString(domain)
const domainLength = varint.encode(domainUint8Array.byteLength)
const payloadTypeLength = varint.encode(payloadType.length)
const payloadLength = varint.encode(payload.length)
return uint8arraysConcat([
new Uint8Array(domainLength),
domain,
domainUint8Array,
new Uint8Array(payloadTypeLength),
payloadType,
new Uint8Array(payloadLength),
@ -124,7 +124,7 @@ const formatSignaturePayload = (domain, payloadType, payload) => {
* @returns {Promise<Envelope>}
*/
Envelope.createFromProtobuf = async (data) => {
const envelopeData = Protobuf.decode(data)
const envelopeData = Protobuf.Envelope.decode(data)
const peerId = await PeerId.createFromPubKey(envelopeData.public_key)
return new Envelope({
@ -142,7 +142,7 @@ Envelope.createFromProtobuf = async (data) => {
* @async
* @param {Record} record
* @param {PeerId} peerId
* @returns {Envelope}
* @returns {Promise<Envelope>}
*/
Envelope.seal = async (record, peerId) => {
const domain = record.domain
@ -166,7 +166,7 @@ Envelope.seal = async (record, peerId) => {
*
* @param {Uint8Array} data
* @param {string} domain
* @returns {Envelope}
* @returns {Promise<Envelope>}
*/
Envelope.openAndCertify = async (data, domain) => {
const envelope = await Envelope.createFromProtobuf(data)

View File

@ -2,7 +2,6 @@
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const Record = require('libp2p-interfaces/src/record')
const arrayEquals = require('libp2p-utils/src/array-equals')
const Protobuf = require('./peer-record.proto')
@ -12,19 +11,28 @@ const {
} = require('./consts')
/**
* The PeerRecord is used for distributing peer routing records across the network.
* It contains the peer's reachable listen addresses.
* @typedef {import('peer-id')} PeerId
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('libp2p-interfaces/src/record/types').Record} Record
*/
class PeerRecord extends Record {
/**
* @implements {Record}
*/
class PeerRecord {
/**
* The PeerRecord is used for distributing peer routing records across the network.
* It contains the peer's reachable listen addresses.
*
* @class
* @param {object} params
* @param {Object} params
* @param {PeerId} params.peerId
* @param {Array<multiaddr>} params.multiaddrs - addresses of the associated peer.
* @param {Multiaddr[]} params.multiaddrs - addresses of the associated peer.
* @param {number} [params.seqNumber] - monotonically-increasing sequence counter that's used to order PeerRecords in time.
*/
constructor ({ peerId, multiaddrs = [], seqNumber = Date.now() }) {
super(ENVELOPE_DOMAIN_PEER_RECORD, ENVELOPE_PAYLOAD_TYPE_PEER_RECORD)
this.domain = ENVELOPE_DOMAIN_PEER_RECORD
this.codec = ENVELOPE_PAYLOAD_TYPE_PEER_RECORD
this.peerId = peerId
this.multiaddrs = multiaddrs
@ -44,7 +52,7 @@ class PeerRecord extends Record {
return this._marshal
}
this._marshal = Protobuf.encode({
this._marshal = Protobuf.PeerRecord.encode({
peer_id: this.peerId.toBytes(),
seq: this.seqNumber,
addresses: this.multiaddrs.map((m) => ({
@ -58,10 +66,14 @@ class PeerRecord extends Record {
/**
* Returns true if `this` record equals the `other`.
*
* @param {Record} other
* @param {unknown} other
* @returns {boolean}
*/
equals (other) {
if (!(other instanceof PeerRecord)) {
return false
}
// Validate PeerId
if (!this.peerId.equals(other.peerId)) {
return false
@ -89,7 +101,7 @@ class PeerRecord extends Record {
*/
PeerRecord.createFromProtobuf = (buf) => {
// Decode
const peerRecord = Protobuf.decode(buf)
const peerRecord = Protobuf.PeerRecord.decode(buf)
const peerId = PeerId.createFromBytes(peerRecord.peer_id)
const multiaddrs = (peerRecord.addresses || []).map((a) => multiaddr(a.multiaddr))

View File

@ -7,7 +7,8 @@ const protons = require('protons')
// is expected to expand to include other information in the future.
// PeerRecords are designed to be serialized to bytes and placed inside of
// SignedEnvelopes before sharing with other peers.
const message = `
/** @type {{PeerRecord: import('../../types').MessageProto}} */
module.exports = protons(`
message PeerRecord {
// AddressInfo is a wrapper around a binary multiaddr. It is defined as a
// separate message to allow us to add per-address metadata in the future.
@ -24,6 +25,4 @@ message PeerRecord {
// addresses is a list of public listen addresses for the peer.
repeated AddressInfo addresses = 3;
}
`
module.exports = protons(message).PeerRecord
`)

View File

@ -3,10 +3,14 @@
const Envelope = require('./envelope')
const PeerRecord = require('./peer-record')
/**
* @typedef {import('../')} Libp2p
*/
/**
* Create (or update if existing) self peer record and store it in the AddressBook.
*
* @param {libp2p} libp2p
* @param {Libp2p} libp2p
* @returns {Promise<void>}
*/
async function updateSelfPeerRecord (libp2p) {

View File

@ -1,15 +1,24 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:peer-store'), {
error: debug('libp2p:peer-store:err')
})
const errcode = require('err-code')
const log = debug('libp2p:peer-store')
log.error = debug('libp2p:peer-store:error')
const {
ERR_INVALID_PARAMETERS
codes: { ERR_INVALID_PARAMETERS }
} = require('./errors')
const Topology = require('libp2p-interfaces/src/topology')
/**
* @typedef {import('peer-id')} PeerId
* @typedef {import('./peer-store')} PeerStore
* @typedef {import('./connection-manager')} ConnectionManager
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/topology')} Topology
*/
/**
* Responsible for notifying registered protocols of events in the network.
*/
@ -17,7 +26,7 @@ class Registrar {
/**
* @param {Object} props
* @param {PeerStore} props.peerStore
* @param {connectionManager} props.connectionManager
* @param {ConnectionManager} props.connectionManager
* @class
*/
constructor ({ peerStore, connectionManager }) {
@ -51,7 +60,7 @@ class Registrar {
* Get a connection with a peer.
*
* @param {PeerId} peerId
* @returns {Connection}
* @returns {Connection | null}
*/
getConnection (peerId) {
return this.connectionManager.get(peerId)
@ -65,11 +74,12 @@ class Registrar {
*/
register (topology) {
if (!Topology.isTopology(topology)) {
log.error('topology must be an instance of interfaces/topology')
throw errcode(new Error('topology must be an instance of interfaces/topology'), ERR_INVALID_PARAMETERS)
}
// Create topology
const id = (parseInt(Math.random() * 1e9)).toString(36) + Date.now()
const id = (Math.random() * 1e9).toString(36) + Date.now()
this.topologies.set(id, topology)

View File

@ -1,25 +1,39 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:transports'), {
error: debug('libp2p:transports:err')
})
const pSettle = require('p-settle')
const { codes } = require('./errors')
const errCode = require('err-code')
const debug = require('debug')
const log = debug('libp2p:transports')
log.error = debug('libp2p:transports:error')
const { updateSelfPeerRecord } = require('./record/utils')
/**
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/transport/types').TransportFactory} TransportFactory
* @typedef {import('libp2p-interfaces/src/transport/types').Transport} Transport
*
* @typedef {Object} TransportManagerProperties
* @property {import('./')} libp2p
* @property {import('./upgrader')} upgrader
*
* @typedef {Object} TransportManagerOptions
* @property {number} [faultTolerance = FAULT_TOLERANCE.FATAL_ALL] - Address listen error tolerance.
*/
class TransportManager {
/**
* @class
* @param {object} options
* @param {Libp2p} options.libp2p - The Libp2p instance. It will be passed to the transports.
* @param {Upgrader} options.upgrader - The upgrader to provide to the transports
* @param {boolean} [options.faultTolerance = FAULT_TOLERANCE.FATAL_ALL] - Address listen error tolerance.
* @param {TransportManagerProperties & TransportManagerOptions} options
*/
constructor ({ libp2p, upgrader, faultTolerance = FAULT_TOLERANCE.FATAL_ALL }) {
this.libp2p = libp2p
this.upgrader = upgrader
/** @type {Map<string, Transport>} */
this._transports = new Map()
this._listeners = new Map()
this._listenerOptions = new Map()
@ -30,7 +44,7 @@ class TransportManager {
* Adds a `Transport` to the manager
*
* @param {string} key
* @param {Transport} Transport
* @param {TransportFactory} Transport
* @param {*} transportOptions - Additional options to pass to the transport
* @returns {void}
*/
@ -119,7 +133,7 @@ class TransportManager {
/**
* Returns all the transports instances.
*
* @returns {Iterator<Transport>}
* @returns {IterableIterator<Transport>}
*/
getTransports () {
return this._transports.values()
@ -143,7 +157,7 @@ class TransportManager {
* Starts listeners for each listen Multiaddr.
*
* @async
* @param {Array<Multiaddr>} addrs - addresses to attempt to listen on
* @param {Multiaddr[]} addrs - addresses to attempt to listen on
*/
async listen (addrs) {
if (!addrs || addrs.length === 0) {
@ -159,7 +173,7 @@ class TransportManager {
// For each supported multiaddr, create a listener
for (const addr of supportedAddrs) {
log('creating listener for %s on %s', key, addr)
const listener = transport.createListener(this._listenerOptions.get(key), this.onConnection)
const listener = transport.createListener(this._listenerOptions.get(key))
this._listeners.get(key).push(listener)
// Track listen/close events

84
src/types.ts Normal file
View File

@ -0,0 +1,84 @@
// Insecure Message types
export enum KeyType {
RSA = 0,
Ed25519 = 1,
Secp256k1 = 2,
ECDSA = 3
}
// Protobufs
export type MessageProto = {
encode(value: any): Uint8Array
decode(bytes: Uint8Array): any
}
export type SUCCESS = 100;
export type HOP_SRC_ADDR_TOO_LONG = 220;
export type HOP_DST_ADDR_TOO_LONG = 221;
export type HOP_SRC_MULTIADDR_INVALID = 250;
export type HOP_DST_MULTIADDR_INVALID = 251;
export type HOP_NO_CONN_TO_DST = 260;
export type HOP_CANT_DIAL_DST = 261;
export type HOP_CANT_OPEN_DST_STREAM = 262;
export type HOP_CANT_SPEAK_RELAY = 270;
export type HOP_CANT_RELAY_TO_SELF = 280;
export type STOP_SRC_ADDR_TOO_LONG = 320;
export type STOP_DST_ADDR_TOO_LONG = 321;
export type STOP_SRC_MULTIADDR_INVALID = 350;
export type STOP_DST_MULTIADDR_INVALID = 351;
export type STOP_RELAY_REFUSED = 390;
export type MALFORMED_MESSAGE = 400;
export type CircuitStatus = SUCCESS | HOP_SRC_ADDR_TOO_LONG | HOP_DST_ADDR_TOO_LONG
| HOP_SRC_MULTIADDR_INVALID | HOP_DST_MULTIADDR_INVALID | HOP_NO_CONN_TO_DST
| HOP_CANT_DIAL_DST | HOP_CANT_OPEN_DST_STREAM | HOP_CANT_SPEAK_RELAY | HOP_CANT_RELAY_TO_SELF
| STOP_SRC_ADDR_TOO_LONG | STOP_DST_ADDR_TOO_LONG | STOP_SRC_MULTIADDR_INVALID
| STOP_DST_MULTIADDR_INVALID | STOP_RELAY_REFUSED | MALFORMED_MESSAGE
export type HOP = 1;
export type STOP = 2;
export type STATUS = 3;
export type CAN_HOP = 4;
export type CircuitType = HOP | STOP | STATUS | CAN_HOP
export type CircuitPeer = {
id: Uint8Array
addrs: Uint8Array[]
}
export type CircuitRequest = {
type: CircuitType
dstPeer: CircuitPeer
srcPeer: CircuitPeer
}
export type CircuitMessageProto = {
encode(value: any): Uint8Array
decode(bytes: Uint8Array): any
Status: {
SUCCESS: SUCCESS,
HOP_SRC_ADDR_TOO_LONG: HOP_SRC_ADDR_TOO_LONG,
HOP_DST_ADDR_TOO_LONG: HOP_DST_ADDR_TOO_LONG,
HOP_SRC_MULTIADDR_INVALID: HOP_SRC_MULTIADDR_INVALID,
HOP_DST_MULTIADDR_INVALID: HOP_DST_MULTIADDR_INVALID,
HOP_NO_CONN_TO_DST: HOP_NO_CONN_TO_DST,
HOP_CANT_DIAL_DST: HOP_CANT_DIAL_DST,
HOP_CANT_OPEN_DST_STREAM: HOP_CANT_OPEN_DST_STREAM,
HOP_CANT_SPEAK_RELAY: HOP_CANT_SPEAK_RELAY,
HOP_CANT_RELAY_TO_SELF: HOP_CANT_RELAY_TO_SELF,
STOP_SRC_ADDR_TOO_LONG: STOP_SRC_ADDR_TOO_LONG,
STOP_DST_ADDR_TOO_LONG: STOP_DST_ADDR_TOO_LONG,
STOP_SRC_MULTIADDR_INVALID: STOP_SRC_MULTIADDR_INVALID,
STOP_DST_MULTIADDR_INVALID: STOP_DST_MULTIADDR_INVALID,
STOP_RELAY_REFUSED: STOP_RELAY_REFUSED,
MALFORMED_MESSAGE: MALFORMED_MESSAGE
},
Type: {
HOP: HOP,
STOP: STOP,
STATUS: STATUS,
CAN_HOP: CAN_HOP
}
}

View File

@ -1,29 +1,30 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:upgrader')
log.error = debug('libp2p:upgrader:error')
const log = Object.assign(debug('libp2p:upgrader'), {
error: debug('libp2p:upgrader:err')
})
const errCode = require('err-code')
const Multistream = require('multistream-select')
const { Connection } = require('libp2p-interfaces/src/connection')
const ConnectionStatus = require('libp2p-interfaces/src/connection/status')
const PeerId = require('peer-id')
const pipe = require('it-pipe')
const errCode = require('err-code')
const { pipe } = require('it-pipe')
const mutableProxy = require('mutable-proxy')
const { codes } = require('./errors')
/**
* @typedef MultiaddrConnection
* @property {Function} sink
* @property {AsyncIterator} source
* @property {*} conn
* @property {Multiaddr} remoteAddr
* @typedef {import('libp2p-interfaces/src/transport/types').MultiaddrConnection} MultiaddrConnection
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxerFactory} MuxerFactory
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').Muxer} Muxer
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
* @typedef {import('libp2p-interfaces/src/crypto/types').Crypto} Crypto
* @typedef {import('multiaddr')} Multiaddr
*/
/**
* @typedef CryptoResult
* @property {*} conn A duplex iterable
* @property {MultiaddrConnection} conn A duplex iterable
* @property {PeerId} remotePeer
* @property {string} protocol
*/
@ -32,24 +33,24 @@ class Upgrader {
/**
* @param {object} options
* @param {PeerId} options.localPeer
* @param {Metrics} options.metrics
* @param {Map<string, Crypto>} options.cryptos
* @param {Map<string, Muxer>} options.muxers
* @param {function(Connection)} options.onConnection - Called when a connection is upgraded
* @param {function(Connection)} options.onConnectionEnd
* @param {import('./metrics')} [options.metrics]
* @param {Map<string, Crypto>} [options.cryptos]
* @param {Map<string, MuxerFactory>} [options.muxers]
* @param {(Connection) => void} options.onConnection - Called when a connection is upgraded
* @param {(Connection) => void} options.onConnectionEnd
*/
constructor ({
localPeer,
metrics,
cryptos,
muxers,
cryptos = new Map(),
muxers = new Map(),
onConnectionEnd = () => {},
onConnection = () => {}
}) {
this.localPeer = localPeer
this.metrics = metrics
this.cryptos = cryptos || new Map()
this.muxers = muxers || new Map()
this.cryptos = cryptos
this.muxers = muxers
this.protector = null
this.protocols = new Map()
this.onConnection = onConnection
@ -74,7 +75,7 @@ class Upgrader {
if (this.metrics) {
({ setTarget: setPeer, proxy: proxyPeer } = mutableProxy())
const idString = (parseInt(Math.random() * 1e9)).toString(36) + Date.now()
const idString = (Math.random() * 1e9).toString(36) + Date.now()
setPeer({ toB58String: () => idString })
maConn = this.metrics.trackStream({ stream: maConn, remotePeer: proxyPeer })
}
@ -132,12 +133,7 @@ class Upgrader {
* @returns {Promise<Connection>}
*/
async upgradeOutbound (maConn) {
let remotePeerId
try {
remotePeerId = PeerId.createFromB58String(maConn.remoteAddr.getPeerId())
} catch (err) {
log.error('multiaddr did not contain a valid peer id', err)
}
const remotePeerId = PeerId.createFromB58String(maConn.remoteAddr.getPeerId())
let encryptedConn
let remotePeer
@ -149,7 +145,7 @@ class Upgrader {
if (this.metrics) {
({ setTarget: setPeer, proxy: proxyPeer } = mutableProxy())
const idString = (parseInt(Math.random() * 1e9)).toString(36) + Date.now()
const idString = (Math.random() * 1e9).toString(36) + Date.now()
setPeer({ toB58String: () => idString })
maConn = this.metrics.trackStream({ stream: maConn, remotePeer: proxyPeer })
}
@ -207,8 +203,8 @@ class Upgrader {
* @param {string} options.cryptoProtocol - The crypto protocol that was negotiated
* @param {string} options.direction - One of ['inbound', 'outbound']
* @param {MultiaddrConnection} options.maConn - The transport layer connection
* @param {*} options.upgradedConn - A duplex connection returned from multiplexer and/or crypto selection
* @param {Muxer} options.Muxer - The muxer to be used for muxing
* @param {MuxedStream | MultiaddrConnection} options.upgradedConn - A duplex connection returned from multiplexer and/or crypto selection
* @param {MuxerFactory} [options.Muxer] - The muxer to be used for muxing
* @param {PeerId} options.remotePeer - The peer the connection is with
* @returns {Connection}
*/
@ -272,7 +268,7 @@ class Upgrader {
// Wait for close to finish before notifying of the closure
(async () => {
try {
if (connection.stat.status === ConnectionStatus.OPEN) {
if (connection.stat.status === 'open') {
await connection.close()
}
} catch (err) {
@ -300,6 +296,7 @@ class Upgrader {
remotePeer: remotePeer,
stat: {
direction,
// @ts-ignore
timeline: maConn.timeline,
multiplexer: Muxer && Muxer.multicodec,
encryption: cryptoProtocol
@ -326,7 +323,7 @@ class Upgrader {
* @private
* @param {object} options
* @param {Connection} options.connection - The connection the stream belongs to
* @param {Stream} options.stream
* @param {MuxedStream} options.stream
* @param {string} options.protocol
*/
_onStream ({ connection, stream, protocol }) {
@ -342,7 +339,7 @@ class Upgrader {
* @param {PeerId} localPeer - The initiators PeerId
* @param {*} connection
* @param {Map<string, Crypto>} cryptos
* @returns {CryptoResult} An encrypted connection, remote peer `PeerId` and the protocol of the `Crypto` used
* @returns {Promise<CryptoResult>} An encrypted connection, remote peer `PeerId` and the protocol of the `Crypto` used
*/
async _encryptInbound (localPeer, connection, cryptos) {
const mss = new Multistream.Listener(connection)
@ -354,6 +351,10 @@ class Upgrader {
const crypto = cryptos.get(protocol)
log('encrypting inbound connection...')
if (!crypto) {
throw new Error(`no crypto module found for ${protocol}`)
}
return {
...await crypto.secureInbound(localPeer, stream),
protocol
@ -373,7 +374,7 @@ class Upgrader {
* @param {*} connection
* @param {PeerId} remotePeerId
* @param {Map<string, Crypto>} cryptos
* @returns {CryptoResult} An encrypted connection, remote peer `PeerId` and the protocol of the `Crypto` used
* @returns {Promise<CryptoResult>} An encrypted connection, remote peer `PeerId` and the protocol of the `Crypto` used
*/
async _encryptOutbound (localPeer, connection, remotePeerId, cryptos) {
const mss = new Multistream.Dialer(connection)
@ -385,6 +386,10 @@ class Upgrader {
const crypto = cryptos.get(protocol)
log('encrypting outbound connection to %j', remotePeerId)
if (!crypto) {
throw new Error(`no crypto module found for ${protocol}`)
}
return {
...await crypto.secureOutbound(localPeer, stream, remotePeerId),
protocol
@ -400,9 +405,9 @@ class Upgrader {
*
* @private
* @async
* @param {*} connection - A basic duplex connection to multiplex
* @param {Map<string, Muxer>} muxers - The muxers to attempt multiplexing with
* @returns {*} A muxed connection
* @param {MultiaddrConnection} connection - A basic duplex connection to multiplex
* @param {Map<string, MuxerFactory>} muxers - The muxers to attempt multiplexing with
* @returns {Promise<{ stream: MuxedStream, Muxer?: MuxerFactory}>} A muxed connection
*/
async _multiplexOutbound (connection, muxers) {
const dialer = new Multistream.Dialer(connection)
@ -424,9 +429,9 @@ class Upgrader {
*
* @private
* @async
* @param {*} connection - A basic duplex connection to multiplex
* @param {Map<string, Muxer>} muxers - The muxers to attempt multiplexing with
* @returns {*} A muxed connection
* @param {MultiaddrConnection} connection - A basic duplex connection to multiplex
* @param {Map<string, MuxerFactory>} muxers - The muxers to attempt multiplexing with
* @returns {Promise<{ stream: MuxedStream, Muxer?: MuxerFactory}>} A muxed connection
*/
async _multiplexInbound (connection, muxers) {
const listener = new Multistream.Listener(connection)

View File

@ -10,7 +10,7 @@ const AggregateError = require('aggregate-error')
const pDefer = require('p-defer')
const delay = require('delay')
const { DialRequest } = require('../../src/dialer/dial-request')
const DialRequest = require('../../src/dialer/dial-request')
const createMockConnection = require('../utils/mockConnection')
const error = new Error('dial failes')

View File

@ -12,7 +12,8 @@ const pWaitFor = require('p-wait-for')
const unit8ArrayToString = require('uint8arrays/to-string')
const { codes: Errors } = require('../../src/errors')
const { IdentifyService, multicodecs } = require('../../src/identify')
const IdentifyService = require('../../src/identify')
const multicodecs = IdentifyService.multicodecs
const Peers = require('../fixtures/peers')
const Libp2p = require('../../src')
const Envelope = require('../../src/record/envelope')
@ -54,7 +55,8 @@ describe('Identify', () => {
connectionManager: new EventEmitter(),
peerStore: localPeerStore,
multiaddrs: listenMaddrs,
isStarted: () => true
isStarted: () => true,
_options: { host: {} }
}
})
const remoteIdentify = new IdentifyService({
@ -63,7 +65,8 @@ describe('Identify', () => {
connectionManager: new EventEmitter(),
peerStore: remotePeerStore,
multiaddrs: listenMaddrs,
isStarted: () => true
isStarted: () => true,
_options: { host: {} }
}
})
@ -109,7 +112,8 @@ describe('Identify', () => {
connectionManager: new EventEmitter(),
peerStore: localPeerStore,
multiaddrs: listenMaddrs,
isStarted: () => true
isStarted: () => true,
_options: { host: {} }
}
})
@ -119,7 +123,8 @@ describe('Identify', () => {
connectionManager: new EventEmitter(),
peerStore: remotePeerStore,
multiaddrs: listenMaddrs,
isStarted: () => true
isStarted: () => true,
_options: { host: {} }
}
})
@ -167,7 +172,8 @@ describe('Identify', () => {
peerId: localPeer,
connectionManager: new EventEmitter(),
peerStore: localPeerStore,
multiaddrs: []
multiaddrs: [],
_options: { host: {} }
}
})
const remoteIdentify = new IdentifyService({
@ -175,7 +181,8 @@ describe('Identify', () => {
peerId: remotePeer,
connectionManager: new EventEmitter(),
peerStore: remotePeerStore,
multiaddrs: []
multiaddrs: [],
_options: { host: {} }
}
})
@ -246,7 +253,8 @@ describe('Identify', () => {
connectionManager: new EventEmitter(),
peerStore: localPeerStore,
multiaddrs: listenMaddrs,
isStarted: () => true
isStarted: () => true,
_options: { host: {} }
}
})
@ -259,7 +267,8 @@ describe('Identify', () => {
connectionManager,
peerStore: remotePeerStore,
multiaddrs: [],
isStarted: () => true
isStarted: () => true,
_options: { host: {} }
}
})
@ -316,7 +325,8 @@ describe('Identify', () => {
connectionManager: new EventEmitter(),
peerStore: localPeerStore,
multiaddrs: listenMaddrs,
isStarted: () => true
isStarted: () => true,
_options: { host: {} }
}
})

View File

@ -6,7 +6,6 @@ chai.use(require('chai-bytes'))
const uint8arrayFromString = require('uint8arrays/from-string')
const uint8arrayEquals = require('uint8arrays/equals')
const Envelope = require('../../src/record/envelope')
const Record = require('libp2p-interfaces/src/record')
const { codes: ErrorCodes } = require('../../src/errors')
const peerUtils = require('../utils/creators/peer')
@ -14,9 +13,10 @@ const peerUtils = require('../utils/creators/peer')
const domain = 'libp2p-testing'
const codec = uint8arrayFromString('/libp2p/testdata')
class TestRecord extends Record {
class TestRecord {
constructor (data) {
super(domain, codec)
this.domain = domain
this.codec = codec
this.data = data
}

View File

@ -51,8 +51,6 @@ describe('Transport Manager (TCP)', () => {
})
it('should be able to listen', async () => {
sinon.spy(tm, '_createSelfPeerRecord')
tm.add(Transport.prototype[Symbol.toStringTag], Transport, { listenerOptions: { listen: 'carefully' } })
const transport = tm._transports.get(Transport.prototype[Symbol.toStringTag])
const spyListener = sinon.spy(transport, 'createListener')

View File

@ -56,28 +56,6 @@ describe('Upgrader', () => {
sinon.restore()
})
it('should ignore a missing remote peer id', async () => {
const { inbound, outbound } = mockMultiaddrConnPair({ addrs, remotePeer })
const muxers = new Map([[Muxer.multicodec, Muxer]])
sinon.stub(localUpgrader, 'muxers').value(muxers)
sinon.stub(remoteUpgrader, 'muxers').value(muxers)
const cryptos = new Map([[Crypto.protocol, Crypto]])
sinon.stub(localUpgrader, 'cryptos').value(cryptos)
sinon.stub(remoteUpgrader, 'cryptos').value(cryptos)
// Remove the peer id from the remote address
outbound.remoteAddr = outbound.remoteAddr.decapsulateCode(421)
const connections = await Promise.all([
localUpgrader.upgradeOutbound(outbound),
remoteUpgrader.upgradeInbound(inbound)
])
expect(connections).to.have.length(2)
})
it('should upgrade with valid muxers and crypto', async () => {
const { inbound, outbound } = mockMultiaddrConnPair({ addrs, remotePeer })

9
tsconfig.json Normal file
View File

@ -0,0 +1,9 @@
{
"extends": "./node_modules/aegir/src/config/tsconfig.aegir.json",
"compilerOptions": {
"outDir": "dist"
},
"include": [
"src"
]
}