chore: use new libp2p interface

This commit is contained in:
Vasco Santos 2020-12-01 22:47:59 +01:00
parent 28b9f8562c
commit 4b309ada54
46 changed files with 355 additions and 232 deletions

View File

@ -24,6 +24,7 @@ jobs:
# Remove pull libs once ping is async
- npx aegir dep-check -- -i pull-handshake -i pull-stream
- npm run lint
- npm run test:types
- stage: test
name: chrome

View File

@ -63,7 +63,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",
@ -71,7 +71,7 @@
"it-pipe": "^1.1.0",
"it-protocol-buffers": "^0.2.0",
"libp2p-crypto": "^0.18.0",
"libp2p-interfaces": "libp2p/js-libp2p-interfaces#chore/add-duplex-iterable-type-to-connection",
"libp2p-interfaces": "libp2p/js-libp2p-interfaces#feat/add-types",
"libp2p-utils": "^0.2.2",
"mafmt": "^8.0.0",
"merge-options": "^2.0.0",

View File

@ -90,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)) {

View File

@ -21,7 +21,19 @@ const multicodec = require('./../multicodec')
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
*/
module.exports.handleHop = async function handleHop ({
/**
* @typedef {Object} HopRequest
* @property {Connection} connection
* @property {any} request
* @property {any} streamHandler
* @property {import('../transport')} circuit
*/
/**
* @param {HopRequest} options
* @returns {Promise<void>}
*/
async function handleHop ({
connection,
request,
streamHandler,
@ -56,6 +68,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 = {
@ -68,8 +83,7 @@ module.exports.handleHop = async function handleHop ({
try {
destinationStream = await stop({
connection: destinationConnection,
request: stopRequest,
circuit
request: stopRequest
})
} catch (err) {
return log.error(err)
@ -96,10 +110,10 @@ module.exports.handleHop = async function handleHop ({
*
* @param {object} options
* @param {Connection} options.connection - Connection to the relay
* @param {*} options.request
* @param {CircuitPB} options.request
* @returns {Promise<Connection>}
*/
module.exports.hop = async function hop ({
async function hop ({
connection,
request
}) {
@ -128,7 +142,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
@ -155,10 +169,10 @@ module.exports.canHop = async function canHop ({
* @param {Object} options
* @param {Connection} options.connection
* @param {StreamHandler} options.streamHandler
* @param {Circuit} options.circuit
* @param {import('../transport')} options.circuit
* @private
*/
module.exports.handleCanHop = function handleCanHop ({
function handleCanHop ({
connection,
streamHandler,
circuit
@ -170,3 +184,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

@ -51,7 +51,7 @@ module.exports.handleStop = function handleStop ({
* @private
* @param {object} options
* @param {Connection} options.connection
* @param {*} options.request - The CircuitRelay protobuf request (unencoded)
* @param {CircuitPB} options.request - The CircuitRelay protobuf request (unencoded)
* @returns {Promise<*>} Resolves a duplex iterable
*/
module.exports.stop = async function stop ({

View File

@ -13,9 +13,10 @@ 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 {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} options.stream - A duplex iterable
* @param {number} [options.maxLength = 4096] - max bytes length of message
*/
constructor ({ stream, maxLength = 4096 }) {
this.stream = stream
@ -28,7 +29,7 @@ class StreamHandler {
* Read and decode message
*
* @async
* @returns {Promise<void>}
* @returns {Promise<CircuitPB>}
*/
async read () {
const msg = await this.decoder.next()
@ -50,6 +51,7 @@ class StreamHandler {
*/
write (msg) {
log('write message type %s', msg.type)
// @ts-ignore
this.shake.write(lp.encode.single(CircuitPB.encode(msg)))
}

View File

@ -5,13 +5,14 @@ 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

@ -5,6 +5,7 @@ const multiaddr = require('multiaddr')
/**
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('libp2p-interfaces/src/transport/types').Listener} Listener
*/
/**
@ -12,26 +13,15 @@ const multiaddr = require('multiaddr')
* @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,8 +1,9 @@
'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')
@ -76,8 +77,7 @@ class Circuit {
virtualConnection = await handleStop({
connection,
request,
streamHandler,
circuit
streamHandler
})
break
}
@ -94,7 +94,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)
@ -109,7 +109,7 @@ class Circuit {
* @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
@ -129,6 +129,7 @@ class Circuit {
try {
const virtualConnection = await hop({
connection: relayConnection,
// @ts-ignore
circuit: this,
request: {
type: CircuitPB.Type.HOP,
@ -164,7 +165,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') {
@ -175,7 +176,7 @@ class Circuit {
// Called on successful HOP and STOP requests
this.handler = handler
return createListener(this._libp2p, options)
return createListener(this._libp2p)
}
/**

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

@ -175,10 +175,7 @@ class ConnectionManager extends EventEmitter {
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)
}
/**

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict'
/**

View File

@ -1,3 +1,4 @@
// @ts-nocheck
/* global document */
/**

View File

@ -18,6 +18,7 @@ const pAny = require('p-any')
* @property {function(Multiaddr):Promise<Connection>} dialAction
* @property {Dialer} dialer
*/
class DialRequest {
/**
* Manages running the `dialAction` on multiple provided `addrs` in parallel
@ -54,6 +55,7 @@ class DialRequest {
const tokenHolder = new FIFO()
tokens.forEach(token => tokenHolder.push(token))
// @ts-ignore
const dialAbortControllers = this.addrs.map(() => new AbortController())
let completedDials = 0
@ -63,6 +65,7 @@ class DialRequest {
let conn
try {
const signal = dialAbortControllers[i].signal
// @ts-ignore
conn = await this.dialAction(addr, { ...options, signal: anySignal([signal, options.signal]) })
// Remove the successful AbortController so it is not aborted
dialAbortControllers.splice(i, 1)
@ -85,4 +88,4 @@ class DialRequest {
}
}
module.exports.DialRequest = DialRequest
module.exports = DialRequest

View File

@ -9,7 +9,7 @@ const multiaddr = require('multiaddr')
const TimeoutController = require('timeout-abort-controller')
const anySignal = require('any-signal')
const { DialRequest } = require('./dial-request')
const DialRequest = require('./dial-request')
const { publicAddressesFirst } = require('libp2p-utils/src/address-sort')
const getPeer = require('../get-peer')
@ -27,7 +27,6 @@ const {
* @typedef {import('../peer-store')} PeerStore
* @typedef {import('../peer-store/address-book').Address} Address
* @typedef {import('../transport-manager')} TransportManager
* @typedef {import('./dial-request')} DialRequest
*/
/**
@ -241,12 +240,13 @@ class Dialer {
return this._resolve(nm)
}))
return recursiveMultiaddrs.flat().reduce((array, newM) => {
if (!array.find(m => m.equals(newM))) {
array.push(newM)
}
return array
}, []) // Unique addresses
return recursiveMultiaddrs.flat()
.reduce((/** @type {Multiaddr[]} */ array, /** @type {Multiaddr} */ newM) => {
if (!array.find(m => m.equals(newM))) {
array.push(newM)
}
return array
}, []) // Unique addresses
}
/**

View File

@ -1,5 +1,6 @@
'use strict'
// @ts-ignore
const libp2pVersion = require('../../package.json').version
module.exports.PROTOCOL_VERSION = 'ipfs/0.1.0'

View File

@ -31,7 +31,7 @@ const { codes } = require('../errors')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/connection/connection').DuplexIterableStream} DuplexIterableStream
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
*/
class IdentifyService {
@ -200,9 +200,9 @@ class IdentifyService {
* A handler to register with Libp2p to process identify messages.
*
* @param {Object} options
* @param {string} options.protocol
* @param {DuplexIterableStream} options.stream
* @param {Connection} options.connection
* @param {MuxedStream} options.stream
* @param {string} options.protocol
* @returns {Promise<void>|undefined}
*/
handleMessage ({ connection, stream, protocol }) {
@ -222,7 +222,7 @@ class IdentifyService {
*
* @private
* @param {Object} options
* @param {DuplexIterableStream} options.stream
* @param {MuxedStream} options.stream
* @param {Connection} options.connection
* @returns {Promise<void>}
*/
@ -262,7 +262,7 @@ class IdentifyService {
*
* @private
* @param {object} options
* @param {DuplexIterableStream} options.stream
* @param {MuxedStream} options.stream
* @param {Connection} options.connection
* @returns {Promise<void>}
*/
@ -323,14 +323,17 @@ class IdentifyService {
}
}
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

@ -30,10 +30,8 @@ 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
@ -193,6 +191,7 @@ class Libp2p extends EventEmitter {
})
if (this._config.relay.enabled) {
// @ts-ignore
this.transportManager.add(Circuit.prototype[Symbol.toStringTag], Circuit)
this.relay = new Relay(this)
}
@ -206,6 +205,7 @@ class Libp2p extends EventEmitter {
// Add the identify service since we can multiplex
this.identifyService = new IdentifyService({ libp2p: this })
// @ts-ignore
this.handle(Object.values(IDENTIFY_PROTOCOLS), this.identifyService.handleMessage)
}
@ -254,13 +254,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
if (eventName === 'error' && !this._events.error) {
log.error(...args)
log.error(args)
return false
} else {
super.emit(eventName, ...args)
return super.emit(eventName, ...args)
}
}
@ -463,7 +466,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: any, protocol: string }) => void} handler
*/
handle (protocols, handler) {
protocols = Array.isArray(protocols) ? protocols : [protocols]
@ -629,7 +632,9 @@ class Libp2p extends EventEmitter {
// Transport modules with discovery
for (const Transport of this.transportManager.getTransports()) {
// @ts-ignore
if (Transport.discovery) {
// @ts-ignore
setupService(Transport.discovery)
}
}

View File

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

View File

@ -1,3 +1,4 @@
// @ts-nocheck
/* eslint max-nested-callbacks: ["error", 5] */
'use strict'

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')
@ -19,11 +20,12 @@ const directionToEvent = {
/**
* @typedef {import('peer-id')} PeerId
* @typedef {import('libp2p-interfaces/src/transport/types').MultiaddrConnection} MultiaddrConnection
*/
/**
* @typedef MetricsProperties
* @property {ConnectionManager} connectionManager
* @property {import('../connection-manager')} connectionManager
*
* @typedef MetricsOptions
* @property {number} [computeThrottleMaxQueueSize = defaultOptions.computeThrottleMaxQueueSize]
@ -216,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
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 {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,8 @@ class Stats extends EventEmitter {
this._frequencyLastTime = Date.now()
this._frequencyAccumulators = {}
/** @type {{}} */
this._movingAverages = {}
this._update = this._update.bind(this)
@ -68,7 +72,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 +81,7 @@ class Stats extends EventEmitter {
/**
* Returns a clone of the internal movingAverages
*
* @returns {MovingAverage[]}
* @returns {MovingAverage}
*/
get movingAverages () {
return Object.assign({}, this._movingAverages)
@ -238,7 +242,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

@ -20,7 +20,7 @@ const {
class PeerRouting {
/**
* @class
* @param {Libp2p} libp2p
* @param {import('./')} libp2p
*/
constructor (libp2p) {
this._peerId = libp2p.peerId

View File

@ -124,6 +124,7 @@ class AddressBook extends Book {
// Replace unsigned addresses by the new ones from the record
// TODO: Once we have ttls for the addresses, we should merge these in.
// @ts-ignore
this._setData(peerId, {
addresses,
record: {
@ -188,22 +189,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
}
@ -211,12 +212,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 && entry.addresses)) {
this._ps.emit('peer', peerId)
}
@ -240,32 +241,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)
}

View File

@ -54,7 +54,7 @@ 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 {T} data - data to store.
* @param {Object} [options] - storing options.
@ -74,9 +74,9 @@ class Book {
/**
* Emit data.
*
* @private
* @protected
* @param {PeerId} peerId
* @param {T} data
* @param {any} [data]
*/
_emit (peerId, data) {
this._ps.emit(this.eventName, {

View File

@ -35,7 +35,7 @@ class PeerStore extends EventEmitter {
* @property {PeerId} id peer's peer-id instance.
* @property {Address[]} addresses peer's addresses containing its multiaddrs and metadata.
* @property {string[]} protocols peer's supported protocols.
* @property {Map<string, Uint8Array>} metadata peer's metadata map.
* @property {Map<string, Uint8Array>|undefined} metadata peer's metadata map.
*/
/**

View File

@ -49,7 +49,7 @@ class KeyBook extends Book {
*
* @override
* @param {PeerId} peerId
* @param {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey} publicKey
* @param {any} publicKey
* @returns {KeyBook}
*/
set (peerId, publicKey) {
@ -79,7 +79,7 @@ class KeyBook extends Book {
*
* @override
* @param {PeerId} peerId
* @returns {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey}
* @returns {any}
*/
get (peerId) {
if (!PeerId.isPeerId(peerId)) {

View File

@ -60,6 +60,7 @@ class MetadataBook extends Book {
* @param {Uint8Array} value - metadata value
* @returns {MetadataBook}
*/
// @ts-ignore
set (peerId, key, value) {
if (!PeerId.isPeerId(peerId)) {
log.error('peerId must be an instance of peer-id to store data')
@ -102,8 +103,9 @@ class MetadataBook extends Book {
* Get the known data of a provided peer.
*
* @param {PeerId} peerId
* @returns {Map<string, Uint8Array>}
* @returns {Map<string, Uint8Array>|undefined}
*/
// @ts-ignore
get (peerId) {
if (!PeerId.isPeerId(peerId)) {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
@ -117,7 +119,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

@ -24,7 +24,7 @@ const Protocols = require('./pb/proto-book.proto')
/**
* @typedef {Object} PersistentPeerStoreProperties
* @property {PeerId} peerId
* @property {Datastore} datastore
* @property {any} datastore
*
* @typedef {Object} PersistentPeerStoreOptions
* @property {number} [threshold = 5] - Number of dirty peers allowed before commit data.

View File

@ -21,8 +21,7 @@ const handshake = require('it-handshake')
const { NONCE_LENGTH } = require('./key-generator')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/connection/connection').DuplexIterableStream} DuplexIterableStream
* @typedef {import('libp2p-interfaces/src/transport/types').MultiaddrConnection} MultiaddrConnection
*/
class Protector {
@ -44,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 {Promise<DuplexIterableStream>} 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

@ -6,42 +6,49 @@
*/
// 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 {(msg: InMessage) => void} [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 {(msg: InMessage) => void} [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

@ -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({

View File

@ -51,7 +51,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) => ({
@ -65,7 +65,7 @@ class PeerRecord extends Record {
/**
* Returns true if `this` record equals the `other`.
*
* @param {Record} other
* @param {PeerRecord} other
* @returns {boolean}
*/
equals (other) {
@ -96,7 +96,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

@ -60,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)

View File

@ -14,6 +14,7 @@ const { updateSelfPeerRecord } = require('./record/utils')
/**
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/transport/types').Transport} Transport
*
* @typedef {Object} TransportManagerProperties
* @property {import('./')} libp2p
@ -53,6 +54,7 @@ class TransportManager {
throw errCode(new Error('There is already a transport with this key'), codes.ERR_DUPLICATE_TRANSPORT)
}
// @ts-ignore
const transport = new Transport({
...transportOptions,
libp2p: this.libp2p,

View File

@ -16,3 +16,68 @@ export type MessageExchange = {
id: Uint8Array
pubKey: MessagePublicKey
}
// 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 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

@ -9,27 +9,23 @@ 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 { pipe } = require('it-pipe')
const mutableProxy = require('mutable-proxy')
const { codes } = require('./errors')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/transport/types').MultiaddrConnection} MultiaddrConnection
* @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 MultiaddrConnection
* @property {Function} sink
* @property {AsyncIterator} source
* @property {*} conn
* @property {Multiaddr} remoteAddr
*/
/**
* @typedef CryptoResult
* @property {*} conn A duplex iterable
* @property {MultiaddrConnection} conn A duplex iterable
* @property {PeerId} remotePeer
* @property {string} protocol
*/
@ -39,23 +35,23 @@ class Upgrader {
* @param {object} options
* @param {PeerId} options.localPeer
* @param {import('./metrics')} [options.metrics]
* @param {Map<string, Crypto>} options.cryptos
* @param {Map<string, Muxer>} options.muxers
* @param {Map<string, Crypto>} [options.cryptos]
* @param {Map<string, Muxer>} [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
@ -138,12 +134,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
@ -155,7 +146,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 })
}
@ -213,8 +204,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 {Muxer} [options.Muxer] - The muxer to be used for muxing
* @param {PeerId} options.remotePeer - The peer the connection is with
* @returns {Connection}
*/
@ -233,6 +224,7 @@ class Upgrader {
if (Muxer) {
// Create the muxer
// @ts-ignore
muxer = new Muxer({
// Run anytime a remote stream is created
onStream: async muxedStream => {
@ -306,6 +298,7 @@ class Upgrader {
remotePeer: remotePeer,
stat: {
direction,
// @ts-ignore
timeline: maConn.timeline,
multiplexer: Muxer && Muxer.multicodec,
encryption: cryptoProtocol
@ -332,7 +325,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 }) {
@ -348,7 +341,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)
@ -360,6 +353,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
@ -379,7 +376,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)
@ -391,6 +388,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
@ -406,9 +407,9 @@ class Upgrader {
*
* @private
* @async
* @param {*} connection - A basic duplex connection to multiplex
* @param {MultiaddrConnection} connection - A basic duplex connection to multiplex
* @param {Map<string, Muxer>} muxers - The muxers to attempt multiplexing with
* @returns {*} A muxed connection
* @returns {Promise<{ stream: MuxedStream, Muxer?: Muxer}>} A muxed connection
*/
async _multiplexOutbound (connection, muxers) {
const dialer = new Multistream.Dialer(connection)
@ -430,9 +431,9 @@ class Upgrader {
*
* @private
* @async
* @param {*} connection - A basic duplex connection to multiplex
* @param {MultiaddrConnection} connection - A basic duplex connection to multiplex
* @param {Map<string, Muxer>} muxers - The muxers to attempt multiplexing with
* @returns {*} A muxed connection
* @returns {Promise<{ stream: MuxedStream, Muxer?: Muxer}>} 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')

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 })

View File

@ -1,7 +1,21 @@
{
"extends": "./node_modules/aegir/src/config/tsconfig.aegir.json",
"compilerOptions": {
"outDir": "dist"
"outDir": "dist",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": false,
"noImplicitAny": false,
"noImplicitThis": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": false,
"strictFunctionTypes": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"strict": true,
"alwaysStrict": true,
"stripInternal": true
},
"include": [
"src"