diff --git a/package.json b/package.json index 9cf33c80..ff65a066 100644 --- a/package.json +++ b/package.json @@ -78,17 +78,17 @@ "scripts": { "lint": "aegir lint", "build": "tsc", - "postbuild": "mkdirp dist/src/circuit/pb dist/src/fetch/pb dist/src/identify/pb dist/src/insecure/pb && cp src/circuit/pb/*.js src/circuit/pb/*.d.ts dist/src/circuit/pb && cp src/fetch/pb/*.js src/fetch/pb/*.d.ts dist/src/fetch/pb && cp src/identify/pb/*.js src/identify/pb/*.d.ts dist/src/identify/pb && cp src/insecure/pb/*.js src/insecure/pb/*.d.ts dist/src/insecure/pb", + "postbuild": "mkdirp dist/src/circuit/v1/pb dist/src/circuit/v2/pb dist/src/fetch/pb dist/src/identify/pb dist/src/insecure/pb && cp src/circuit/v1/pb/*.js src/circuit/v1/pb/*.d.ts dist/src/circuit/v1/pb && cp src/circuit/v2/pb/*.js src/circuit/v2/pb/*.d.ts dist/src/circuit/v2/pb && cp src/fetch/pb/*.js src/fetch/pb/*.d.ts dist/src/fetch/pb && cp src/identify/pb/*.js src/identify/pb/*.d.ts dist/src/identify/pb && cp src/insecure/pb/*.js src/insecure/pb/*.d.ts dist/src/insecure/pb", "generate": "run-s generate:proto:* generate:proto-types:*", - "generate:proto:circuit": "pbjs -t static-module -w es6 -r libp2p-circuit --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/circuit/protocol/index.js ./src/circuit/protocol/index.proto", - "generate:proto:fetch": "pbjs -t static-module -w es6 -r libp2p-fetch --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/fetch/proto.js ./src/fetch/proto.proto", - "generate:proto:identify": "pbjs -t static-module -w es6 -r libp2p-identify --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/identify/message.js ./src/identify/message.proto", - "generate:proto:plaintext": "pbjs -t static-module -w es6 -r libp2p-plaintext --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/insecure/proto.js ./src/insecure/proto.proto", - "generate:proto-types:circuit": "pbts -o src/circuit/protocol/index.d.ts src/circuit/protocol/index.js", - "generate:proto:circuitv2": "pbjs -t static-module -w es6 -r libp2p-circuit --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/circuit/v2/protocol/index.js ./src/circuit/v2/protocol/index.proto && pbts --out src/circuit/v2/protocol/index.d.ts src/circuit/v2/protocol/index.js", - "generate:proto-types:fetch": "pbts -o src/fetch/proto.d.ts src/fetch/proto.js", - "generate:proto-types:identify": "pbts -o src/identify/message.d.ts src/identify/message.js", - "generate:proto-types:plaintext": "pbts -o src/insecure/proto.d.ts src/insecure/proto.js", + "generate:proto:fetch": "pbjs -t static-module -w es6 -r libp2p-fetch --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/fetch/pb/proto.js ./src/fetch/pb/proto.proto", + "generate:proto:identify": "pbjs -t static-module -w es6 -r libp2p-identify --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/identify/pb/message.js ./src/identify/pb/message.proto", + "generate:proto:plaintext": "pbjs -t static-module -w es6 -r libp2p-plaintext --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/insecure/pb/proto.js ./src/insecure/pb/proto.proto", + "generate:proto:circuit": "pbjs -t static-module -w es6 -r libp2p-circuit --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/circuit/v1/pb/index.js ./src/circuit/v1/pb/index.proto", + "generate:proto:circuitv2": "pbjs -t static-module -w es6 -r libp2p-circuitv2 --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/circuit/v2/pb/index.js ./src/circuit/v2/pb/index.proto && pbts --out src/circuit/v2/pb/index.d.ts src/circuit/v2/pb/index.js", + "generate:proto-types:circuit": "pbts -o src/circuit/v1/pb/index.d.ts src/circuit/v1/pb/index.js", + "generate:proto-types:fetch": "pbts -o src/fetch/pb/proto.d.ts src/fetch/pb/proto.js", + "generate:proto-types:identify": "pbts -o src/identify/pb/message.d.ts src/identify/pb/message.js", + "generate:proto-types:plaintext": "pbts -o src/insecure/pb/proto.d.ts src/insecure/pb/proto.js", "pretest": "npm run build", "test": "aegir test", "test:node": "npm run test -- -t node -f \"./dist/test/**/*.{node,spec}.js\" --cov", @@ -197,7 +197,8 @@ "p-wait-for": "^4.1.0", "rimraf": "^3.0.2", "sinon": "^13.0.1", - "ts-sinon": "^2.0.2" + "ts-sinon": "^2.0.2", + "typescript": "4" }, "browser": { "nat-api": false diff --git a/src/circuit/auto-relay.js b/src/circuit/auto-relay.js deleted file mode 100644 index 21fe90ca..00000000 --- a/src/circuit/auto-relay.js +++ /dev/null @@ -1,316 +0,0 @@ -'use strict' - -const debug = require('debug') -const log = Object.assign(debug('libp2p:auto-relay'), { - error: debug('libp2p:auto-relay:err') -}) - -const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string') -const { toString: uint8ArrayToString } = require('uint8arrays/to-string') -const { Multiaddr } = require('multiaddr') -const all = require('it-all') - -const { protocolIDv2Hop } = require('./multicodec') -const { namespaceToCid } = require('./utils') -const { - CIRCUIT_PROTO_CODE, - HOP_METADATA_KEY, - HOP_METADATA_VALUE, - RELAY_RENDEZVOUS_NS -} = require('./constants') -const { reserve } = require('./v2/hop') - -/** - * @typedef {import('libp2p-interfaces/src/connection').Connection} Connection - * @typedef {import('../peer-store/types').Address} Address - * @typedef {import('./v2/protocol').IReservation} Reservation - * @typedef {import('peer-id')} PeerId - */ - -/** - * @typedef {Object} AutoRelayProperties - * @property {import('../')} libp2p - * - * @typedef {Object} AutoRelayOptions - * @property {number} [maxListeners = 1] - maximum number of relays to listen. - * @property {(error: Error, msg?: string) => {}} [onError] - */ - -class AutoRelay { - /** - * Creates an instance of AutoRelay. - * - * @class - * @param {AutoRelayProperties & AutoRelayOptions} props - */ - constructor ({ libp2p, maxListeners = 1, onError }) { - this._libp2p = libp2p - this._peerId = libp2p.peerId - this._peerStore = libp2p.peerStore - this._connectionManager = libp2p.connectionManager - this._transportManager = libp2p.transportManager - this._addressSorter = libp2p.dialer.addressSorter - - this.maxListeners = maxListeners - - /** - * id => Reservation - * - * @type {Map} - */ - this._listenRelays = new Map() - - this._onProtocolChange = this._onProtocolChange.bind(this) - this._onPeerDisconnected = this._onPeerDisconnected.bind(this) - - this._peerStore.on('change:protocols', this._onProtocolChange) - this._connectionManager.on('peer:disconnect', this._onPeerDisconnected) - - /** - * @param {Error} error - * @param {string} [msg] - */ - this._onError = (error, msg) => { - log.error(msg || error) - onError && onError(error, msg) - } - } - - /** - * Check if a peer supports the relay protocol. - * If the protocol is not supported, check if it was supported before and remove it as a listen relay. - * If the protocol is supported, check if the peer supports **HOP** and add it as a listener if - * inside the threshold. - * - * @param {Object} props - * @param {PeerId} props.peerId - * @param {string[]} props.protocols - * @returns {Promise} - */ - async _onProtocolChange ({ peerId, protocols }) { - const id = peerId.toB58String() - - // Check if it has the protocol - const hasProtocol = protocols.find(protocol => protocol === protocolIDv2Hop) - - // If no protocol, check if we were keeping the peer before as a listenRelay - if (!hasProtocol && this._listenRelays.has(id)) { - await this._removeListenRelay(id) - return - } else if (!hasProtocol || this._listenRelays.has(id)) { - return - } - - // 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)) { - log(`relayed connection to ${id} will not be used to make reservation on`) - return - } - - await this._peerStore.metadataBook.setValue(peerId, HOP_METADATA_KEY, uint8ArrayFromString(HOP_METADATA_VALUE)) - - // Check if already listening on enough relays - if (this._listenRelays.size >= this.maxListeners) { - log('skip reservation as we created max reservations already') - return - } - const reservation = await reserve(connection) - - await this._addListenRelay(connection, id, reservation) - log('added listen relay %s', connection.remotePeer.toB58String()) - } catch (/** @type {any} */ err) { - this._onError(err) - } - } - - /** - * Peer disconnects. - * - * @param {Connection} connection - connection to the peer - */ - _onPeerDisconnected (connection) { - const peerId = connection.remotePeer - const id = peerId.toB58String() - - // Not listening on this relay - if (!this._listenRelays.has(id)) { - return - } - - this._removeListenRelay(id).catch(err => { - log.error(err) - }) - } - - /** - * Attempt to listen on the given relay connection. - * - * @private - * @param {Connection} connection - connection to the peer - * @param {string} id - peer identifier string - * @param {Reservation} reservation - * @returns {Promise} - */ - async _addListenRelay (connection, id, reservation) { - try { - // Get peer known addresses and sort them per public addresses first - const remoteAddrs = await this._peerStore.addressBook.getMultiaddrsForPeer( - connection.remotePeer, this._addressSorter - ) - - // Attempt to listen on relay - const result = await Promise.all( - remoteAddrs.map(async addr => { - try { - // Announce multiaddrs will update on listen success by TransportManager event being triggered - await this._transportManager.listen([new Multiaddr(`${addr.toString()}/p2p-circuit`)]) - return true - } catch (/** @type {any} */ err) { - this._onError(err) - } - - return false - }) - ) - - if (result.includes(true)) { - this._listenRelays.set(id, reservation) - } - } catch (/** @type {any} */ err) { - this._onError(err) - this._listenRelays.delete(id) - } - } - - /** - * Remove listen relay. - * - * @private - * @param {string} id - peer identifier string. - */ - async _removeListenRelay (id) { - if (this._listenRelays.delete(id)) { - // TODO: this should be responsibility of the connMgr - await this._listenOnAvailableHopRelays([id]) - } - } - - /** - * Try to listen on available hop relay connections. - * The following order will happen while we do not have enough relays. - * 1. Check the metadata store for known relays, try to listen on the ones we are already connected. - * 2. Dial and try to listen on the peers we know that support hop but are not connected. - * 3. Search the network. - * - * @param {string[]} [peersToIgnore] - */ - async _listenOnAvailableHopRelays (peersToIgnore = []) { - // TODO: The peer redial issue on disconnect should be handled by connection gating - // Check if already listening on enough relays - if (this._listenRelays.size >= this.maxListeners) { - return - } - - const knownHopsToDial = [] - const peers = await all(this._peerStore.getPeers()) - - // Check if we have known hop peers to use and attempt to listen on the already connected - for await (const { id, metadata } of peers) { - const idStr = id.toB58String() - - // Continue to next if listening on this or peer to ignore - if (this._listenRelays.has(idStr)) { - continue - } - - if (peersToIgnore.includes(idStr)) { - continue - } - - const supportsHop = metadata.get(HOP_METADATA_KEY) - - // Continue to next if it does not support Hop - if (!supportsHop || uint8ArrayToString(supportsHop) !== HOP_METADATA_VALUE) { - continue - } - - const connection = this._connectionManager.get(id) - - // If not connected, store for possible later use. - if (!connection) { - knownHopsToDial.push(id) - continue - } - - // Check if already listening on enough relays - if (this._listenRelays.size >= this.maxListeners) { - return - } - - let reservation - try { - reservation = await reserve(connection) - } catch (e) { - continue - } - - await this._addListenRelay(connection, idStr, reservation) - } - - // Try to listen on known peers that are not connected - for (const peerId of knownHopsToDial) { - await this._tryToListenOnRelay(peerId) - - // Check if already listening on enough relays - if (this._listenRelays.size >= this.maxListeners) { - return - } - } - - // Try to find relays to hop on the network - try { - const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS) - for await (const provider of this._libp2p.contentRouting.findProviders(cid)) { - if (!provider.multiaddrs.length) { - continue - } - - const peerId = provider.id - await this._peerStore.addressBook.add(peerId, provider.multiaddrs) - - await this._tryToListenOnRelay(peerId) - - // Check if already listening on enough relays - if (this._listenRelays.size >= this.maxListeners) { - return - } - } - } catch (/** @type {any} */ err) { - this._onError(err) - } - } - - /** - * @param {PeerId} peerId - */ - async _tryToListenOnRelay (peerId) { - try { - const connection = await this._libp2p.dial(peerId) - - const reservation = await reserve(connection) - - await this._addListenRelay(connection, peerId.toB58String(), reservation) - } catch (/** @type {any} */ err) { - this._onError(err, `could not connect and make reservation on known hop relay ${peerId.toB58String()}`) - } - } -} - -module.exports = AutoRelay diff --git a/src/circuit/auto-relay.ts b/src/circuit/auto-relay.ts index 8d84a8e9..36c02c26 100644 --- a/src/circuit/auto-relay.ts +++ b/src/circuit/auto-relay.ts @@ -1,8 +1,8 @@ import { logger } from '@libp2p/logger' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' -import { RELAY_CODEC } from './multicodec.js' -import { canHop } from './circuit/hop.js' +import { RELAY_V1_CODEC } from './multicodec.js' +import { canHop } from './v1/hop.js' import { namespaceToCid } from './utils.js' import { CIRCUIT_PROTO_CODE, @@ -68,7 +68,7 @@ export class AutoRelay { const id = peerId.toString() // Check if it has the protocol - const hasProtocol = protocols.find(protocol => protocol === RELAY_CODEC) + const hasProtocol = protocols.find(protocol => protocol === RELAY_V1_CODEC) // If no protocol, check if we were keeping the peer before as a listenRelay if (hasProtocol == null) { diff --git a/src/circuit/multicodec.js b/src/circuit/multicodec.js deleted file mode 100644 index d12e17ca..00000000 --- a/src/circuit/multicodec.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' - -module.exports = { - relayV1: '/libp2p/circuit/relay/0.1.0', - protocolIDv2Hop: '/libp2p/circuit/relay/0.2.0/hop', - protocolIDv2Stop: '/libp2p/circuit/relay/0.2.0/stop' -} diff --git a/src/circuit/multicodec.ts b/src/circuit/multicodec.ts index fcd28221..16399847 100644 --- a/src/circuit/multicodec.ts +++ b/src/circuit/multicodec.ts @@ -1,2 +1,4 @@ -export const RELAY_CODEC = '/libp2p/circuit/relay/0.1.0' +export const RELAY_V1_CODEC = '/libp2p/circuit/relay/0.1.0' +export const protocolIDv2Hop = '/libp2p/circuit/relay/0.2.0/hop' +export const protocolIDv2Stop = '/libp2p/circuit/relay/0.2.0/stop' diff --git a/src/circuit/transport.js b/src/circuit/transport.js deleted file mode 100644 index e56ede12..00000000 --- a/src/circuit/transport.js +++ /dev/null @@ -1,391 +0,0 @@ -'use strict' - -const debug = require('debug') -const log = Object.assign(debug('libp2p:circuit'), { - error: debug('libp2p:circuit:err') -}) - -const errCode = require('err-code') -const mafmt = require('mafmt') -const { Multiaddr } = require('multiaddr') -const PeerId = require('peer-id') -const { CircuitRelay: CircuitPB } = require('./v1/protocol') -const { codes } = require('../errors') - -const toConnection = require('libp2p-utils/src/stream-to-ma-conn') - -const { relayV1: protocolIDv1, protocolIDv2Hop, protocolIDv2Stop } = require('./multicodec') -const createListener = require('./listener') -const { handleCanHop, handleHop, hop } = require('./v1/hop') -const { handleStop: handleStopV1 } = require('./v1/stop') -const StreamHandler = require('./v1/stream-handler') -const StreamHandlerV2 = require('./v2/stream-handler') -const { handleHopProtocol } = require('./v2/hop') -const { handleStop: handleStopV2 } = require('./v2/stop') -const { Status, HopMessage, StopMessage } = require('./v2/protocol') -const createError = require('err-code') -const ReservationStore = require('./v2/reservation-store') - -const transportSymbol = Symbol.for('@libp2p/js-libp2p-circuit/circuit') - -/** - * @typedef {import('libp2p-interfaces/src/connection').Connection} Connection - * @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream - */ - -class Circuit { - /** - * Creates an instance of the Circuit Transport. - * - * @class - * @param {object} options - * @param {import('../')} options.libp2p - * @param {import('../upgrader')} options.upgrader - */ - constructor ({ libp2p, upgrader }) { - this._dialer = libp2p.dialer - this._registrar = libp2p.registrar - this._connectionManager = libp2p.connectionManager - this._upgrader = upgrader - this._options = libp2p._config.relay - this._libp2p = libp2p - this.peerId = libp2p.peerId - this._reservationStore = new ReservationStore(this._options.reservationLimit) - - this._registrar.handle(protocolIDv1, this._onV1Protocol.bind(this)) - this._registrar.handle(protocolIDv2Hop, this._onV2ProtocolHop.bind(this)) - this._registrar.handle(protocolIDv2Stop, this._onV2ProtocolStop.bind(this)) - } - - /** - * @param {Object} props - * @param {Connection} props.connection - * @param {MuxedStream} props.stream - */ - async _onV1Protocol ({ connection, stream }) { - /** @type {import('./v1/stream-handler')} */ - const streamHandler = new StreamHandler({ stream }) - const request = await streamHandler.read() - - if (!request) { - return - } - - const circuit = this - let virtualConnection - - switch (request.type) { - case CircuitPB.Type.CAN_HOP: { - log('received CAN_HOP request from %s', connection.remotePeer.toB58String()) - await handleCanHop({ circuit, connection, streamHandler }) - break - } - case CircuitPB.Type.HOP: { - log('received HOP request from %s', connection.remotePeer.toB58String()) - virtualConnection = await handleHop({ - connection, - request, - streamHandler, - circuit - }) - break - } - case CircuitPB.Type.STOP: { - log('received STOP request from %s', connection.remotePeer.toB58String()) - virtualConnection = await handleStopV1({ - connection, - request, - streamHandler - }) - break - } - default: { - log('Request of type %s not supported', request.type) - } - } - - if (virtualConnection) { - // @ts-ignore dst peer will not be undefined - const remoteAddr = new Multiaddr(request.dstPeer.addrs[0]) - // @ts-ignore src peer will not be undefined - const localAddr = new Multiaddr(request.srcPeer.addrs[0]) - const maConn = toConnection({ - stream: virtualConnection, - remoteAddr, - localAddr - }) - const type = request.type === CircuitPB.Type.HOP ? 'relay' : 'inbound' - log('new %s connection %s', type, maConn.remoteAddr) - - const conn = await this._upgrader.upgradeInbound(maConn) - log('%s connection %s upgraded', type, maConn.remoteAddr) - this.handler && this.handler(conn) - } - } - - /** - * As a relay it handles hop connect and reserve request - * - * @param {Object} props - * @param {Connection} props.connection - * @param {MuxedStream} props.stream - */ - async _onV2ProtocolHop ({ connection, stream }) { - log('received circuit v2 hop protocol stream from %s', connection.remotePeer.toB58String()) - const streamHandler = new StreamHandlerV2({ stream }) - const request = HopMessage.decode(await streamHandler.read()) - - if (!request) { - return - } - - await handleHopProtocol({ - connection, - streamHandler, - circuit: this, - relayPeer: this._libp2p.peerId, - relayAddrs: this._libp2p.multiaddrs, - reservationStore: this._reservationStore, - request, - limit: null, - acl: null - }) - } - - /** - * As a client this is used to - * - * @param {Object} props - * @param {Connection} props.connection - * @param {MuxedStream} props.stream - */ - async _onV2ProtocolStop ({ connection, stream }) { - const streamHandler = new StreamHandlerV2({ stream }) - const request = StopMessage.decode(await streamHandler.read()) - log('received circuit v2 stop protocol request from %s', connection.remotePeer.toB58String()) - if (!request) { - return - } - - const mStream = await handleStopV2({ - connection, - streamHandler, - request - }) - - if (mStream) { - // @ts-ignore dst peer will not be undefined - const remoteAddr = new Multiaddr(request.peer.addrs[0]) - const localAddr = this._libp2p.transportManager.getAddrs()[0] - const maConn = toConnection({ - stream: mStream, - remoteAddr, - localAddr - }) - log('new inbound connection %s', maConn.remoteAddr) - const conn = await this._upgrader.upgradeInbound(maConn) - log('%s connection %s upgraded', 'inbound', maConn.remoteAddr) - this.handler && this.handler(conn) - } - } - - /** - * Dial a peer over a relay - * - * @param {Multiaddr} ma - the multiaddr of the peer to dial - * @param {Object} options - dial options - * @param {AbortSignal} [options.signal] - An optional abort signal - * @returns {Promise} - the connection - */ - async dial (ma, options) { - // Check the multiaddr to see if it contains a relay and a destination peer - const addrs = ma.toString().split('/p2p-circuit') - const relayAddr = new Multiaddr(addrs[0]) - const destinationAddr = new Multiaddr(addrs[addrs.length - 1]) - const relayId = relayAddr.getPeerId() - const destinationId = destinationAddr.getPeerId() - - if (!relayId || !destinationId) { - const errMsg = 'Circuit relay dial failed as addresses did not have peer id' - log.error(errMsg) - throw errCode(new Error(errMsg), codes.ERR_RELAYED_DIAL) - } - - const relayPeer = PeerId.createFromB58String(relayId) - const destinationPeer = PeerId.createFromB58String(destinationId) - - let disconnectOnFailure = false - let relayConnection = this._connectionManager.get(relayPeer) - if (!relayConnection) { - relayConnection = await this._dialer.connectToPeer(relayAddr, options) - disconnectOnFailure = true - } - - const stream = await relayConnection.newStream([protocolIDv2Hop, protocolIDv1]) - - switch (stream.protocol) { - case protocolIDv1: return await this.connectV1({ - stream: stream.stream, - connection: relayConnection, - destinationPeer, - destinationAddr, - relayAddr, - ma, - disconnectOnFailure - }) - case protocolIDv2Hop: return await this.connectV2({ - stream: stream.stream, - connection: relayConnection, - destinationPeer, - destinationAddr, - relayAddr, - ma, - disconnectOnFailure - }) - default: - stream.stream.reset() - throw new Error('Unexpected stream protocol') - } - } - - /** - * - * @param {Object} params - * @param {MuxedStream} params.stream - * @param {Connection} params.connection - * @param {PeerId} params.destinationPeer - * @param {Multiaddr} params.destinationAddr - * @param {Multiaddr} params.relayAddr - * @param {Multiaddr} params.ma - * @param {boolean} params.disconnectOnFailure - * @returns {Promise} - */ - async connectV1 ({ stream, connection, destinationPeer, destinationAddr, relayAddr, ma, disconnectOnFailure }) { - try { - const virtualConnection = await hop({ - stream, - request: { - type: CircuitPB.Type.HOP, - srcPeer: { - id: this.peerId.toBytes(), - addrs: this._libp2p.multiaddrs.map(addr => addr.bytes) - }, - dstPeer: { - id: destinationPeer.toBytes(), - addrs: [new Multiaddr(destinationAddr).bytes] - } - } - }) - - const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toB58String()}`) - const maConn = toConnection({ - stream: virtualConnection, - remoteAddr: ma, - localAddr - }) - log('new outbound connection %s', maConn.remoteAddr) - - return this._upgrader.upgradeOutbound(maConn) - } catch (/** @type {any} */ err) { - log.error('Circuit relay dial failed', err) - disconnectOnFailure && await connection.close() - throw err - } - } - - /** - * - * @param {Object} params - * @param {MuxedStream} params.stream - * @param {Connection} params.connection - * @param {PeerId} params.destinationPeer - * @param {Multiaddr} params.destinationAddr - * @param {Multiaddr} params.relayAddr - * @param {Multiaddr} params.ma - * @param {boolean} params.disconnectOnFailure - * @returns {Promise} - */ - async connectV2 ({ stream, connection, destinationPeer, destinationAddr, relayAddr, ma, disconnectOnFailure }) { - try { - const streamHandler = new StreamHandlerV2({ stream }) - streamHandler.write(HopMessage.encode({ - type: HopMessage.Type.CONNECT, - peer: { - id: destinationPeer.toBytes(), - addrs: [new Multiaddr(destinationAddr).bytes] - } - }).finish()) - - const status = HopMessage.decode(await streamHandler.read()) - if (status.status !== Status.OK) { - throw createError(new Error('failed to connect via relay with status ' + status.status), codes.ERR_HOP_REQUEST_FAILED) - } - - // TODO: do something with limit and transient connection - - let localAddr = relayAddr - localAddr = localAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toB58String()}`) - const maConn = toConnection({ - stream: streamHandler.rest(), - remoteAddr: ma, - localAddr - }) - log('new outbound connection %s', maConn.remoteAddr) - const conn = await this._upgrader.upgradeOutbound(maConn) - return conn - } catch (/** @type {any} */ err) { - log.error('Circuit relay dial failed', err) - disconnectOnFailure && await connection.close() - throw err - } - } - - /** - * Create a listener - * - * @param {any} options - * @param {Function} handler - * @returns {import('libp2p-interfaces/src/transport/types').Listener} - */ - createListener (options, handler) { - if (typeof options === 'function') { - handler = options - options = {} - } - - // Called on successful HOP and STOP requests - this.handler = handler - - return createListener(this._libp2p) - } - - /** - * Filter check for all Multiaddrs that this transport can dial on - * - * @param {Multiaddr[]} multiaddrs - * @returns {Multiaddr[]} - */ - filter (multiaddrs) { - multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs] - - return multiaddrs.filter((ma) => { - 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]) - } -} - -module.exports = Circuit diff --git a/src/circuit/transport.ts b/src/circuit/transport.ts index 9722d576..a7cb25cf 100644 --- a/src/circuit/transport.ts +++ b/src/circuit/transport.ts @@ -1,33 +1,56 @@ +import * as CircuitV1 from './v1/pb/index.js' +import * as CircuitV2 from './v2/pb/index.js' +import { ReservationStore } from './v2/reservation-store.js' import { logger } from '@libp2p/logger' -import errCode from 'err-code' +import createError from 'err-code' import * as mafmt from '@multiformats/mafmt' import { Multiaddr } from '@multiformats/multiaddr' -import { CircuitRelay as CircuitPB } from './pb/index.js' import { codes } from '../errors.js' import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn' -import { RELAY_CODEC } from './multicodec.js' +import { protocolIDv2Hop, RELAY_V1_CODEC } from './multicodec.js' import { createListener } from './listener.js' -import { handleCanHop, handleHop, hop } from './circuit/hop.js' -import { handleStop } from './circuit/stop.js' -import { StreamHandler } from './circuit/stream-handler.js' import { symbol } from '@libp2p/interfaces/transport' import { peerIdFromString } from '@libp2p/peer-id' import { Components, Initializable } from '@libp2p/interfaces/components' import type { AbortOptions } from '@libp2p/interfaces' import type { IncomingStreamData } from '@libp2p/interfaces/registrar' import type { Listener, Transport, CreateListenerOptions, ConnectionHandler } from '@libp2p/interfaces/transport' -import type { Connection } from '@libp2p/interfaces/connection' +import type { Connection, Stream } from '@libp2p/interfaces/connection' +import type { PeerId } from '@libp2p/interfaces/peer-id' +import { StreamHandlerV2 } from './v2/stream-handler.js' +import { StreamHandlerV1 } from './v1/stream-handler.js' +import * as CircuitV1Handler from './v1/index.js' +import * as CircuitV2Handler from './v2/index.js' const log = logger('libp2p:circuit') +export interface CircuitOptions { + limit?: number +} + +interface ConnectOptions { + stream: Stream + connection: Connection + destinationPeer: PeerId + destinationAddr: Multiaddr + relayAddr: Multiaddr + ma: Multiaddr + disconnectOnFailure: boolean +} export class Circuit implements Transport, Initializable { private handler?: ConnectionHandler private components: Components = new Components() + private readonly reservationStore: ReservationStore + + constructor (options: CircuitOptions) { + this.reservationStore = new ReservationStore(options.limit) + } init (components: Components): void { this.components = components - void this.components.getRegistrar().handle(RELAY_CODEC, (data) => { - void this._onProtocol(data).catch(err => { + + void this.components.getRegistrar().handle(RELAY_V1_CODEC, (data) => { + void this._onProtocolV1(data).catch(err => { log.error(err) }) }) @@ -52,16 +75,20 @@ export class Circuit implements Transport, Initializable { return this.constructor.name } - async _onProtocol (data: IncomingStreamData) { + getPeerConnection (dstPeer: PeerId) { + return this.components.getConnectionManager().getConnection(dstPeer) + } + + async _onProtocolV1 (data: IncomingStreamData) { const { connection, stream } = data - const streamHandler = new StreamHandler({ stream }) + const streamHandler = new StreamHandlerV1({ stream }) const request = await streamHandler.read() if (request == null) { log('request was invalid, could not read from stream') streamHandler.write({ - type: CircuitPB.Type.STATUS, - code: CircuitPB.Status.MALFORMED_MESSAGE + type: CircuitV1.CircuitRelay.Type.STATUS, + code: CircuitV1.CircuitRelay.Status.MALFORMED_MESSAGE }) streamHandler.close() return @@ -70,14 +97,14 @@ export class Circuit implements Transport, Initializable { let virtualConnection switch (request.type) { - case CircuitPB.Type.CAN_HOP: { + case CircuitV1.CircuitRelay.Type.CAN_HOP: { log('received CAN_HOP request from %p', connection.remotePeer) - await handleCanHop({ circuit: this, connection, streamHandler }) + await CircuitV1Handler.handleCanHop({ circuit: this, connection, streamHandler }) break } - case CircuitPB.Type.HOP: { + case CircuitV1.CircuitRelay.Type.HOP: { log('received HOP request from %p', connection.remotePeer) - virtualConnection = await handleHop({ + virtualConnection = await CircuitV1Handler.handleHop({ connection, request, streamHandler, @@ -86,9 +113,9 @@ export class Circuit implements Transport, Initializable { }) break } - case CircuitPB.Type.STOP: { + case CircuitV1.CircuitRelay.Type.STOP: { log('received STOP request from %p', connection.remotePeer) - virtualConnection = await handleStop({ + virtualConnection = await CircuitV1Handler.handleStop({ connection, request, streamHandler @@ -98,8 +125,8 @@ export class Circuit implements Transport, Initializable { default: { log('Request of type %s not supported', request.type) streamHandler.write({ - type: CircuitPB.Type.STATUS, - code: CircuitPB.Status.MALFORMED_MESSAGE + type: CircuitV1.CircuitRelay.Type.STATUS, + code: CircuitV1.CircuitRelay.Status.MALFORMED_MESSAGE }) streamHandler.close() return @@ -107,16 +134,14 @@ export class Circuit implements Transport, Initializable { } if (virtualConnection != null) { - // @ts-expect-error dst peer will not be undefined - const remoteAddr = new Multiaddr(request.dstPeer.addrs[0]) - // @ts-expect-error dst peer will not be undefined - const localAddr = new Multiaddr(request.srcPeer.addrs[0]) + const remoteAddr = new Multiaddr(request.dstPeer?.addrs?.[0] ?? '') + const localAddr = new Multiaddr(request.srcPeer?.addrs?.[0] ?? '') const maConn = streamToMaConnection({ stream: virtualConnection, remoteAddr, localAddr }) - const type = request.type === CircuitPB.Type.HOP ? 'relay' : 'inbound' + const type = request.type === CircuitV1.CircuitRelay.Type.HOP ? 'relay' : 'inbound' log('new %s connection %s', type, maConn.remoteAddr) const conn = await this.components.getUpgrader().upgradeInbound(maConn) @@ -128,6 +153,55 @@ export class Circuit implements Transport, Initializable { } } + async _onV2ProtocolHop ({ connection, stream }: IncomingStreamData) { + log('received circuit v2 hop protocol stream from %s', connection.remotePeer) + const streamHandler = new StreamHandlerV2({ stream }) + const request = CircuitV2.HopMessage.decode(await streamHandler.read()) + + if (request?.type === undefined) { + return + } + + await CircuitV2Handler.handleHopProtocol({ + connection, + streamHandler, + circuit: this, + relayPeer: this.components.getPeerId(), + relayAddrs: this.components.getAddressManager().getListenAddrs(), + reservationStore: this.reservationStore, + request + }) + } + + async _onV2ProtocolStop ({ connection, stream }: IncomingStreamData) { + const streamHandler = new StreamHandlerV2({ stream }) + const request = CircuitV2.StopMessage.decode(await streamHandler.read()) + log('received circuit v2 stop protocol request from %s', connection.remotePeer) + if (request?.type === undefined) { + return + } + + const mStream = await CircuitV2Handler.handleStop({ + connection, + streamHandler, + request + }) + + if (mStream !== null && mStream !== undefined) { + const remoteAddr = new Multiaddr(request.peer?.addrs?.[0]) + const localAddr = this.components.getTransportManager().getAddrs()[0] + const maConn = streamToMaConnection({ + stream: mStream, + remoteAddr, + localAddr + }) + log('new inbound connection %s', maConn.remoteAddr) + const conn = await this.components.getUpgrader().upgradeInbound(maConn) + log('%s connection %s upgraded', 'inbound', maConn.remoteAddr) + this.handler?.(conn) + } + } + /** * Dial a peer over a relay */ @@ -142,7 +216,7 @@ export class Circuit implements Transport, Initializable { if (relayId == null || destinationId == null) { const errMsg = 'Circuit relay dial failed as addresses did not have peer id' log.error(errMsg) - throw errCode(new Error(errMsg), codes.ERR_RELAYED_DIAL) + throw createError(new Error(errMsg), codes.ERR_RELAYED_DIAL) } const relayPeer = peerIdFromString(relayId) @@ -156,33 +230,107 @@ export class Circuit implements Transport, Initializable { } try { - const virtualConnection = await hop({ - connection: relayConnection, - request: { - type: CircuitPB.Type.HOP, - srcPeer: { - id: this.components.getPeerId().toBytes(), - addrs: this.components.getAddressManager().getAddresses().map(addr => addr.bytes) - }, - dstPeer: { - id: destinationPeer.toBytes(), - addrs: [new Multiaddr(destinationAddr).bytes] - } - } - }) + const stream = await relayConnection.newStream([protocolIDv2Hop, RELAY_V1_CODEC]) - const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.components.getPeerId().toString()}`) + switch (stream.protocol) { + case RELAY_V1_CODEC: return await this.connectV1({ + stream: stream.stream, + connection: relayConnection, + destinationPeer, + destinationAddr, + relayAddr, + ma, + disconnectOnFailure + }) + case protocolIDv2Hop: return await this.connectV2({ + stream: stream.stream, + connection: relayConnection, + destinationPeer, + destinationAddr, + relayAddr, + ma, + disconnectOnFailure + }) + default: + stream.stream.reset() + throw new Error('Unexpected stream protocol') + } + } catch (err: any) { + log.error('Circuit relay dial failed', err) + disconnectOnFailure && await relayConnection.close() + throw err + } + } + + async connectV1 ({ + stream, connection, destinationPeer, + destinationAddr, relayAddr, ma, + disconnectOnFailure + }: ConnectOptions + ) { + const virtualConnection = await CircuitV1Handler.hop({ + stream, + request: { + type: CircuitV1.CircuitRelay.Type.HOP, + srcPeer: { + id: this.components.getPeerId().toBytes(), + addrs: this.components.getAddressManager().getListenAddrs().map(addr => addr.bytes) + }, + dstPeer: { + id: destinationPeer.toBytes(), + addrs: [new Multiaddr(destinationAddr).bytes] + } + } + }) + + const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.components.getPeerId().toString()}`) + const maConn = streamToMaConnection({ + stream: virtualConnection, + remoteAddr: ma, + localAddr + }) + log('new outbound connection %s', maConn.remoteAddr) + + return await this.components.getUpgrader().upgradeOutbound(maConn) + } + + async connectV2 ( + { + stream, connection, destinationPeer, + destinationAddr, relayAddr, ma, + disconnectOnFailure + }: ConnectOptions + ) { + try { + const streamHandler = new StreamHandlerV2({ stream }) + streamHandler.write(CircuitV2.HopMessage.encode({ + type: CircuitV2.HopMessage.Type.CONNECT, + peer: { + id: destinationPeer.toBytes(), + addrs: [new Multiaddr(destinationAddr).bytes] + } + }).finish()) + + const status = CircuitV2.HopMessage.decode(await streamHandler.read()) + if (status.status !== CircuitV2.Status.OK) { + throw createError(new Error('failed to connect via relay with status ' + status.status.toString()), codes.ERR_HOP_REQUEST_FAILED) + } + + // TODO: do something with limit and transient connection + + let localAddr = relayAddr + localAddr = localAddr.encapsulate(`/p2p-circuit/p2p/${this.components.getPeerId().toString()}`) const maConn = streamToMaConnection({ - stream: virtualConnection, + stream: streamHandler.rest(), remoteAddr: ma, localAddr }) log('new outbound connection %s', maConn.remoteAddr) - - return await this.components.getUpgrader().upgradeOutbound(maConn) - } catch (err: any) { + const conn = await this.components.getUpgrader().upgradeOutbound(maConn) + return conn + } catch (/** @type {any} */ err) { log.error('Circuit relay dial failed', err) - disconnectOnFailure && await relayConnection.close() + disconnectOnFailure && await connection.close() throw err } } diff --git a/src/circuit/v1/hop.js b/src/circuit/v1/hop.js deleted file mode 100644 index 321caa62..00000000 --- a/src/circuit/v1/hop.js +++ /dev/null @@ -1,203 +0,0 @@ -'use strict' - -const debug = require('debug') -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 { codes: Errors } = require('../../errors') - -const { stop } = require('./stop') - -const multicodec = require('../multicodec') - -/** - * @typedef {import('./protocol').ICircuitRelay} ICircuitRelay - * @typedef {import('libp2p-interfaces/src/connection').Connection} Connection - * @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream - * @typedef {import('../transport')} Transport - */ - -/** - * @typedef {Object} HopRequest - * @property {Connection} connection - * @property {ICircuitRelay} request - * @property {StreamHandler} streamHandler - * @property {Transport} circuit - */ - -/** - * @param {HopRequest} options - * @returns {Promise} - */ -async function handleHop ({ - connection, - request, - streamHandler, - circuit -}) { - // Ensure hop is enabled - if (!circuit._options.hop.enabled) { - log('HOP request received but we are not acting as a relay') - return streamHandler.end({ - type: CircuitPB.Type.STATUS, - code: CircuitPB.Status.HOP_CANT_SPEAK_RELAY - }) - } - - // Validate the HOP request has the required input - try { - validateAddrs(request, streamHandler) - } catch (/** @type {any} */ err) { - return log.error('invalid hop request via peer %s', connection.remotePeer.toB58String(), err) - } - - if (!request.dstPeer) { - log('HOP request received but we do not receive a dstPeer') - return - } - - // Get the connection to the destination (stop) peer - const destinationPeer = new PeerId(request.dstPeer.id) - - const destinationConnection = circuit._connectionManager.get(destinationPeer) - if (!destinationConnection && !circuit._options.hop.active) { - log('HOP request received but we are not connected to the destination peer') - return streamHandler.end({ - type: CircuitPB.Type.STATUS, - code: CircuitPB.Status.HOP_NO_CONN_TO_DST - }) - } - - // TODO: Handle being an active relay - if (!destinationConnection) { - return - } - - // Handle the incoming HOP request by performing a STOP request - const stopRequest = { - type: CircuitPB.Type.STOP, - dstPeer: request.dstPeer, - srcPeer: request.srcPeer - } - - let destinationStream - try { - destinationStream = await stop({ - connection: destinationConnection, - request: stopRequest - }) - } catch (/** @type {any} */ err) { - return log.error(err) - } - - log('hop request from %s is valid', connection.remotePeer.toB58String()) - streamHandler.write({ - type: CircuitPB.Type.STATUS, - code: CircuitPB.Status.SUCCESS - }) - const sourceStream = streamHandler.rest() - - // Short circuit the two streams to create the relayed connection - return pipe( - sourceStream, - destinationStream, - sourceStream - ) -} - -/** - * Performs a HOP request to a relay peer, to request a connection to another - * peer. A new, virtual, connection will be created between the two via the relay. - * - * @param {object} options - * @param {MuxedStream} options.stream - Stream to the relay - * @param {ICircuitRelay} options.request - * @returns {Promise} - */ -async function hop ({ - stream, - request -}) { - // Send the HOP request - const streamHandler = new StreamHandler({ stream }) - streamHandler.write(request) - - const response = await streamHandler.read() - - if (!response) { - throw errCode(new Error('HOP request had no response'), Errors.ERR_HOP_REQUEST_FAILED) - } - - if (response.code === CircuitPB.Status.SUCCESS) { - log('hop request was successful') - return streamHandler.rest() - } - - log('hop request failed with code %d, closing stream', response.code) - streamHandler.close() - throw errCode(new Error(`HOP request failed with code ${response.code}`), Errors.ERR_HOP_REQUEST_FAILED) -} - -/** - * Performs a CAN_HOP request to a relay peer, in order to understand its capabilities. - * - * @param {object} options - * @param {Connection} options.connection - Connection to the relay - * @returns {Promise} - */ -async function canHop ({ - connection -}) { - // Create a new stream to the relay - const { stream } = await connection.newStream([multicodec.relayV1]) - // Send the HOP request - const streamHandler = new StreamHandler({ stream }) - streamHandler.write({ - type: CircuitPB.Type.CAN_HOP - }) - - const response = await streamHandler.read() - await streamHandler.close() - - if (!response || response.code !== CircuitPB.Status.SUCCESS) { - return false - } - - return true -} - -/** - * Creates an unencoded CAN_HOP response based on the Circuits configuration - * - * @param {Object} options - * @param {Connection} options.connection - * @param {StreamHandler} options.streamHandler - * @param {Transport} options.circuit - * @private - */ -function handleCanHop ({ - connection, - streamHandler, - circuit -}) { - const canHop = circuit._options.hop.enabled - log('can hop (%s) request from %s', canHop, connection.remotePeer.toB58String()) - streamHandler.end({ - type: CircuitPB.Type.STATUS, - code: canHop ? CircuitPB.Status.SUCCESS : CircuitPB.Status.HOP_CANT_SPEAK_RELAY - }) -} - -module.exports = { - handleHop, - hop, - canHop, - handleCanHop -} diff --git a/src/circuit/v1/hop.ts b/src/circuit/v1/hop.ts index 5440813a..3e77dc17 100644 --- a/src/circuit/v1/hop.ts +++ b/src/circuit/v1/hop.ts @@ -1,13 +1,13 @@ import { logger } from '@libp2p/logger' import errCode from 'err-code' import { validateAddrs } from './utils.js' -import { StreamHandler } from './stream-handler.js' -import { CircuitRelay as CircuitPB, ICircuitRelay } from '../pb/index.js' +import { StreamHandlerV1 } from './stream-handler.js' +import { CircuitRelay as CircuitPB, ICircuitRelay } from './pb/index.js' import { pipe } from 'it-pipe' import { codes as Errors } from '../../errors.js' import { stop } from './stop.js' -import { RELAY_CODEC } from '../multicodec.js' -import type { Connection } from '@libp2p/interfaces/connection' +import { RELAY_V1_CODEC } from '../multicodec.js' +import type { Connection, Stream } from '@libp2p/interfaces/connection' import { peerIdFromBytes } from '@libp2p/peer-id' import type { Duplex } from 'it-stream-types' import type { Circuit } from '../transport.js' @@ -18,7 +18,7 @@ const log = logger('libp2p:circuit:hop') export interface HopRequest { connection: Connection request: ICircuitRelay - streamHandler: StreamHandler + streamHandler: StreamHandlerV1 circuit: Circuit connectionManager: ConnectionManager } @@ -119,7 +119,7 @@ export async function handleHop (hopRequest: HopRequest) { } export interface HopConfig { - connection: Connection + stream: Stream request: ICircuitRelay } @@ -129,14 +129,12 @@ export interface HopConfig { */ export async function hop (options: HopConfig): Promise> { const { - connection, + stream, request } = options - // Create a new stream to the relay - const { stream } = await connection.newStream(RELAY_CODEC) // Send the HOP request - const streamHandler = new StreamHandler({ stream }) + const streamHandler = new StreamHandlerV1({ stream }) streamHandler.write(request) const response = await streamHandler.read() @@ -169,10 +167,10 @@ export async function canHop (options: CanHopOptions) { } = options // Create a new stream to the relay - const { stream } = await connection.newStream(RELAY_CODEC) + const { stream } = await connection.newStream(RELAY_V1_CODEC) // Send the HOP request - const streamHandler = new StreamHandler({ stream }) + const streamHandler = new StreamHandlerV1({ stream }) streamHandler.write({ type: CircuitPB.Type.CAN_HOP }) @@ -189,7 +187,7 @@ export async function canHop (options: CanHopOptions) { export interface HandleCanHopOptions { connection: Connection - streamHandler: StreamHandler + streamHandler: StreamHandlerV1 circuit: Circuit } diff --git a/src/circuit/v1/index.ts b/src/circuit/v1/index.ts new file mode 100644 index 00000000..27a393b2 --- /dev/null +++ b/src/circuit/v1/index.ts @@ -0,0 +1,2 @@ +export * from './hop.js' +export * from './stop.js' diff --git a/src/circuit/pb/index.d.ts b/src/circuit/v1/pb/index.d.ts similarity index 100% rename from src/circuit/pb/index.d.ts rename to src/circuit/v1/pb/index.d.ts diff --git a/src/circuit/pb/index.js b/src/circuit/v1/pb/index.js similarity index 100% rename from src/circuit/pb/index.js rename to src/circuit/v1/pb/index.js diff --git a/src/circuit/pb/index.proto b/src/circuit/v1/pb/index.proto similarity index 100% rename from src/circuit/pb/index.proto rename to src/circuit/v1/pb/index.proto diff --git a/src/circuit/v1/protocol/index.d.ts b/src/circuit/v1/protocol/index.d.ts deleted file mode 100644 index 68e4880b..00000000 --- a/src/circuit/v1/protocol/index.d.ts +++ /dev/null @@ -1,173 +0,0 @@ -import * as $protobuf from "protobufjs"; -/** Properties of a CircuitRelay. */ -export interface ICircuitRelay { - - /** CircuitRelay type */ - type?: (CircuitRelay.Type|null); - - /** CircuitRelay srcPeer */ - srcPeer?: (CircuitRelay.IPeer|null); - - /** CircuitRelay dstPeer */ - dstPeer?: (CircuitRelay.IPeer|null); - - /** CircuitRelay code */ - code?: (CircuitRelay.Status|null); -} - -/** Represents a CircuitRelay. */ -export class CircuitRelay implements ICircuitRelay { - - /** - * Constructs a new CircuitRelay. - * @param [p] Properties to set - */ - constructor(p?: ICircuitRelay); - - /** CircuitRelay type. */ - public type: CircuitRelay.Type; - - /** CircuitRelay srcPeer. */ - public srcPeer?: (CircuitRelay.IPeer|null); - - /** CircuitRelay dstPeer. */ - public dstPeer?: (CircuitRelay.IPeer|null); - - /** CircuitRelay code. */ - public code: CircuitRelay.Status; - - /** - * Encodes the specified CircuitRelay message. Does not implicitly {@link CircuitRelay.verify|verify} messages. - * @param m CircuitRelay message or plain object to encode - * @param [w] Writer to encode to - * @returns Writer - */ - public static encode(m: ICircuitRelay, w?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a CircuitRelay message from the specified reader or buffer. - * @param r Reader or buffer to decode from - * @param [l] Message length if known beforehand - * @returns CircuitRelay - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): CircuitRelay; - - /** - * Creates a CircuitRelay message from a plain object. Also converts values to their respective internal types. - * @param d Plain object - * @returns CircuitRelay - */ - public static fromObject(d: { [k: string]: any }): CircuitRelay; - - /** - * Creates a plain object from a CircuitRelay message. Also converts values to other types if specified. - * @param m CircuitRelay - * @param [o] Conversion options - * @returns Plain object - */ - public static toObject(m: CircuitRelay, o?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this CircuitRelay to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; -} - -export namespace CircuitRelay { - - /** Status enum. */ - enum Status { - SUCCESS = 100, - HOP_SRC_ADDR_TOO_LONG = 220, - HOP_DST_ADDR_TOO_LONG = 221, - HOP_SRC_MULTIADDR_INVALID = 250, - HOP_DST_MULTIADDR_INVALID = 251, - HOP_NO_CONN_TO_DST = 260, - HOP_CANT_DIAL_DST = 261, - HOP_CANT_OPEN_DST_STREAM = 262, - HOP_CANT_SPEAK_RELAY = 270, - HOP_CANT_RELAY_TO_SELF = 280, - STOP_SRC_ADDR_TOO_LONG = 320, - STOP_DST_ADDR_TOO_LONG = 321, - STOP_SRC_MULTIADDR_INVALID = 350, - STOP_DST_MULTIADDR_INVALID = 351, - STOP_RELAY_REFUSED = 390, - MALFORMED_MESSAGE = 400 - } - - /** Type enum. */ - enum Type { - HOP = 1, - STOP = 2, - STATUS = 3, - CAN_HOP = 4 - } - - /** Properties of a Peer. */ - interface IPeer { - - /** Peer id */ - id: Uint8Array; - - /** Peer addrs */ - addrs?: (Uint8Array[]|null); - } - - /** Represents a Peer. */ - class Peer implements IPeer { - - /** - * Constructs a new Peer. - * @param [p] Properties to set - */ - constructor(p?: CircuitRelay.IPeer); - - /** Peer id. */ - public id: Uint8Array; - - /** Peer addrs. */ - public addrs: Uint8Array[]; - - /** - * Encodes the specified Peer message. Does not implicitly {@link CircuitRelay.Peer.verify|verify} messages. - * @param m Peer message or plain object to encode - * @param [w] Writer to encode to - * @returns Writer - */ - public static encode(m: CircuitRelay.IPeer, w?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a Peer message from the specified reader or buffer. - * @param r Reader or buffer to decode from - * @param [l] Message length if known beforehand - * @returns Peer - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): CircuitRelay.Peer; - - /** - * Creates a Peer message from a plain object. Also converts values to their respective internal types. - * @param d Plain object - * @returns Peer - */ - public static fromObject(d: { [k: string]: any }): CircuitRelay.Peer; - - /** - * Creates a plain object from a Peer message. Also converts values to other types if specified. - * @param m Peer - * @param [o] Conversion options - * @returns Plain object - */ - public static toObject(m: CircuitRelay.Peer, o?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this Peer to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - } -} diff --git a/src/circuit/v1/protocol/index.js b/src/circuit/v1/protocol/index.js deleted file mode 100644 index cabdc3bd..00000000 --- a/src/circuit/v1/protocol/index.js +++ /dev/null @@ -1,1653 +0,0 @@ -/*eslint-disable*/ -"use strict"; - -var $protobuf = require("protobufjs/minimal"); - -// Common aliases -var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; - -// Exported root namespace -var $root = $protobuf.roots["libp2p-circuit"] || ($protobuf.roots["libp2p-circuit"] = {}); - -$root.CircuitRelay = (function() { - - /** - * Properties of a CircuitRelay. - * @exports ICircuitRelay - * @interface ICircuitRelay - * @property {CircuitRelay.Type|null} [type] CircuitRelay type - * @property {CircuitRelay.IPeer|null} [srcPeer] CircuitRelay srcPeer - * @property {CircuitRelay.IPeer|null} [dstPeer] CircuitRelay dstPeer - * @property {CircuitRelay.Status|null} [code] CircuitRelay code - */ - - /** - * Constructs a new CircuitRelay. - * @exports CircuitRelay - * @classdesc Represents a CircuitRelay. - * @implements ICircuitRelay - * @constructor - * @param {ICircuitRelay=} [p] Properties to set - */ - function CircuitRelay(p) { - if (p) - for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) - if (p[ks[i]] != null) - this[ks[i]] = p[ks[i]]; - } - - /** - * CircuitRelay type. - * @member {CircuitRelay.Type} type - * @memberof CircuitRelay - * @instance - */ - CircuitRelay.prototype.type = 1; - - /** - * CircuitRelay srcPeer. - * @member {CircuitRelay.IPeer|null|undefined} srcPeer - * @memberof CircuitRelay - * @instance - */ - CircuitRelay.prototype.srcPeer = null; - - /** - * CircuitRelay dstPeer. - * @member {CircuitRelay.IPeer|null|undefined} dstPeer - * @memberof CircuitRelay - * @instance - */ - CircuitRelay.prototype.dstPeer = null; - - /** - * CircuitRelay code. - * @member {CircuitRelay.Status} code - * @memberof CircuitRelay - * @instance - */ - CircuitRelay.prototype.code = 100; - - /** - * Encodes the specified CircuitRelay message. Does not implicitly {@link CircuitRelay.verify|verify} messages. - * @function encode - * @memberof CircuitRelay - * @static - * @param {ICircuitRelay} m CircuitRelay message or plain object to encode - * @param {$protobuf.Writer} [w] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - CircuitRelay.encode = function encode(m, w) { - if (!w) - w = $Writer.create(); - if (m.type != null && Object.hasOwnProperty.call(m, "type")) - w.uint32(8).int32(m.type); - if (m.srcPeer != null && Object.hasOwnProperty.call(m, "srcPeer")) - $root.CircuitRelay.Peer.encode(m.srcPeer, w.uint32(18).fork()).ldelim(); - if (m.dstPeer != null && Object.hasOwnProperty.call(m, "dstPeer")) - $root.CircuitRelay.Peer.encode(m.dstPeer, w.uint32(26).fork()).ldelim(); - if (m.code != null && Object.hasOwnProperty.call(m, "code")) - w.uint32(32).int32(m.code); - return w; - }; - - /** - * Decodes a CircuitRelay message from the specified reader or buffer. - * @function decode - * @memberof CircuitRelay - * @static - * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from - * @param {number} [l] Message length if known beforehand - * @returns {CircuitRelay} CircuitRelay - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - CircuitRelay.decode = function decode(r, l) { - if (!(r instanceof $Reader)) - r = $Reader.create(r); - var c = l === undefined ? r.len : r.pos + l, m = new $root.CircuitRelay(); - while (r.pos < c) { - var t = r.uint32(); - switch (t >>> 3) { - case 1: - m.type = r.int32(); - break; - case 2: - m.srcPeer = $root.CircuitRelay.Peer.decode(r, r.uint32()); - break; - case 3: - m.dstPeer = $root.CircuitRelay.Peer.decode(r, r.uint32()); - break; - case 4: - m.code = r.int32(); - break; - default: - r.skipType(t & 7); - break; - } - } - return m; - }; - - /** - * Creates a CircuitRelay message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof CircuitRelay - * @static - * @param {Object.} d Plain object - * @returns {CircuitRelay} CircuitRelay - */ - CircuitRelay.fromObject = function fromObject(d) { - if (d instanceof $root.CircuitRelay) - return d; - var m = new $root.CircuitRelay(); - switch (d.type) { - case "HOP": - case 1: - m.type = 1; - break; - case "STOP": - case 2: - m.type = 2; - break; - case "STATUS": - case 3: - m.type = 3; - break; - case "CAN_HOP": - case 4: - m.type = 4; - break; - } - if (d.srcPeer != null) { - if (typeof d.srcPeer !== "object") - throw TypeError(".CircuitRelay.srcPeer: object expected"); - m.srcPeer = $root.CircuitRelay.Peer.fromObject(d.srcPeer); - } - if (d.dstPeer != null) { - if (typeof d.dstPeer !== "object") - throw TypeError(".CircuitRelay.dstPeer: object expected"); - m.dstPeer = $root.CircuitRelay.Peer.fromObject(d.dstPeer); - } - switch (d.code) { - case "SUCCESS": - case 100: - m.code = 100; - break; - case "HOP_SRC_ADDR_TOO_LONG": - case 220: - m.code = 220; - break; - case "HOP_DST_ADDR_TOO_LONG": - case 221: - m.code = 221; - break; - case "HOP_SRC_MULTIADDR_INVALID": - case 250: - m.code = 250; - break; - case "HOP_DST_MULTIADDR_INVALID": - case 251: - m.code = 251; - break; - case "HOP_NO_CONN_TO_DST": - case 260: - m.code = 260; - break; - case "HOP_CANT_DIAL_DST": - case 261: - m.code = 261; - break; - case "HOP_CANT_OPEN_DST_STREAM": - case 262: - m.code = 262; - break; - case "HOP_CANT_SPEAK_RELAY": - case 270: - m.code = 270; - break; - case "HOP_CANT_RELAY_TO_SELF": - case 280: - m.code = 280; - break; - case "STOP_SRC_ADDR_TOO_LONG": - case 320: - m.code = 320; - break; - case "STOP_DST_ADDR_TOO_LONG": - case 321: - m.code = 321; - break; - case "STOP_SRC_MULTIADDR_INVALID": - case 350: - m.code = 350; - break; - case "STOP_DST_MULTIADDR_INVALID": - case 351: - m.code = 351; - break; - case "STOP_RELAY_REFUSED": - case 390: - m.code = 390; - break; - case "MALFORMED_MESSAGE": - case 400: - m.code = 400; - break; - } - return m; - }; - - /** - * Creates a plain object from a CircuitRelay message. Also converts values to other types if specified. - * @function toObject - * @memberof CircuitRelay - * @static - * @param {CircuitRelay} m CircuitRelay - * @param {$protobuf.IConversionOptions} [o] Conversion options - * @returns {Object.} Plain object - */ - CircuitRelay.toObject = function toObject(m, o) { - if (!o) - o = {}; - var d = {}; - if (o.defaults) { - d.type = o.enums === String ? "HOP" : 1; - d.srcPeer = null; - d.dstPeer = null; - d.code = o.enums === String ? "SUCCESS" : 100; - } - if (m.type != null && m.hasOwnProperty("type")) { - d.type = o.enums === String ? $root.CircuitRelay.Type[m.type] : m.type; - } - if (m.srcPeer != null && m.hasOwnProperty("srcPeer")) { - d.srcPeer = $root.CircuitRelay.Peer.toObject(m.srcPeer, o); - } - if (m.dstPeer != null && m.hasOwnProperty("dstPeer")) { - d.dstPeer = $root.CircuitRelay.Peer.toObject(m.dstPeer, o); - } - if (m.code != null && m.hasOwnProperty("code")) { - d.code = o.enums === String ? $root.CircuitRelay.Status[m.code] : m.code; - } - return d; - }; - - /** - * Converts this CircuitRelay to JSON. - * @function toJSON - * @memberof CircuitRelay - * @instance - * @returns {Object.} JSON object - */ - CircuitRelay.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Status enum. - * @name CircuitRelay.Status - * @enum {number} - * @property {number} SUCCESS=100 SUCCESS value - * @property {number} HOP_SRC_ADDR_TOO_LONG=220 HOP_SRC_ADDR_TOO_LONG value - * @property {number} HOP_DST_ADDR_TOO_LONG=221 HOP_DST_ADDR_TOO_LONG value - * @property {number} HOP_SRC_MULTIADDR_INVALID=250 HOP_SRC_MULTIADDR_INVALID value - * @property {number} HOP_DST_MULTIADDR_INVALID=251 HOP_DST_MULTIADDR_INVALID value - * @property {number} HOP_NO_CONN_TO_DST=260 HOP_NO_CONN_TO_DST value - * @property {number} HOP_CANT_DIAL_DST=261 HOP_CANT_DIAL_DST value - * @property {number} HOP_CANT_OPEN_DST_STREAM=262 HOP_CANT_OPEN_DST_STREAM value - * @property {number} HOP_CANT_SPEAK_RELAY=270 HOP_CANT_SPEAK_RELAY value - * @property {number} HOP_CANT_RELAY_TO_SELF=280 HOP_CANT_RELAY_TO_SELF value - * @property {number} STOP_SRC_ADDR_TOO_LONG=320 STOP_SRC_ADDR_TOO_LONG value - * @property {number} STOP_DST_ADDR_TOO_LONG=321 STOP_DST_ADDR_TOO_LONG value - * @property {number} STOP_SRC_MULTIADDR_INVALID=350 STOP_SRC_MULTIADDR_INVALID value - * @property {number} STOP_DST_MULTIADDR_INVALID=351 STOP_DST_MULTIADDR_INVALID value - * @property {number} STOP_RELAY_REFUSED=390 STOP_RELAY_REFUSED value - * @property {number} MALFORMED_MESSAGE=400 MALFORMED_MESSAGE value - */ - CircuitRelay.Status = (function() { - var valuesById = {}, values = Object.create(valuesById); - values[valuesById[100] = "SUCCESS"] = 100; - values[valuesById[220] = "HOP_SRC_ADDR_TOO_LONG"] = 220; - values[valuesById[221] = "HOP_DST_ADDR_TOO_LONG"] = 221; - values[valuesById[250] = "HOP_SRC_MULTIADDR_INVALID"] = 250; - values[valuesById[251] = "HOP_DST_MULTIADDR_INVALID"] = 251; - values[valuesById[260] = "HOP_NO_CONN_TO_DST"] = 260; - values[valuesById[261] = "HOP_CANT_DIAL_DST"] = 261; - values[valuesById[262] = "HOP_CANT_OPEN_DST_STREAM"] = 262; - values[valuesById[270] = "HOP_CANT_SPEAK_RELAY"] = 270; - values[valuesById[280] = "HOP_CANT_RELAY_TO_SELF"] = 280; - values[valuesById[320] = "STOP_SRC_ADDR_TOO_LONG"] = 320; - values[valuesById[321] = "STOP_DST_ADDR_TOO_LONG"] = 321; - values[valuesById[350] = "STOP_SRC_MULTIADDR_INVALID"] = 350; - values[valuesById[351] = "STOP_DST_MULTIADDR_INVALID"] = 351; - values[valuesById[390] = "STOP_RELAY_REFUSED"] = 390; - values[valuesById[400] = "MALFORMED_MESSAGE"] = 400; - return values; - })(); - - /** - * Type enum. - * @name CircuitRelay.Type - * @enum {number} - * @property {number} HOP=1 HOP value - * @property {number} STOP=2 STOP value - * @property {number} STATUS=3 STATUS value - * @property {number} CAN_HOP=4 CAN_HOP value - */ - CircuitRelay.Type = (function() { - var valuesById = {}, values = Object.create(valuesById); - values[valuesById[1] = "HOP"] = 1; - values[valuesById[2] = "STOP"] = 2; - values[valuesById[3] = "STATUS"] = 3; - values[valuesById[4] = "CAN_HOP"] = 4; - return values; - })(); - - CircuitRelay.Peer = (function() { - - /** - * Properties of a Peer. - * @memberof CircuitRelay - * @interface IPeer - * @property {Uint8Array} id Peer id - * @property {Array.|null} [addrs] Peer addrs - */ - - /** - * Constructs a new Peer. - * @memberof CircuitRelay - * @classdesc Represents a Peer. - * @implements IPeer - * @constructor - * @param {CircuitRelay.IPeer=} [p] Properties to set - */ - function Peer(p) { - this.addrs = []; - if (p) - for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) - if (p[ks[i]] != null) - this[ks[i]] = p[ks[i]]; - } - - /** - * Peer id. - * @member {Uint8Array} id - * @memberof CircuitRelay.Peer - * @instance - */ - Peer.prototype.id = $util.newBuffer([]); - - /** - * Peer addrs. - * @member {Array.} addrs - * @memberof CircuitRelay.Peer - * @instance - */ - Peer.prototype.addrs = $util.emptyArray; - - /** - * Encodes the specified Peer message. Does not implicitly {@link CircuitRelay.Peer.verify|verify} messages. - * @function encode - * @memberof CircuitRelay.Peer - * @static - * @param {CircuitRelay.IPeer} m Peer message or plain object to encode - * @param {$protobuf.Writer} [w] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Peer.encode = function encode(m, w) { - if (!w) - w = $Writer.create(); - w.uint32(10).bytes(m.id); - if (m.addrs != null && m.addrs.length) { - for (var i = 0; i < m.addrs.length; ++i) - w.uint32(18).bytes(m.addrs[i]); - } - return w; - }; - - /** - * Decodes a Peer message from the specified reader or buffer. - * @function decode - * @memberof CircuitRelay.Peer - * @static - * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from - * @param {number} [l] Message length if known beforehand - * @returns {CircuitRelay.Peer} Peer - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Peer.decode = function decode(r, l) { - if (!(r instanceof $Reader)) - r = $Reader.create(r); - var c = l === undefined ? r.len : r.pos + l, m = new $root.CircuitRelay.Peer(); - while (r.pos < c) { - var t = r.uint32(); - switch (t >>> 3) { - case 1: - m.id = r.bytes(); - break; - case 2: - if (!(m.addrs && m.addrs.length)) - m.addrs = []; - m.addrs.push(r.bytes()); - break; - default: - r.skipType(t & 7); - break; - } - } - if (!m.hasOwnProperty("id")) - throw $util.ProtocolError("missing required 'id'", { instance: m }); - return m; - }; - - /** - * Creates a Peer message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof CircuitRelay.Peer - * @static - * @param {Object.} d Plain object - * @returns {CircuitRelay.Peer} Peer - */ - Peer.fromObject = function fromObject(d) { - if (d instanceof $root.CircuitRelay.Peer) - return d; - var m = new $root.CircuitRelay.Peer(); - if (d.id != null) { - if (typeof d.id === "string") - $util.base64.decode(d.id, m.id = $util.newBuffer($util.base64.length(d.id)), 0); - else if (d.id.length) - m.id = d.id; - } - if (d.addrs) { - if (!Array.isArray(d.addrs)) - throw TypeError(".CircuitRelay.Peer.addrs: array expected"); - m.addrs = []; - for (var i = 0; i < d.addrs.length; ++i) { - if (typeof d.addrs[i] === "string") - $util.base64.decode(d.addrs[i], m.addrs[i] = $util.newBuffer($util.base64.length(d.addrs[i])), 0); - else if (d.addrs[i].length) - m.addrs[i] = d.addrs[i]; - } - } - return m; - }; - - /** - * Creates a plain object from a Peer message. Also converts values to other types if specified. - * @function toObject - * @memberof CircuitRelay.Peer - * @static - * @param {CircuitRelay.Peer} m Peer - * @param {$protobuf.IConversionOptions} [o] Conversion options - * @returns {Object.} Plain object - */ - Peer.toObject = function toObject(m, o) { - if (!o) - o = {}; - var d = {}; - if (o.arrays || o.defaults) { - d.addrs = []; - } - if (o.defaults) { - if (o.bytes === String) - d.id = ""; - else { - d.id = []; - if (o.bytes !== Array) - d.id = $util.newBuffer(d.id); - } - } - if (m.id != null && m.hasOwnProperty("id")) { - d.id = o.bytes === String ? $util.base64.encode(m.id, 0, m.id.length) : o.bytes === Array ? Array.prototype.slice.call(m.id) : m.id; - } - if (m.addrs && m.addrs.length) { - d.addrs = []; - for (var j = 0; j < m.addrs.length; ++j) { - d.addrs[j] = o.bytes === String ? $util.base64.encode(m.addrs[j], 0, m.addrs[j].length) : o.bytes === Array ? Array.prototype.slice.call(m.addrs[j]) : m.addrs[j]; - } - } - return d; - }; - - /** - * Converts this Peer to JSON. - * @function toJSON - * @memberof CircuitRelay.Peer - * @instance - * @returns {Object.} JSON object - */ - Peer.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return Peer; - })(); - - return CircuitRelay; -})(); - -$root.HopMessage = (function() { - - /** - * Properties of a HopMessage. - * @exports IHopMessage - * @interface IHopMessage - * @property {HopMessage.Type} type HopMessage type - * @property {IPeer|null} [peer] HopMessage peer - * @property {IReservation|null} [reservation] HopMessage reservation - * @property {ILimit|null} [limit] HopMessage limit - * @property {Status|null} [status] HopMessage status - */ - - /** - * Constructs a new HopMessage. - * @exports HopMessage - * @classdesc Represents a HopMessage. - * @implements IHopMessage - * @constructor - * @param {IHopMessage=} [p] Properties to set - */ - function HopMessage(p) { - if (p) - for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) - if (p[ks[i]] != null) - this[ks[i]] = p[ks[i]]; - } - - /** - * HopMessage type. - * @member {HopMessage.Type} type - * @memberof HopMessage - * @instance - */ - HopMessage.prototype.type = 0; - - /** - * HopMessage peer. - * @member {IPeer|null|undefined} peer - * @memberof HopMessage - * @instance - */ - HopMessage.prototype.peer = null; - - /** - * HopMessage reservation. - * @member {IReservation|null|undefined} reservation - * @memberof HopMessage - * @instance - */ - HopMessage.prototype.reservation = null; - - /** - * HopMessage limit. - * @member {ILimit|null|undefined} limit - * @memberof HopMessage - * @instance - */ - HopMessage.prototype.limit = null; - - /** - * HopMessage status. - * @member {Status} status - * @memberof HopMessage - * @instance - */ - HopMessage.prototype.status = 100; - - /** - * Encodes the specified HopMessage message. Does not implicitly {@link HopMessage.verify|verify} messages. - * @function encode - * @memberof HopMessage - * @static - * @param {IHopMessage} m HopMessage message or plain object to encode - * @param {$protobuf.Writer} [w] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - HopMessage.encode = function encode(m, w) { - if (!w) - w = $Writer.create(); - w.uint32(8).int32(m.type); - if (m.peer != null && Object.hasOwnProperty.call(m, "peer")) - $root.Peer.encode(m.peer, w.uint32(18).fork()).ldelim(); - if (m.reservation != null && Object.hasOwnProperty.call(m, "reservation")) - $root.Reservation.encode(m.reservation, w.uint32(26).fork()).ldelim(); - if (m.limit != null && Object.hasOwnProperty.call(m, "limit")) - $root.Limit.encode(m.limit, w.uint32(34).fork()).ldelim(); - if (m.status != null && Object.hasOwnProperty.call(m, "status")) - w.uint32(40).int32(m.status); - return w; - }; - - /** - * Decodes a HopMessage message from the specified reader or buffer. - * @function decode - * @memberof HopMessage - * @static - * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from - * @param {number} [l] Message length if known beforehand - * @returns {HopMessage} HopMessage - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - HopMessage.decode = function decode(r, l) { - if (!(r instanceof $Reader)) - r = $Reader.create(r); - var c = l === undefined ? r.len : r.pos + l, m = new $root.HopMessage(); - while (r.pos < c) { - var t = r.uint32(); - switch (t >>> 3) { - case 1: - m.type = r.int32(); - break; - case 2: - m.peer = $root.Peer.decode(r, r.uint32()); - break; - case 3: - m.reservation = $root.Reservation.decode(r, r.uint32()); - break; - case 4: - m.limit = $root.Limit.decode(r, r.uint32()); - break; - case 5: - m.status = r.int32(); - break; - default: - r.skipType(t & 7); - break; - } - } - if (!m.hasOwnProperty("type")) - throw $util.ProtocolError("missing required 'type'", { instance: m }); - return m; - }; - - /** - * Creates a HopMessage message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof HopMessage - * @static - * @param {Object.} d Plain object - * @returns {HopMessage} HopMessage - */ - HopMessage.fromObject = function fromObject(d) { - if (d instanceof $root.HopMessage) - return d; - var m = new $root.HopMessage(); - switch (d.type) { - case "RESERVE": - case 0: - m.type = 0; - break; - case "CONNECT": - case 1: - m.type = 1; - break; - case "STATUS": - case 2: - m.type = 2; - break; - } - if (d.peer != null) { - if (typeof d.peer !== "object") - throw TypeError(".HopMessage.peer: object expected"); - m.peer = $root.Peer.fromObject(d.peer); - } - if (d.reservation != null) { - if (typeof d.reservation !== "object") - throw TypeError(".HopMessage.reservation: object expected"); - m.reservation = $root.Reservation.fromObject(d.reservation); - } - if (d.limit != null) { - if (typeof d.limit !== "object") - throw TypeError(".HopMessage.limit: object expected"); - m.limit = $root.Limit.fromObject(d.limit); - } - switch (d.status) { - case "OK": - case 100: - m.status = 100; - break; - case "RESERVATION_REFUSED": - case 200: - m.status = 200; - break; - case "RESOURCE_LIMIT_EXCEEDED": - case 201: - m.status = 201; - break; - case "PERMISSION_DENIED": - case 202: - m.status = 202; - break; - case "CONNECTION_FAILED": - case 203: - m.status = 203; - break; - case "NO_RESERVATION": - case 204: - m.status = 204; - break; - case "MALFORMED_MESSAGE": - case 400: - m.status = 400; - break; - case "UNEXPECTED_MESSAGE": - case 401: - m.status = 401; - break; - } - return m; - }; - - /** - * Creates a plain object from a HopMessage message. Also converts values to other types if specified. - * @function toObject - * @memberof HopMessage - * @static - * @param {HopMessage} m HopMessage - * @param {$protobuf.IConversionOptions} [o] Conversion options - * @returns {Object.} Plain object - */ - HopMessage.toObject = function toObject(m, o) { - if (!o) - o = {}; - var d = {}; - if (o.defaults) { - d.type = o.enums === String ? "RESERVE" : 0; - d.peer = null; - d.reservation = null; - d.limit = null; - d.status = o.enums === String ? "OK" : 100; - } - if (m.type != null && m.hasOwnProperty("type")) { - d.type = o.enums === String ? $root.HopMessage.Type[m.type] : m.type; - } - if (m.peer != null && m.hasOwnProperty("peer")) { - d.peer = $root.Peer.toObject(m.peer, o); - } - if (m.reservation != null && m.hasOwnProperty("reservation")) { - d.reservation = $root.Reservation.toObject(m.reservation, o); - } - if (m.limit != null && m.hasOwnProperty("limit")) { - d.limit = $root.Limit.toObject(m.limit, o); - } - if (m.status != null && m.hasOwnProperty("status")) { - d.status = o.enums === String ? $root.Status[m.status] : m.status; - } - return d; - }; - - /** - * Converts this HopMessage to JSON. - * @function toJSON - * @memberof HopMessage - * @instance - * @returns {Object.} JSON object - */ - HopMessage.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Type enum. - * @name HopMessage.Type - * @enum {number} - * @property {number} RESERVE=0 RESERVE value - * @property {number} CONNECT=1 CONNECT value - * @property {number} STATUS=2 STATUS value - */ - HopMessage.Type = (function() { - var valuesById = {}, values = Object.create(valuesById); - values[valuesById[0] = "RESERVE"] = 0; - values[valuesById[1] = "CONNECT"] = 1; - values[valuesById[2] = "STATUS"] = 2; - return values; - })(); - - return HopMessage; -})(); - -$root.StopMessage = (function() { - - /** - * Properties of a StopMessage. - * @exports IStopMessage - * @interface IStopMessage - * @property {StopMessage.Type} type StopMessage type - * @property {IPeer|null} [peer] StopMessage peer - * @property {ILimit|null} [limit] StopMessage limit - * @property {Status|null} [status] StopMessage status - */ - - /** - * Constructs a new StopMessage. - * @exports StopMessage - * @classdesc Represents a StopMessage. - * @implements IStopMessage - * @constructor - * @param {IStopMessage=} [p] Properties to set - */ - function StopMessage(p) { - if (p) - for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) - if (p[ks[i]] != null) - this[ks[i]] = p[ks[i]]; - } - - /** - * StopMessage type. - * @member {StopMessage.Type} type - * @memberof StopMessage - * @instance - */ - StopMessage.prototype.type = 0; - - /** - * StopMessage peer. - * @member {IPeer|null|undefined} peer - * @memberof StopMessage - * @instance - */ - StopMessage.prototype.peer = null; - - /** - * StopMessage limit. - * @member {ILimit|null|undefined} limit - * @memberof StopMessage - * @instance - */ - StopMessage.prototype.limit = null; - - /** - * StopMessage status. - * @member {Status} status - * @memberof StopMessage - * @instance - */ - StopMessage.prototype.status = 100; - - /** - * Encodes the specified StopMessage message. Does not implicitly {@link StopMessage.verify|verify} messages. - * @function encode - * @memberof StopMessage - * @static - * @param {IStopMessage} m StopMessage message or plain object to encode - * @param {$protobuf.Writer} [w] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - StopMessage.encode = function encode(m, w) { - if (!w) - w = $Writer.create(); - w.uint32(8).int32(m.type); - if (m.peer != null && Object.hasOwnProperty.call(m, "peer")) - $root.Peer.encode(m.peer, w.uint32(18).fork()).ldelim(); - if (m.limit != null && Object.hasOwnProperty.call(m, "limit")) - $root.Limit.encode(m.limit, w.uint32(26).fork()).ldelim(); - if (m.status != null && Object.hasOwnProperty.call(m, "status")) - w.uint32(32).int32(m.status); - return w; - }; - - /** - * Decodes a StopMessage message from the specified reader or buffer. - * @function decode - * @memberof StopMessage - * @static - * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from - * @param {number} [l] Message length if known beforehand - * @returns {StopMessage} StopMessage - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - StopMessage.decode = function decode(r, l) { - if (!(r instanceof $Reader)) - r = $Reader.create(r); - var c = l === undefined ? r.len : r.pos + l, m = new $root.StopMessage(); - while (r.pos < c) { - var t = r.uint32(); - switch (t >>> 3) { - case 1: - m.type = r.int32(); - break; - case 2: - m.peer = $root.Peer.decode(r, r.uint32()); - break; - case 3: - m.limit = $root.Limit.decode(r, r.uint32()); - break; - case 4: - m.status = r.int32(); - break; - default: - r.skipType(t & 7); - break; - } - } - if (!m.hasOwnProperty("type")) - throw $util.ProtocolError("missing required 'type'", { instance: m }); - return m; - }; - - /** - * Creates a StopMessage message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof StopMessage - * @static - * @param {Object.} d Plain object - * @returns {StopMessage} StopMessage - */ - StopMessage.fromObject = function fromObject(d) { - if (d instanceof $root.StopMessage) - return d; - var m = new $root.StopMessage(); - switch (d.type) { - case "CONNECT": - case 0: - m.type = 0; - break; - case "STATUS": - case 1: - m.type = 1; - break; - } - if (d.peer != null) { - if (typeof d.peer !== "object") - throw TypeError(".StopMessage.peer: object expected"); - m.peer = $root.Peer.fromObject(d.peer); - } - if (d.limit != null) { - if (typeof d.limit !== "object") - throw TypeError(".StopMessage.limit: object expected"); - m.limit = $root.Limit.fromObject(d.limit); - } - switch (d.status) { - case "OK": - case 100: - m.status = 100; - break; - case "RESERVATION_REFUSED": - case 200: - m.status = 200; - break; - case "RESOURCE_LIMIT_EXCEEDED": - case 201: - m.status = 201; - break; - case "PERMISSION_DENIED": - case 202: - m.status = 202; - break; - case "CONNECTION_FAILED": - case 203: - m.status = 203; - break; - case "NO_RESERVATION": - case 204: - m.status = 204; - break; - case "MALFORMED_MESSAGE": - case 400: - m.status = 400; - break; - case "UNEXPECTED_MESSAGE": - case 401: - m.status = 401; - break; - } - return m; - }; - - /** - * Creates a plain object from a StopMessage message. Also converts values to other types if specified. - * @function toObject - * @memberof StopMessage - * @static - * @param {StopMessage} m StopMessage - * @param {$protobuf.IConversionOptions} [o] Conversion options - * @returns {Object.} Plain object - */ - StopMessage.toObject = function toObject(m, o) { - if (!o) - o = {}; - var d = {}; - if (o.defaults) { - d.type = o.enums === String ? "CONNECT" : 0; - d.peer = null; - d.limit = null; - d.status = o.enums === String ? "OK" : 100; - } - if (m.type != null && m.hasOwnProperty("type")) { - d.type = o.enums === String ? $root.StopMessage.Type[m.type] : m.type; - } - if (m.peer != null && m.hasOwnProperty("peer")) { - d.peer = $root.Peer.toObject(m.peer, o); - } - if (m.limit != null && m.hasOwnProperty("limit")) { - d.limit = $root.Limit.toObject(m.limit, o); - } - if (m.status != null && m.hasOwnProperty("status")) { - d.status = o.enums === String ? $root.Status[m.status] : m.status; - } - return d; - }; - - /** - * Converts this StopMessage to JSON. - * @function toJSON - * @memberof StopMessage - * @instance - * @returns {Object.} JSON object - */ - StopMessage.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Type enum. - * @name StopMessage.Type - * @enum {number} - * @property {number} CONNECT=0 CONNECT value - * @property {number} STATUS=1 STATUS value - */ - StopMessage.Type = (function() { - var valuesById = {}, values = Object.create(valuesById); - values[valuesById[0] = "CONNECT"] = 0; - values[valuesById[1] = "STATUS"] = 1; - return values; - })(); - - return StopMessage; -})(); - -$root.Peer = (function() { - - /** - * Properties of a Peer. - * @exports IPeer - * @interface IPeer - * @property {Uint8Array} id Peer id - * @property {Array.|null} [addrs] Peer addrs - */ - - /** - * Constructs a new Peer. - * @exports Peer - * @classdesc Represents a Peer. - * @implements IPeer - * @constructor - * @param {IPeer=} [p] Properties to set - */ - function Peer(p) { - this.addrs = []; - if (p) - for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) - if (p[ks[i]] != null) - this[ks[i]] = p[ks[i]]; - } - - /** - * Peer id. - * @member {Uint8Array} id - * @memberof Peer - * @instance - */ - Peer.prototype.id = $util.newBuffer([]); - - /** - * Peer addrs. - * @member {Array.} addrs - * @memberof Peer - * @instance - */ - Peer.prototype.addrs = $util.emptyArray; - - /** - * Encodes the specified Peer message. Does not implicitly {@link Peer.verify|verify} messages. - * @function encode - * @memberof Peer - * @static - * @param {IPeer} m Peer message or plain object to encode - * @param {$protobuf.Writer} [w] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Peer.encode = function encode(m, w) { - if (!w) - w = $Writer.create(); - w.uint32(10).bytes(m.id); - if (m.addrs != null && m.addrs.length) { - for (var i = 0; i < m.addrs.length; ++i) - w.uint32(18).bytes(m.addrs[i]); - } - return w; - }; - - /** - * Decodes a Peer message from the specified reader or buffer. - * @function decode - * @memberof Peer - * @static - * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from - * @param {number} [l] Message length if known beforehand - * @returns {Peer} Peer - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Peer.decode = function decode(r, l) { - if (!(r instanceof $Reader)) - r = $Reader.create(r); - var c = l === undefined ? r.len : r.pos + l, m = new $root.Peer(); - while (r.pos < c) { - var t = r.uint32(); - switch (t >>> 3) { - case 1: - m.id = r.bytes(); - break; - case 2: - if (!(m.addrs && m.addrs.length)) - m.addrs = []; - m.addrs.push(r.bytes()); - break; - default: - r.skipType(t & 7); - break; - } - } - if (!m.hasOwnProperty("id")) - throw $util.ProtocolError("missing required 'id'", { instance: m }); - return m; - }; - - /** - * Creates a Peer message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof Peer - * @static - * @param {Object.} d Plain object - * @returns {Peer} Peer - */ - Peer.fromObject = function fromObject(d) { - if (d instanceof $root.Peer) - return d; - var m = new $root.Peer(); - if (d.id != null) { - if (typeof d.id === "string") - $util.base64.decode(d.id, m.id = $util.newBuffer($util.base64.length(d.id)), 0); - else if (d.id.length) - m.id = d.id; - } - if (d.addrs) { - if (!Array.isArray(d.addrs)) - throw TypeError(".Peer.addrs: array expected"); - m.addrs = []; - for (var i = 0; i < d.addrs.length; ++i) { - if (typeof d.addrs[i] === "string") - $util.base64.decode(d.addrs[i], m.addrs[i] = $util.newBuffer($util.base64.length(d.addrs[i])), 0); - else if (d.addrs[i].length) - m.addrs[i] = d.addrs[i]; - } - } - return m; - }; - - /** - * Creates a plain object from a Peer message. Also converts values to other types if specified. - * @function toObject - * @memberof Peer - * @static - * @param {Peer} m Peer - * @param {$protobuf.IConversionOptions} [o] Conversion options - * @returns {Object.} Plain object - */ - Peer.toObject = function toObject(m, o) { - if (!o) - o = {}; - var d = {}; - if (o.arrays || o.defaults) { - d.addrs = []; - } - if (o.defaults) { - if (o.bytes === String) - d.id = ""; - else { - d.id = []; - if (o.bytes !== Array) - d.id = $util.newBuffer(d.id); - } - } - if (m.id != null && m.hasOwnProperty("id")) { - d.id = o.bytes === String ? $util.base64.encode(m.id, 0, m.id.length) : o.bytes === Array ? Array.prototype.slice.call(m.id) : m.id; - } - if (m.addrs && m.addrs.length) { - d.addrs = []; - for (var j = 0; j < m.addrs.length; ++j) { - d.addrs[j] = o.bytes === String ? $util.base64.encode(m.addrs[j], 0, m.addrs[j].length) : o.bytes === Array ? Array.prototype.slice.call(m.addrs[j]) : m.addrs[j]; - } - } - return d; - }; - - /** - * Converts this Peer to JSON. - * @function toJSON - * @memberof Peer - * @instance - * @returns {Object.} JSON object - */ - Peer.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return Peer; -})(); - -$root.Reservation = (function() { - - /** - * Properties of a Reservation. - * @exports IReservation - * @interface IReservation - * @property {number} expire Reservation expire - * @property {Array.|null} [addrs] Reservation addrs - * @property {Uint8Array|null} [voucher] Reservation voucher - */ - - /** - * Constructs a new Reservation. - * @exports Reservation - * @classdesc Represents a Reservation. - * @implements IReservation - * @constructor - * @param {IReservation=} [p] Properties to set - */ - function Reservation(p) { - this.addrs = []; - if (p) - for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) - if (p[ks[i]] != null) - this[ks[i]] = p[ks[i]]; - } - - /** - * Reservation expire. - * @member {number} expire - * @memberof Reservation - * @instance - */ - Reservation.prototype.expire = $util.Long ? $util.Long.fromBits(0,0,true) : 0; - - /** - * Reservation addrs. - * @member {Array.} addrs - * @memberof Reservation - * @instance - */ - Reservation.prototype.addrs = $util.emptyArray; - - /** - * Reservation voucher. - * @member {Uint8Array} voucher - * @memberof Reservation - * @instance - */ - Reservation.prototype.voucher = $util.newBuffer([]); - - /** - * Encodes the specified Reservation message. Does not implicitly {@link Reservation.verify|verify} messages. - * @function encode - * @memberof Reservation - * @static - * @param {IReservation} m Reservation message or plain object to encode - * @param {$protobuf.Writer} [w] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Reservation.encode = function encode(m, w) { - if (!w) - w = $Writer.create(); - w.uint32(8).uint64(m.expire); - if (m.addrs != null && m.addrs.length) { - for (var i = 0; i < m.addrs.length; ++i) - w.uint32(18).bytes(m.addrs[i]); - } - if (m.voucher != null && Object.hasOwnProperty.call(m, "voucher")) - w.uint32(26).bytes(m.voucher); - return w; - }; - - /** - * Decodes a Reservation message from the specified reader or buffer. - * @function decode - * @memberof Reservation - * @static - * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from - * @param {number} [l] Message length if known beforehand - * @returns {Reservation} Reservation - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Reservation.decode = function decode(r, l) { - if (!(r instanceof $Reader)) - r = $Reader.create(r); - var c = l === undefined ? r.len : r.pos + l, m = new $root.Reservation(); - while (r.pos < c) { - var t = r.uint32(); - switch (t >>> 3) { - case 1: - m.expire = r.uint64(); - break; - case 2: - if (!(m.addrs && m.addrs.length)) - m.addrs = []; - m.addrs.push(r.bytes()); - break; - case 3: - m.voucher = r.bytes(); - break; - default: - r.skipType(t & 7); - break; - } - } - if (!m.hasOwnProperty("expire")) - throw $util.ProtocolError("missing required 'expire'", { instance: m }); - return m; - }; - - /** - * Creates a Reservation message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof Reservation - * @static - * @param {Object.} d Plain object - * @returns {Reservation} Reservation - */ - Reservation.fromObject = function fromObject(d) { - if (d instanceof $root.Reservation) - return d; - var m = new $root.Reservation(); - if (d.expire != null) { - if ($util.Long) - (m.expire = $util.Long.fromValue(d.expire)).unsigned = true; - else if (typeof d.expire === "string") - m.expire = parseInt(d.expire, 10); - else if (typeof d.expire === "number") - m.expire = d.expire; - else if (typeof d.expire === "object") - m.expire = new $util.LongBits(d.expire.low >>> 0, d.expire.high >>> 0).toNumber(true); - } - if (d.addrs) { - if (!Array.isArray(d.addrs)) - throw TypeError(".Reservation.addrs: array expected"); - m.addrs = []; - for (var i = 0; i < d.addrs.length; ++i) { - if (typeof d.addrs[i] === "string") - $util.base64.decode(d.addrs[i], m.addrs[i] = $util.newBuffer($util.base64.length(d.addrs[i])), 0); - else if (d.addrs[i].length) - m.addrs[i] = d.addrs[i]; - } - } - if (d.voucher != null) { - if (typeof d.voucher === "string") - $util.base64.decode(d.voucher, m.voucher = $util.newBuffer($util.base64.length(d.voucher)), 0); - else if (d.voucher.length) - m.voucher = d.voucher; - } - return m; - }; - - /** - * Creates a plain object from a Reservation message. Also converts values to other types if specified. - * @function toObject - * @memberof Reservation - * @static - * @param {Reservation} m Reservation - * @param {$protobuf.IConversionOptions} [o] Conversion options - * @returns {Object.} Plain object - */ - Reservation.toObject = function toObject(m, o) { - if (!o) - o = {}; - var d = {}; - if (o.arrays || o.defaults) { - d.addrs = []; - } - if (o.defaults) { - if ($util.Long) { - var n = new $util.Long(0, 0, true); - d.expire = o.longs === String ? n.toString() : o.longs === Number ? n.toNumber() : n; - } else - d.expire = o.longs === String ? "0" : 0; - if (o.bytes === String) - d.voucher = ""; - else { - d.voucher = []; - if (o.bytes !== Array) - d.voucher = $util.newBuffer(d.voucher); - } - } - if (m.expire != null && m.hasOwnProperty("expire")) { - if (typeof m.expire === "number") - d.expire = o.longs === String ? String(m.expire) : m.expire; - else - d.expire = o.longs === String ? $util.Long.prototype.toString.call(m.expire) : o.longs === Number ? new $util.LongBits(m.expire.low >>> 0, m.expire.high >>> 0).toNumber(true) : m.expire; - } - if (m.addrs && m.addrs.length) { - d.addrs = []; - for (var j = 0; j < m.addrs.length; ++j) { - d.addrs[j] = o.bytes === String ? $util.base64.encode(m.addrs[j], 0, m.addrs[j].length) : o.bytes === Array ? Array.prototype.slice.call(m.addrs[j]) : m.addrs[j]; - } - } - if (m.voucher != null && m.hasOwnProperty("voucher")) { - d.voucher = o.bytes === String ? $util.base64.encode(m.voucher, 0, m.voucher.length) : o.bytes === Array ? Array.prototype.slice.call(m.voucher) : m.voucher; - } - return d; - }; - - /** - * Converts this Reservation to JSON. - * @function toJSON - * @memberof Reservation - * @instance - * @returns {Object.} JSON object - */ - Reservation.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return Reservation; -})(); - -$root.Limit = (function() { - - /** - * Properties of a Limit. - * @exports ILimit - * @interface ILimit - * @property {number|null} [duration] Limit duration - * @property {number|null} [data] Limit data - */ - - /** - * Constructs a new Limit. - * @exports Limit - * @classdesc Represents a Limit. - * @implements ILimit - * @constructor - * @param {ILimit=} [p] Properties to set - */ - function Limit(p) { - if (p) - for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) - if (p[ks[i]] != null) - this[ks[i]] = p[ks[i]]; - } - - /** - * Limit duration. - * @member {number} duration - * @memberof Limit - * @instance - */ - Limit.prototype.duration = 0; - - /** - * Limit data. - * @member {number} data - * @memberof Limit - * @instance - */ - Limit.prototype.data = $util.Long ? $util.Long.fromBits(0,0,true) : 0; - - /** - * Encodes the specified Limit message. Does not implicitly {@link Limit.verify|verify} messages. - * @function encode - * @memberof Limit - * @static - * @param {ILimit} m Limit message or plain object to encode - * @param {$protobuf.Writer} [w] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Limit.encode = function encode(m, w) { - if (!w) - w = $Writer.create(); - if (m.duration != null && Object.hasOwnProperty.call(m, "duration")) - w.uint32(8).uint32(m.duration); - if (m.data != null && Object.hasOwnProperty.call(m, "data")) - w.uint32(16).uint64(m.data); - return w; - }; - - /** - * Decodes a Limit message from the specified reader or buffer. - * @function decode - * @memberof Limit - * @static - * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from - * @param {number} [l] Message length if known beforehand - * @returns {Limit} Limit - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Limit.decode = function decode(r, l) { - if (!(r instanceof $Reader)) - r = $Reader.create(r); - var c = l === undefined ? r.len : r.pos + l, m = new $root.Limit(); - while (r.pos < c) { - var t = r.uint32(); - switch (t >>> 3) { - case 1: - m.duration = r.uint32(); - break; - case 2: - m.data = r.uint64(); - break; - default: - r.skipType(t & 7); - break; - } - } - return m; - }; - - /** - * Creates a Limit message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof Limit - * @static - * @param {Object.} d Plain object - * @returns {Limit} Limit - */ - Limit.fromObject = function fromObject(d) { - if (d instanceof $root.Limit) - return d; - var m = new $root.Limit(); - if (d.duration != null) { - m.duration = d.duration >>> 0; - } - if (d.data != null) { - if ($util.Long) - (m.data = $util.Long.fromValue(d.data)).unsigned = true; - else if (typeof d.data === "string") - m.data = parseInt(d.data, 10); - else if (typeof d.data === "number") - m.data = d.data; - else if (typeof d.data === "object") - m.data = new $util.LongBits(d.data.low >>> 0, d.data.high >>> 0).toNumber(true); - } - return m; - }; - - /** - * Creates a plain object from a Limit message. Also converts values to other types if specified. - * @function toObject - * @memberof Limit - * @static - * @param {Limit} m Limit - * @param {$protobuf.IConversionOptions} [o] Conversion options - * @returns {Object.} Plain object - */ - Limit.toObject = function toObject(m, o) { - if (!o) - o = {}; - var d = {}; - if (o.defaults) { - d.duration = 0; - if ($util.Long) { - var n = new $util.Long(0, 0, true); - d.data = o.longs === String ? n.toString() : o.longs === Number ? n.toNumber() : n; - } else - d.data = o.longs === String ? "0" : 0; - } - if (m.duration != null && m.hasOwnProperty("duration")) { - d.duration = m.duration; - } - if (m.data != null && m.hasOwnProperty("data")) { - if (typeof m.data === "number") - d.data = o.longs === String ? String(m.data) : m.data; - else - d.data = o.longs === String ? $util.Long.prototype.toString.call(m.data) : o.longs === Number ? new $util.LongBits(m.data.low >>> 0, m.data.high >>> 0).toNumber(true) : m.data; - } - return d; - }; - - /** - * Converts this Limit to JSON. - * @function toJSON - * @memberof Limit - * @instance - * @returns {Object.} JSON object - */ - Limit.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return Limit; -})(); - -/** - * Status enum. - * @exports Status - * @enum {number} - * @property {number} OK=100 OK value - * @property {number} RESERVATION_REFUSED=200 RESERVATION_REFUSED value - * @property {number} RESOURCE_LIMIT_EXCEEDED=201 RESOURCE_LIMIT_EXCEEDED value - * @property {number} PERMISSION_DENIED=202 PERMISSION_DENIED value - * @property {number} CONNECTION_FAILED=203 CONNECTION_FAILED value - * @property {number} NO_RESERVATION=204 NO_RESERVATION value - * @property {number} MALFORMED_MESSAGE=400 MALFORMED_MESSAGE value - * @property {number} UNEXPECTED_MESSAGE=401 UNEXPECTED_MESSAGE value - */ -$root.Status = (function() { - var valuesById = {}, values = Object.create(valuesById); - values[valuesById[100] = "OK"] = 100; - values[valuesById[200] = "RESERVATION_REFUSED"] = 200; - values[valuesById[201] = "RESOURCE_LIMIT_EXCEEDED"] = 201; - values[valuesById[202] = "PERMISSION_DENIED"] = 202; - values[valuesById[203] = "CONNECTION_FAILED"] = 203; - values[valuesById[204] = "NO_RESERVATION"] = 204; - values[valuesById[400] = "MALFORMED_MESSAGE"] = 400; - values[valuesById[401] = "UNEXPECTED_MESSAGE"] = 401; - return values; -})(); - -module.exports = $root; diff --git a/src/circuit/v1/protocol/index.proto b/src/circuit/v1/protocol/index.proto deleted file mode 100644 index 259ad283..00000000 --- a/src/circuit/v1/protocol/index.proto +++ /dev/null @@ -1,42 +0,0 @@ -syntax = "proto2"; - -message CircuitRelay { - - enum Status { - SUCCESS = 100; - HOP_SRC_ADDR_TOO_LONG = 220; - HOP_DST_ADDR_TOO_LONG = 221; - HOP_SRC_MULTIADDR_INVALID = 250; - HOP_DST_MULTIADDR_INVALID = 251; - HOP_NO_CONN_TO_DST = 260; - HOP_CANT_DIAL_DST = 261; - HOP_CANT_OPEN_DST_STREAM = 262; - HOP_CANT_SPEAK_RELAY = 270; - HOP_CANT_RELAY_TO_SELF = 280; - STOP_SRC_ADDR_TOO_LONG = 320; - STOP_DST_ADDR_TOO_LONG = 321; - STOP_SRC_MULTIADDR_INVALID = 350; - STOP_DST_MULTIADDR_INVALID = 351; - STOP_RELAY_REFUSED = 390; - MALFORMED_MESSAGE = 400; - } - - enum Type { // RPC identifier, either HOP, STOP or STATUS - HOP = 1; - STOP = 2; - STATUS = 3; - CAN_HOP = 4; - } - - message Peer { - required bytes id = 1; // peer id - repeated bytes addrs = 2; // peer's known addresses - } - - optional Type type = 1; // Type of the message - - optional Peer srcPeer = 2; // srcPeer and dstPeer are used when Type is HOP or STATUS - optional Peer dstPeer = 3; - - optional Status code = 4; // Status code, used when Type is STATUS -} diff --git a/src/circuit/v1/stop.js b/src/circuit/v1/stop.js deleted file mode 100644 index c5856a8f..00000000 --- a/src/circuit/v1/stop.js +++ /dev/null @@ -1,81 +0,0 @@ -'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') - -/** - * @typedef {import('libp2p-interfaces/src/connection').Connection} Connection - * @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream - * @typedef {import('./protocol').ICircuitRelay} ICircuitRelay - */ - -/** - * Handles incoming STOP requests - * - * @private - * @param {Object} options - * @param {Connection} options.connection - * @param {ICircuitRelay} options.request - The CircuitRelay protobuf request (unencoded) - * @param {StreamHandler} options.streamHandler - * @returns {Promise|void} Resolves a duplex iterable - */ -module.exports.handleStop = function handleStop ({ - connection, - request, - streamHandler -}) { - // Validate the STOP request has the required input - try { - validateAddrs(request, streamHandler) - } catch (/** @type {any} */ err) { - return log.error('invalid stop request via peer %s', connection.remotePeer.toB58String(), err) - } - - // The request is valid - log('stop request is valid') - streamHandler.write({ - type: CircuitPB.Type.STATUS, - code: CircuitPB.Status.SUCCESS - }) - return streamHandler.rest() -} - -/** - * Creates a STOP request - * - * @private - * @param {Object} options - * @param {Connection} options.connection - * @param {ICircuitRelay} options.request - The CircuitRelay protobuf request (unencoded) - * @returns {Promise} Resolves a duplex iterable - */ -module.exports.stop = async function stop ({ - connection, - request -}) { - const { stream } = await connection.newStream([multicodec.relayV1]) - log('starting stop request to %s', connection.remotePeer.toB58String()) - const streamHandler = new StreamHandler({ stream }) - - streamHandler.write(request) - const response = await streamHandler.read() - - if (!response) { - return streamHandler.close() - } - - if (response.code === CircuitPB.Status.SUCCESS) { - log('stop request to %s was successful', connection.remotePeer.toB58String()) - return streamHandler.rest() - } - - log('stop request failed with code %d', response.code) - streamHandler.close() -} diff --git a/src/circuit/v1/stop.ts b/src/circuit/v1/stop.ts index edef9672..3c120b0e 100644 --- a/src/circuit/v1/stop.ts +++ b/src/circuit/v1/stop.ts @@ -1,7 +1,7 @@ import { logger } from '@libp2p/logger' -import { CircuitRelay as CircuitPB, ICircuitRelay } from '../pb/index.js' -import { RELAY_CODEC } from '../multicodec.js' -import { StreamHandler } from './stream-handler.js' +import { CircuitRelay as CircuitPB, ICircuitRelay } from './pb/index.js' +import { RELAY_V1_CODEC } from '../multicodec.js' +import { StreamHandlerV1 } from './stream-handler.js' import { validateAddrs } from './utils.js' import type { Connection } from '@libp2p/interfaces/connection' import type { Duplex } from 'it-stream-types' @@ -11,7 +11,7 @@ const log = logger('libp2p:circuit:stop') export interface HandleStopOptions { connection: Connection request: ICircuitRelay - streamHandler: StreamHandler + streamHandler: StreamHandlerV1 } /** @@ -56,9 +56,9 @@ export async function stop (options: StopOptions) { request } = options - const { stream } = await connection.newStream([RELAY_CODEC]) + const { stream } = await connection.newStream([RELAY_V1_CODEC]) log('starting stop request to %p', connection.remotePeer) - const streamHandler = new StreamHandler({ stream }) + const streamHandler = new StreamHandlerV1({ stream }) streamHandler.write(request) const response = await streamHandler.read() diff --git a/src/circuit/v1/stream-handler.js b/src/circuit/v1/stream-handler.js deleted file mode 100644 index af9836c7..00000000 --- a/src/circuit/v1/stream-handler.js +++ /dev/null @@ -1,94 +0,0 @@ -'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') -// @ts-ignore it-handshake does not export types -const handshake = require('it-handshake') -const { CircuitRelay } = require('./protocol') - -/** - * @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream - * @typedef {import('./protocol').ICircuitRelay} ICircuitRelay - */ - -class StreamHandler { - /** - * Create a stream handler for connection - * - * @class - * @param {object} options - * @param {MuxedStream} options.stream - A duplex iterable - * @param {number} [options.maxLength = 4096] - max bytes length of message - */ - constructor ({ stream, maxLength = 4096 }) { - this.stream = stream - - this.shake = handshake(this.stream) - // @ts-ignore options are not optional - this.decoder = lp.decode.fromReader(this.shake.reader, { maxDataLength: maxLength }) - } - - /** - * Read and decode message - * - * @async - */ - async read () { - const msg = await this.decoder.next() - if (msg.value) { - const value = CircuitRelay.decode(msg.value.slice()) - log('read message type', value.type) - return value - } - - log('read received no value, closing stream') - // End the stream, we didn't get data - this.close() - } - - /** - * Encode and write array of buffers - * - * @param {ICircuitRelay} 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(CircuitRelay.encode(msg).finish())) - } - - /** - * Return the handshake rest stream and invalidate handler - * - * @returns {*} A duplex iterable - */ - rest () { - this.shake.rest() - return this.shake.stream - } - - /** - * @param {ICircuitRelay} msg - An unencoded CircuitRelay protobuf message - */ - end (msg) { - this.write(msg) - this.close() - } - - /** - * Close the stream - * - * @returns {void} - */ - close () { - log('closing the stream') - this.rest().sink([]) - } -} - -module.exports = StreamHandler diff --git a/src/circuit/v1/stream-handler.ts b/src/circuit/v1/stream-handler.ts index 8b55f36a..3276e9e5 100644 --- a/src/circuit/v1/stream-handler.ts +++ b/src/circuit/v1/stream-handler.ts @@ -1,11 +1,11 @@ import { logger } from '@libp2p/logger' import * as lp from 'it-length-prefixed' import { Handshake, handshake } from 'it-handshake' -import { CircuitRelay, ICircuitRelay } from '../pb/index.js' +import { CircuitRelay, ICircuitRelay } from './pb/index.js' import type { Stream } from '@libp2p/interfaces/connection' import type { Source } from 'it-stream-types' -const log = logger('libp2p:circuit:stream-handler') +const log = logger('libp2p:circuitv1:stream-handler') export interface StreamHandlerOptions { /** @@ -19,7 +19,7 @@ export interface StreamHandlerOptions { maxLength?: number } -export class StreamHandler { +export class StreamHandlerV1 { private readonly stream: Stream private readonly shake: Handshake private readonly decoder: Source diff --git a/src/circuit/v1/utils.js b/src/circuit/v1/utils.js deleted file mode 100644 index 7dfab9c8..00000000 --- a/src/circuit/v1/utils.js +++ /dev/null @@ -1,62 +0,0 @@ -<<<<<<< HEAD:src/circuit/v1/utils.js -'use strict' - -const { Multiaddr } = require('multiaddr') -const { CircuitRelay } = require('./protocol') - -/** - * @typedef {import('./stream-handler')} StreamHandler - * @typedef {import('./protocol').ICircuitRelay} ICircuitRelay - */ - -/** - * Write a response - * - * @param {StreamHandler} streamHandler - * @param {import('./protocol').CircuitRelay.Status} status -======= -import { Multiaddr } from '@multiformats/multiaddr' -import { CircuitRelay, ICircuitRelay } from '../pb/index.js' -import type { StreamHandler } from './stream-handler.js' - -/** - * Write a response ->>>>>>> origin/master:src/circuit/v1/utils.ts - */ -function writeResponse (streamHandler: StreamHandler, status: CircuitRelay.Status) { - streamHandler.write({ - type: CircuitRelay.Type.STATUS, - code: status - }) -} - -/** - * Validate incomming HOP/STOP message - */ -export function validateAddrs (msg: ICircuitRelay, streamHandler: StreamHandler) { - try { - if (msg.dstPeer?.addrs != null) { - msg.dstPeer.addrs.forEach((addr) => { - return new Multiaddr(addr) - }) - } - } catch (err: any) { - writeResponse(streamHandler, msg.type === CircuitRelay.Type.HOP - ? CircuitRelay.Status.HOP_DST_MULTIADDR_INVALID - : CircuitRelay.Status.STOP_DST_MULTIADDR_INVALID) - throw err - } - - try { - if (msg.srcPeer?.addrs != null) { - msg.srcPeer.addrs.forEach((addr) => { - return new Multiaddr(addr) - }) - } - } catch (err: any) { - writeResponse(streamHandler, msg.type === CircuitRelay.Type.HOP - ? CircuitRelay.Status.HOP_SRC_MULTIADDR_INVALID - : CircuitRelay.Status.STOP_SRC_MULTIADDR_INVALID) - throw err - } -} diff --git a/src/circuit/v1/utils.ts b/src/circuit/v1/utils.ts index 7dfab9c8..ab526ac0 100644 --- a/src/circuit/v1/utils.ts +++ b/src/circuit/v1/utils.ts @@ -1,29 +1,11 @@ -<<<<<<< HEAD:src/circuit/v1/utils.js -'use strict' - -const { Multiaddr } = require('multiaddr') -const { CircuitRelay } = require('./protocol') - -/** - * @typedef {import('./stream-handler')} StreamHandler - * @typedef {import('./protocol').ICircuitRelay} ICircuitRelay - */ - -/** - * Write a response - * - * @param {StreamHandler} streamHandler - * @param {import('./protocol').CircuitRelay.Status} status -======= import { Multiaddr } from '@multiformats/multiaddr' -import { CircuitRelay, ICircuitRelay } from '../pb/index.js' -import type { StreamHandler } from './stream-handler.js' +import { CircuitRelay, ICircuitRelay } from './pb/index.js' +import type { StreamHandlerV1 } from './stream-handler.js' /** * Write a response ->>>>>>> origin/master:src/circuit/v1/utils.ts */ -function writeResponse (streamHandler: StreamHandler, status: CircuitRelay.Status) { +function writeResponse (streamHandler: StreamHandlerV1, status: CircuitRelay.Status) { streamHandler.write({ type: CircuitRelay.Type.STATUS, code: status @@ -33,7 +15,7 @@ function writeResponse (streamHandler: StreamHandler, status: CircuitRelay.Statu /** * Validate incomming HOP/STOP message */ -export function validateAddrs (msg: ICircuitRelay, streamHandler: StreamHandler) { +export function validateAddrs (msg: ICircuitRelay, streamHandler: StreamHandlerV1) { try { if (msg.dstPeer?.addrs != null) { msg.dstPeer.addrs.forEach((addr) => { diff --git a/src/circuit/v2/hop.js b/src/circuit/v2/hop.js deleted file mode 100644 index b8490fb1..00000000 --- a/src/circuit/v2/hop.js +++ /dev/null @@ -1,257 +0,0 @@ -'use strict' - -const debug = require('debug') -const { pipe } = require('it-pipe') -const PeerId = require('peer-id') -const Envelope = require('../../record/envelope') -const log = Object.assign(debug('libp2p:circuitv2:hop'), { - error: debug('libp2p:circuitv2:hop:err') -}) -const { HopMessage, Status, StopMessage } = require('./protocol') -const { protocolIDv2Hop } = require('../multicodec') -const { stop } = require('./stop') -const { ReservationVoucherRecord } = require('./reservation-voucher') -const { validateHopConnectRequest } = require('./validation') -const { Multiaddr } = require('multiaddr') -const StreamHandler = require('./stream-handler') - -/** - * @typedef {import('./protocol').IHopMessage} IHopMessage - * @typedef {import('./protocol').IReservation} IReservation - * @typedef {import('./protocol').ILimit} ILimit - * @typedef {import('./interfaces').ReservationStore} ReservationStore - * @typedef {import('./interfaces').Acl} Acl - * @typedef {import('libp2p-interfaces/src/connection').Connection} Connection - * @typedef {import('../transport')} Transport - */ - -/** - * - * @param {Object} options - * @param {Connection} options.connection - * @param {IHopMessage} options.request - * @param {StreamHandler} options.streamHandler - * @param {PeerId} options.relayPeer - * @param {Multiaddr[]}options.relayAddrs - * @param {Transport} options.circuit - * @param {ILimit|null} options.limit - * @param {Acl?} options.acl - * @param {ReservationStore} options.reservationStore - */ -module.exports.handleHopProtocol = async function (options) { - switch (options.request.type) { - case HopMessage.Type.RESERVE: await handleReserve(options); break - case HopMessage.Type.CONNECT: await handleConnect(options); break - default: { - log.error('invalid hop request type %s via peer %s', options.request.type, options.connection.remotePeer.toB58String()) - writeErrorResponse(options.streamHandler, Status.MALFORMED_MESSAGE) - options.streamHandler.close() - } - } -} - -/** - * - * @param {Connection} connection - * @returns - */ -module.exports.reserve = async function (connection) { - log('requesting reservation from %s', connection.remotePeer.toB58String()) - const { stream } = await connection.newStream([protocolIDv2Hop]) - const streamHandler = new StreamHandler({ stream }) - streamHandler.write(HopMessage.encode({ - type: HopMessage.Type.RESERVE - }).finish()) - - let response - try { - response = HopMessage.decode(await streamHandler.read()) - } catch (/** @type {any} */ e) { - log.error('error passing reserve message response from %s because', connection.remotePeer.toB58String(), e.message) - streamHandler.close() - throw e - } - - if (response.status === Status.OK && response.reservation) { - return response.reservation - } - const errMsg = `reservation failed with status ${response.status}` - log.error(errMsg) - throw new Error(errMsg) -} - -/** - * - * @param {Object} options - * @param {Connection} options.connection - * @param {StreamHandler} options.streamHandler - * @param {ReservationStore} options.reservationStore - * @param {PeerId} options.relayPeer - * @param {Multiaddr[]}options.relayAddrs - * @param {Acl?} options.acl - * @param {ILimit?} options.limit - */ -async function handleReserve ({ connection, streamHandler, relayPeer, relayAddrs, limit, acl, reservationStore }) { - log('hop reserve request from %s', connection.remotePeer.toB58String()) - - // TODO: prevent reservation over relay address - - if (acl && acl.allowReserve && !acl.allowReserve(connection.remotePeer, connection.remoteAddr)) { - log.error('acl denied reservation to %s', connection.remotePeer.toB58String()) - writeErrorResponse(streamHandler, Status.PERMISSION_DENIED) - streamHandler.close() - return - } - - const result = await reservationStore.reserve(connection.remotePeer, connection.remoteAddr) - - if (result.status !== Status.OK) { - writeErrorResponse(streamHandler, result.status) - streamHandler.close() - return - } - - try { - writeResponse( - streamHandler, - { - type: HopMessage.Type.STATUS, - reservation: await makeReservation(relayAddrs, relayPeer, connection.remotePeer, result.expire || 0), - limit - }) - log('sent confirmation response to %s', connection.remotePeer.toB58String()) - } catch (err) { - log.error('failed to send confirmation response to %s', connection.remotePeer.toB58String()) - await reservationStore.removeReservation(connection.remotePeer) - } - // TODO: how to ensure connection manager doesn't close reserved relay conn -} - -/** - * - * @param {Object} options - * @param {Connection} options.connection - * @param {IHopMessage} options.request - * @param {ReservationStore} options.reservationStore - * @param {StreamHandler} options.streamHandler - * @param {Transport} options.circuit - * @param {Acl?} options.acl - */ -async function handleConnect ({ connection, streamHandler, request, reservationStore, circuit, acl }) { - log('hop connect request from %s', connection.remotePeer.toB58String()) - // Validate the HOP connect request has the required input - try { - validateHopConnectRequest(request, streamHandler) - } catch (/** @type {any} */ err) { - log.error('invalid hop connect request via peer %s', connection.remotePeer.toB58String(), err) - writeErrorResponse(streamHandler, Status.MALFORMED_MESSAGE) - return - } - - // @ts-ignore peer is defined at this point - const dstPeer = new PeerId(request.peer.id) - - if (acl && acl.allowConnect) { - const status = await acl.allowConnect(connection.remotePeer, connection.remoteAddr, dstPeer) - if (status !== Status.OK) { - log.error('hop connect denied for %s with status %s', connection.remotePeer.toB58String(), status) - writeErrorResponse(streamHandler, status) - return - } - } - - if (!await reservationStore.hasReservation(dstPeer)) { - log.error('hop connect denied for %s with status %s', connection.remotePeer.toB58String(), Status.NO_RESERVATION) - writeErrorResponse(streamHandler, Status.NO_RESERVATION) - return - } - - const destinationConnection = circuit._connectionManager.get(dstPeer) - if (!destinationConnection) { - log('hop connect denied for %s as there is no destination connection', connection.remotePeer.toB58String()) - writeErrorResponse(streamHandler, Status.NO_RESERVATION) - return - } - - log('hop connect request from %s to %s is valid', connection.remotePeer.toB58String(), dstPeer.toB58String()) - - const destinationStream = await stop({ - connection: destinationConnection, - request: { - type: StopMessage.Type.CONNECT, - peer: { - id: connection.remotePeer.id, - addrs: [new Multiaddr('/p2p/' + connection.remotePeer.toB58String()).bytes] - } - } - }) - - if (!destinationStream) { - log.error('failed to open stream to destination peer %s', destinationConnection?.remotePeer.toB58String()) - writeErrorResponse(streamHandler, Status.CONNECTION_FAILED) - return - } - - writeResponse(streamHandler, { type: HopMessage.Type.STATUS, status: Status.OK }) - - const sourceStream = streamHandler.rest() - log('connection to destination established, short circuiting streams...') - // Short circuit the two streams to create the relayed connection - return pipe( - sourceStream, - destinationStream, - sourceStream - ) -} - -/** - * - * @param {Multiaddr[]} relayAddrs - * @param {PeerId} relayPeerId - * @param {PeerId} remotePeer - * @param {number} expire - * @returns {Promise} - */ -async function makeReservation (relayAddrs, relayPeerId, remotePeer, expire) { - const addrs = [] - - for (const relayAddr of relayAddrs) { - addrs.push(relayAddr.bytes) - } - - const voucher = await Envelope.seal(new ReservationVoucherRecord({ - peer: remotePeer, - relay: relayPeerId, - expiration: expire - }), relayPeerId) - - return { - addrs, - expire, - voucher: voucher.marshal() - } -} - -/** - * Write an error response and closes stream - * - * @param {StreamHandler} streamHandler - * @param {import('./protocol').Status} status - */ -function writeErrorResponse (streamHandler, status) { - writeResponse(streamHandler, { - type: HopMessage.Type.STATUS, - status - }) - streamHandler.close() -} - -/** - * Write a response - * - * @param {StreamHandler} streamHandler - * @param {IHopMessage} msg - */ -function writeResponse (streamHandler, msg) { - streamHandler.write(HopMessage.encode(msg).finish()) -} diff --git a/src/circuit/v2/hop.ts b/src/circuit/v2/hop.ts new file mode 100644 index 00000000..d641fd1f --- /dev/null +++ b/src/circuit/v2/hop.ts @@ -0,0 +1,220 @@ +import type { PeerId } from '@libp2p/interfaces/peer-id' +import { RecordEnvelope } from '@libp2p/peer-record' +import { logger } from '@libp2p/logger' +import { pipe } from 'it-pipe' +import type { Connection } from '@libp2p/interfaces/connection' +import { HopMessage, IHopMessage, IReservation, ILimit, Status, StopMessage } from './pb/index.js' +import { StreamHandlerV2 } from './stream-handler.js' +import type { Circuit } from '../transport.js' +import { Multiaddr } from '@multiformats/multiaddr' +import type { Acl, ReservationStore } from './interfaces.js' +import { protocolIDv2Hop } from '../multicodec.js' +import { validateHopConnectRequest } from './validation.js' +import { stop } from './stop.js' +import { ReservationVoucherRecord } from './reservation-voucher.js' +import { peerIdFromBytes } from '@libp2p/peer-id' + +const log = logger('libp2p:circuitv2:hop') + +export interface HopProtocolOptions { + connection: Connection + request: IHopMessage + streamHandler: StreamHandlerV2 + circuit: Circuit + relayPeer: PeerId + relayAddrs: Multiaddr[] + limit?: ILimit + acl?: Acl + reservationStore: ReservationStore +} + +export async function handleHopProtocol (options: HopProtocolOptions) { + switch (options.request.type) { + case HopMessage.Type.RESERVE: await handleReserve(options); break + case HopMessage.Type.CONNECT: await handleConnect(options); break + default: { + log.error('invalid hop request type %s via peer %s', options.request.type, options.connection.remotePeer) + writeErrorResponse(options.streamHandler, Status.MALFORMED_MESSAGE) + options.streamHandler.close() + } + } +} + +export async function reserve (connection: Connection) { + log('requesting reservation from %s', connection.remotePeer) + const { stream } = await connection.newStream([protocolIDv2Hop]) + const streamHandler = new StreamHandlerV2({ stream }) + streamHandler.write(HopMessage.encode({ + type: HopMessage.Type.RESERVE + }).finish()) + + let response + try { + response = HopMessage.decode(await streamHandler.read()) + } catch (e: any) { + log.error('error passing reserve message response from %s because', connection.remotePeer, e.message) + streamHandler.close() + throw e + } + + if (response.status === Status.OK && response.reservation !== null) { + return response.reservation + } + const errMsg = `reservation failed with status ${response.status}` + log.error(errMsg) + throw new Error(errMsg) +} + +async function handleReserve ({ connection, streamHandler, relayPeer, relayAddrs, limit, acl, reservationStore }: HopProtocolOptions) { + log('hop reserve request from %s', connection.remotePeer) + + // TODO: prevent reservation over relay address + + if ((await acl?.allowReserve?.(connection.remotePeer, connection.remoteAddr)) === false) { + log.error('acl denied reservation to %s', connection.remotePeer) + writeErrorResponse(streamHandler, Status.PERMISSION_DENIED) + streamHandler.close() + return + } + + const result = await reservationStore.reserve(connection.remotePeer, connection.remoteAddr) + + if (result.status !== Status.OK) { + writeErrorResponse(streamHandler, result.status) + streamHandler.close() + return + } + + try { + writeResponse( + streamHandler, + { + type: HopMessage.Type.STATUS, + reservation: await makeReservation(relayAddrs, relayPeer, connection.remotePeer, result.expire ?? 0), + limit + }) + log('sent confirmation response to %s', connection.remotePeer) + } catch (err) { + log.error('failed to send confirmation response to %s', connection.remotePeer) + await reservationStore.removeReservation(connection.remotePeer) + } + // TODO: how to ensure connection manager doesn't close reserved relay conn +} + +type HopConnectOptions = Pick< +HopProtocolOptions, +'connection' | 'streamHandler' | 'request' | 'reservationStore' |'circuit' |'acl' +> + +async function handleConnect (options: HopConnectOptions) { + const { connection, streamHandler, request, reservationStore, circuit, acl } = options + log('hop connect request from %s', connection.remotePeer) + // Validate the HOP connect request has the required input + try { + validateHopConnectRequest(request, streamHandler) + } catch (/** @type {any} */ err) { + log.error('invalid hop connect request via peer %s', connection.remotePeer, err) + writeErrorResponse(streamHandler, Status.MALFORMED_MESSAGE) + return + } + + // @ts-expect-error + const dstPeer = peerIdFromBytes(request.peer.id) + + if (acl?.allowConnect !== undefined) { + const status = await acl.allowConnect(connection.remotePeer, connection.remoteAddr, dstPeer) + if (status !== Status.OK) { + log.error('hop connect denied for %s with status %s', connection.remotePeer, status) + writeErrorResponse(streamHandler, status) + return + } + } + + if (!await reservationStore.hasReservation(dstPeer)) { + log.error('hop connect denied for %s with status %s', connection.remotePeer, Status.NO_RESERVATION) + writeErrorResponse(streamHandler, Status.NO_RESERVATION) + return + } + + const destinationConnection = circuit.getPeerConnection(dstPeer) + if (destinationConnection === undefined || destinationConnection === null) { + log('hop connect denied for %s as there is no destination connection', connection.remotePeer) + writeErrorResponse(streamHandler, Status.NO_RESERVATION) + return + } + + log('hop connect request from %s to %s is valid', connection.remotePeer, dstPeer) + + const destinationStream = await stop({ + connection: destinationConnection, + request: { + type: StopMessage.Type.CONNECT, + peer: { + id: connection.remotePeer.toBytes(), + addrs: [new Multiaddr('/p2p/' + connection.remotePeer.toString()).bytes] + } + } + }) + + if (destinationStream === undefined || destinationStream === null) { + log.error('failed to open stream to destination peer %s', destinationConnection?.remotePeer) + writeErrorResponse(streamHandler, Status.CONNECTION_FAILED) + return + } + + writeResponse(streamHandler, { type: HopMessage.Type.STATUS, status: Status.OK }) + + const sourceStream = streamHandler.rest() + log('connection to destination established, short circuiting streams...') + // Short circuit the two streams to create the relayed connection + return await pipe( + sourceStream, + destinationStream, + sourceStream + ) +} + +async function makeReservation ( + relayAddrs: Multiaddr[], + relayPeerId: PeerId, + remotePeer: PeerId, + expire: number +): Promise { + const addrs = [] + + for (const relayAddr of relayAddrs) { + addrs.push(relayAddr.bytes) + } + + const voucher = await RecordEnvelope.seal(new ReservationVoucherRecord({ + peer: remotePeer, + relay: relayPeerId, + expiration: expire + }), relayPeerId) + + return { + addrs, + expire, + voucher: voucher.marshal() + } +} + +/** + * Write an error response and closes stream + * + */ +function writeErrorResponse (streamHandler: StreamHandlerV2, status: Status) { + writeResponse(streamHandler, { + type: HopMessage.Type.STATUS, + status + }) + streamHandler.close() +} + +/** + * Write a response + * + */ +function writeResponse (streamHandler: StreamHandlerV2, msg: IHopMessage) { + streamHandler.write(HopMessage.encode(msg).finish()) +} diff --git a/src/circuit/v2/index.ts b/src/circuit/v2/index.ts new file mode 100644 index 00000000..27a393b2 --- /dev/null +++ b/src/circuit/v2/index.ts @@ -0,0 +1,2 @@ +export * from './hop.js' +export * from './stop.js' diff --git a/src/circuit/v2/interfaces.d.ts b/src/circuit/v2/interfaces.ts similarity index 68% rename from src/circuit/v2/interfaces.d.ts rename to src/circuit/v2/interfaces.ts index 8f967651..31faf241 100644 --- a/src/circuit/v2/interfaces.d.ts +++ b/src/circuit/v2/interfaces.ts @@ -1,8 +1,8 @@ -import { PeerId } from 'peer-id' -import { Multiaddr } from 'multiaddr' -import { HopMessage, Status } from './protocol' +import type { PeerId } from '@libp2p/interfaces/peer-id' +import type { Multiaddr } from '@multiformats/multiaddr' +import type { Status } from './pb/index.js' -type ReservationStatus = Status.OK | Status.PERMISSION_DENIED | Status.RESERVATION_REFUSED +export type ReservationStatus = Status.OK | Status.PERMISSION_DENIED | Status.RESERVATION_REFUSED export interface ReservationStore { reserve: (peer: PeerId, addr: Multiaddr) => Promise<{status: ReservationStatus, expire?: number}> diff --git a/src/circuit/v2/protocol/index.d.ts b/src/circuit/v2/pb/index.d.ts similarity index 100% rename from src/circuit/v2/protocol/index.d.ts rename to src/circuit/v2/pb/index.d.ts diff --git a/src/circuit/v2/protocol/index.js b/src/circuit/v2/pb/index.js similarity index 97% rename from src/circuit/v2/protocol/index.js rename to src/circuit/v2/pb/index.js index 1bd4b6a3..0300a1b5 100644 --- a/src/circuit/v2/protocol/index.js +++ b/src/circuit/v2/pb/index.js @@ -1,15 +1,17 @@ /*eslint-disable*/ -"use strict"; +import $protobuf from "protobufjs/minimal.js"; -var $protobuf = require("protobufjs/minimal"); +// @ts-expect-error Explicitly disable long.js support +$protobuf.util.Long = undefined; +$protobuf.configure(); // Common aliases -var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; +const $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; // Exported root namespace -var $root = $protobuf.roots["libp2p-circuit"] || ($protobuf.roots["libp2p-circuit"] = {}); +const $root = $protobuf.roots["libp2p-circuitv2"] || ($protobuf.roots["libp2p-circuitv2"] = {}); -$root.HopMessage = (function() { +export const HopMessage = $root.HopMessage = (() => { /** * Properties of a HopMessage. @@ -280,7 +282,7 @@ $root.HopMessage = (function() { * @property {number} STATUS=2 STATUS value */ HopMessage.Type = (function() { - var valuesById = {}, values = Object.create(valuesById); + const valuesById = {}, values = Object.create(valuesById); values[valuesById[0] = "RESERVE"] = 0; values[valuesById[1] = "CONNECT"] = 1; values[valuesById[2] = "STATUS"] = 2; @@ -290,7 +292,7 @@ $root.HopMessage = (function() { return HopMessage; })(); -$root.StopMessage = (function() { +export const StopMessage = $root.StopMessage = (() => { /** * Properties of a StopMessage. @@ -533,7 +535,7 @@ $root.StopMessage = (function() { * @property {number} STATUS=1 STATUS value */ StopMessage.Type = (function() { - var valuesById = {}, values = Object.create(valuesById); + const valuesById = {}, values = Object.create(valuesById); values[valuesById[0] = "CONNECT"] = 0; values[valuesById[1] = "STATUS"] = 1; return values; @@ -542,7 +544,7 @@ $root.StopMessage = (function() { return StopMessage; })(); -$root.Peer = (function() { +export const Peer = $root.Peer = (() => { /** * Properties of a Peer. @@ -723,7 +725,7 @@ $root.Peer = (function() { return Peer; })(); -$root.Reservation = (function() { +export const Reservation = $root.Reservation = (() => { /** * Properties of a Reservation. @@ -939,7 +941,7 @@ $root.Reservation = (function() { return Reservation; })(); -$root.Limit = (function() { +export const Limit = $root.Limit = (() => { /** * Properties of a Limit. @@ -1119,8 +1121,8 @@ $root.Limit = (function() { * @property {number} MALFORMED_MESSAGE=400 MALFORMED_MESSAGE value * @property {number} UNEXPECTED_MESSAGE=401 UNEXPECTED_MESSAGE value */ -$root.Status = (function() { - var valuesById = {}, values = Object.create(valuesById); +export const Status = $root.Status = (() => { + const valuesById = {}, values = Object.create(valuesById); values[valuesById[100] = "OK"] = 100; values[valuesById[200] = "RESERVATION_REFUSED"] = 200; values[valuesById[201] = "RESOURCE_LIMIT_EXCEEDED"] = 201; @@ -1132,7 +1134,7 @@ $root.Status = (function() { return values; })(); -$root.ReservationVoucher = (function() { +export const ReservationVoucher = $root.ReservationVoucher = (() => { /** * Properties of a ReservationVoucher. @@ -1341,4 +1343,4 @@ $root.ReservationVoucher = (function() { return ReservationVoucher; })(); -module.exports = $root; +export { $root as default }; diff --git a/src/circuit/v2/protocol/index.proto b/src/circuit/v2/pb/index.proto similarity index 98% rename from src/circuit/v2/protocol/index.proto rename to src/circuit/v2/pb/index.proto index 3361dc70..852bdc6c 100644 --- a/src/circuit/v2/protocol/index.proto +++ b/src/circuit/v2/pb/index.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + message HopMessage { enum Type { RESERVE = 0; diff --git a/src/circuit/v2/reservation-store.js b/src/circuit/v2/reservation-store.js deleted file mode 100644 index a88de3ce..00000000 --- a/src/circuit/v2/reservation-store.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict' - -const { Status } = require('./protocol') - -/** - * @typedef {import('./interfaces').ReservationStore} IReservationStore - * @typedef {import('./interfaces').ReservationStatus} ReservationStatus - * @typedef {import('multiaddr').Multiaddr} Multiaddr - * @typedef {import('peer-id')} PeerId - */ - -/** - * @implements IReservationStore - */ -class ReservationStore { - constructor (limit = 15) { - /** - * PeerId => - */ - this._reservations = new Map() - this._limit = limit - } - - /** - * @typedef {Object} Result - * @property {ReservationStatus} status - * @property {number|undefined} expire - */ - - /** - * - * @param {PeerId} peer - * @param {Multiaddr} addr - * @returns {Promise} - */ - async reserve (peer, addr) { - if (this._reservations.size >= this._limit && !this._reservations.has(peer.toB58String())) { - return { status: Status.RESERVATION_REFUSED, expire: undefined } - } - const expire = new Date() - this._reservations.set(peer.toB58String(), { addr, expire }) - return { status: Status.OK, expire: expire.getTime() } - } - - /** - * @param {PeerId} peer - */ - async removeReservation (peer) { - this._reservations.delete(peer.toB58String()) - } - - /** - * - * @param {PeerId} dst - * @returns {Promise} - */ - async hasReservation (dst) { - return this._reservations.has(dst.toB58String()) - } -} - -module.exports = ReservationStore diff --git a/src/circuit/v2/reservation-store.ts b/src/circuit/v2/reservation-store.ts new file mode 100644 index 00000000..be9162bf --- /dev/null +++ b/src/circuit/v2/reservation-store.ts @@ -0,0 +1,33 @@ +import { Status } from './pb/index.js' +import type { ReservationStore as IReservationStore, ReservationStatus } from './interfaces.js' +import type { Multiaddr } from '@multiformats/multiaddr' +import type { PeerId } from '@libp2p/interfaces/peer-id' + +interface Reservation { + addr: Multiaddr + expire: Date +} + +export class ReservationStore implements IReservationStore { + private readonly reservations = new Map() + + constructor (private readonly limit = 15) { + } + + async reserve (peer: PeerId, addr: Multiaddr): Promise<{status: ReservationStatus, expire?: number}> { + if (this.reservations.size >= this.limit && !this.reservations.has(peer.toString())) { + return { status: Status.RESERVATION_REFUSED, expire: undefined } + } + const expire = new Date() + this.reservations.set(peer.toString(), { addr, expire }) + return { status: Status.OK, expire: expire.getTime() } + } + + async removeReservation (peer: PeerId) { + this.reservations.delete(peer.toString()) + } + + async hasReservation (dst: PeerId) { + return this.reservations.has(dst.toString()) + } +} diff --git a/src/circuit/v2/reservation-voucher.js b/src/circuit/v2/reservation-voucher.js deleted file mode 100644 index c9a03b16..00000000 --- a/src/circuit/v2/reservation-voucher.js +++ /dev/null @@ -1,66 +0,0 @@ -'use strict' - -const { ReservationVoucher: Protobuf } = require('./protocol') - -/** - * @typedef {import('libp2p-interfaces/src/record/types').Record} Record - * @typedef {import('peer-id')} PeerId - */ - -/** - * @implements Record - */ -class ReservationVoucherRecord { - /** - * 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 {PeerId} params.relay - * @param {PeerId} params.peer - * @param {number} params.expiration - */ - constructor ({ relay, peer, expiration }) { - this.domain = 'libp2p-relay-rsvp' - this.codec = new Uint8Array([0x03, 0x02]) - - this.relay = relay - this.peer = peer - this.expiration = expiration - } - - marshal () { - return Protobuf.encode({ - relay: this.relay.toBytes(), - peer: this.peer.toBytes(), - expiration: this.expiration - }).finish() - } - - /** - * - * @param {this} other - * @returns - */ - equals (other) { - if (!(other instanceof ReservationVoucherRecord)) { - return false - } - if (!this.peer.equals(other.peer)) { - return false - } - - if (!this.relay.equals(other.relay)) { - return false - } - - if (this.expiration !== other.expiration) { - return false - } - - return true - } -} - -module.exports.ReservationVoucherRecord = ReservationVoucherRecord diff --git a/src/circuit/v2/reservation-voucher.ts b/src/circuit/v2/reservation-voucher.ts new file mode 100644 index 00000000..ea9a15a9 --- /dev/null +++ b/src/circuit/v2/reservation-voucher.ts @@ -0,0 +1,51 @@ +import type { PeerId } from '@libp2p/interfaces/peer-id' +import type { Record } from '@libp2p/interfaces/record' +import { ReservationVoucher } from './pb/index.js' + +export interface ReservationVoucherOptions { + relay: PeerId + peer: PeerId + expiration: number +} + +export class ReservationVoucherRecord implements Record { + public readonly domain = 'libp2p-relay-rsvp' + public readonly codec = new Uint8Array([0x03, 0x02]) + + private readonly relay: PeerId + private readonly peer: PeerId + private readonly expiration: number + + constructor ({ relay, peer, expiration }: ReservationVoucherOptions) { + this.relay = relay + this.peer = peer + this.expiration = expiration + } + + marshal () { + return ReservationVoucher.encode({ + relay: this.relay.toBytes(), + peer: this.peer.toBytes(), + expiration: this.expiration + }).finish() + } + + equals (other: Record) { + if (!(other instanceof ReservationVoucherRecord)) { + return false + } + if (!this.peer.equals(other.peer)) { + return false + } + + if (!this.relay.equals(other.relay)) { + return false + } + + if (this.expiration !== other.expiration) { + return false + } + + return true + } +} diff --git a/src/circuit/v2/stop.js b/src/circuit/v2/stop.js deleted file mode 100644 index 1126b7a7..00000000 --- a/src/circuit/v2/stop.js +++ /dev/null @@ -1,89 +0,0 @@ -'use strict' - -const debug = require('debug') -const log = Object.assign(debug('libp2p:circuitv2:stop'), { - error: debug('libp2p:circuitv2:stop:err') -}) -const multicodec = require('../multicodec') -const StreamHandler = require('./stream-handler') -const { StopMessage, Status } = require('./protocol') -const { validateStopConnectRequest } = require('./validation') - -/** - * @typedef {import('libp2p-interfaces/src/connection').Connection} Connection - * @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream - * @typedef {import('./protocol').IStopMessage} IStopMessage - */ - -/** - * Handles incoming STOP requests - * - * @private - * @param {Object} options - * @param {Connection} options.connection - * @param {IStopMessage} options.request - The StopMessage protobuf request (unencoded) - * @param {StreamHandler} options.streamHandler - * @returns {Promise} Resolves a duplex iterable - */ -module.exports.handleStop = async function stopHandler ({ - connection, - request, - streamHandler -}) { - log('new circuit relay v2 stop stream from %s', connection.remotePeer.toB58String()) - // Validate the STOP request has the required input - try { - validateStopConnectRequest(request, streamHandler) - } catch (/** @type {any} */ err) { - return log.error('invalid stop connect request via peer %s', connection.remotePeer.toB58String(), err) - } - log('stop request is valid') - - // TODO: go-libp2p marks connection transient if there is limit field present in request. - // Cannot find any reference to transient connections in js-libp2p - - streamHandler.write(StopMessage.encode( - { - type: StopMessage.Type.STATUS, - status: Status.OK - } - ).finish()) - return streamHandler.rest() -} - -/** - * Creates a STOP request - * - * @private - * @param {Object} options - * @param {Connection} options.connection - * @param {IStopMessage} options.request - The StopMessage protobuf request (unencoded) - * @returns {Promise} Resolves a duplex iterable - */ -module.exports.stop = async function stop ({ - connection, - request -}) { - const { stream } = await connection.newStream([multicodec.protocolIDv2Stop]) - log('starting circuit relay v2 stop request to %s', connection.remotePeer.toB58String()) - const streamHandler = new StreamHandler({ stream }) - streamHandler.write(StopMessage.encode(request).finish()) - let response - try { - response = StopMessage.decode(await streamHandler.read()) - } catch (/** @type {any} */ err) { - log.error('error parsing stop message response from %s', connection.remotePeer.toB58String()) - } - - if (!response) { - return streamHandler.close() - } - - if (response.status === Status.OK) { - log('stop request to %s was successful', connection.remotePeer.toB58String()) - return streamHandler.rest() - } - - log('stop request failed with code %d', response.status) - streamHandler.close() -} diff --git a/src/circuit/v2/stop.ts b/src/circuit/v2/stop.ts new file mode 100644 index 00000000..e39cf335 --- /dev/null +++ b/src/circuit/v2/stop.ts @@ -0,0 +1,79 @@ + +import { IStopMessage, Status, StopMessage } from './pb/index.js' +import type { Connection } from '@libp2p/interfaces/connection' + +import { logger } from '@libp2p/logger' +import { StreamHandlerV2 } from './stream-handler.js' +import { protocolIDv2Stop } from '../multicodec.js' +import { validateStopConnectRequest } from './validation.js' + +const log = logger('libp2p:circuitv2:stop') + +export interface HandleStopOptions { + connection: Connection + request: IStopMessage + streamHandler: StreamHandlerV2 +} + +export async function handleStop ({ + connection, + request, + streamHandler +}: HandleStopOptions) { + log('new circuit relay v2 stop stream from %s', connection.remotePeer) + // Validate the STOP request has the required input + try { + validateStopConnectRequest(request, streamHandler) + } catch (/** @type {any} */ err) { + return log.error('invalid stop connect request via peer %s', connection.remotePeer, err) + } + log('stop request is valid') + + // TODO: go-libp2p marks connection transient if there is limit field present in request. + // Cannot find any reference to transient connections in js-libp2p + + streamHandler.write(StopMessage.encode( + { + type: StopMessage.Type.STATUS, + status: Status.OK + } + ).finish()) + return streamHandler.rest() +} + +export interface StopOptions { + connection: Connection + request: IStopMessage +} + +/** + * Creates a STOP request + * + */ +export async function stop ({ + connection, + request +}: StopOptions) { + const { stream } = await connection.newStream([protocolIDv2Stop]) + log('starting circuit relay v2 stop request to %s', connection.remotePeer) + const streamHandler = new StreamHandlerV2({ stream }) + streamHandler.write(StopMessage.encode(request).finish()) + let response + try { + response = StopMessage.decode(await streamHandler.read()) + } catch (/** @type {any} */ err) { + log.error('error parsing stop message response from %s', connection.remotePeer) + } + + if (response == null) { + return streamHandler.close() + } + + if (response.status === Status.OK) { + log('stop request to %s was successful', connection.remotePeer) + return streamHandler.rest() + } + + log('stop request failed with code %d', response.status) + streamHandler.close() +} diff --git a/src/circuit/v2/stream-handler.js b/src/circuit/v2/stream-handler.js deleted file mode 100644 index d6f63e51..00000000 --- a/src/circuit/v2/stream-handler.js +++ /dev/null @@ -1,90 +0,0 @@ -'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') -// @ts-ignore it-handshake does not export types -const handshake = require('it-handshake') - -/** - * @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream - */ - -class StreamHandler { - /** - * Create a stream handler for connection - * - * @class - * @param {object} options - * @param {MuxedStream} options.stream - A duplex iterable - * @param {number} [options.maxLength = 4096] - max bytes length of message - */ - constructor ({ stream, maxLength = 4096 }) { - this.stream = stream - - this.shake = handshake(this.stream) - // @ts-ignore options are not optional - this.decoder = lp.decode.fromReader(this.shake.reader, { maxDataLength: maxLength }) - } - - /** - * Read and decode message - * - * @async - */ - async read () { - const msg = await this.decoder.next() - if (msg.value) { - return msg.value.slice() - } - - log('read received no value, closing stream') - // End the stream, we didn't get data - this.close() - } - - /** - * Encode and write array of buffers - * - * @param {Uint8Array} msg - An encoded Uint8Array protobuf message - * @returns {void} - */ - write (msg) { - // @ts-ignore lp.encode expects type type 'Buffer | BufferList', not 'Uint8Array' - this.shake.write(lp.encode.single(msg)) - } - - /** - * Return the handshake rest stream and invalidate handler - * - * @returns {*} A duplex iterable - */ - rest () { - this.shake.rest() - return this.shake.stream - } - - /** - * @param {Uint8Array} msg - An encoded Uint8Array protobuf message - */ - end (msg) { - this.write(msg) - this.close() - } - - /** - * Close the stream - * - * @returns {void} - */ - close () { - log('closing the stream') - this.rest().sink([]) - } -} - -module.exports.StreamHandler = StreamHandler -module.exports = StreamHandler diff --git a/src/circuit/v2/stream-handler.ts b/src/circuit/v2/stream-handler.ts new file mode 100644 index 00000000..14402035 --- /dev/null +++ b/src/circuit/v2/stream-handler.ts @@ -0,0 +1,81 @@ +import { logger } from '@libp2p/logger' +import * as lp from 'it-length-prefixed' +import { Handshake, handshake } from 'it-handshake' +import type { Stream } from '@libp2p/interfaces/connection' +import type { Source } from 'it-stream-types' + +const log = logger('libp2p:circuitv2:stream-handler') + +export interface StreamHandlerOptions { + /** + * A duplex iterable + */ + stream: Stream + + /** + * max bytes length of message + */ + maxLength?: number +} + +export class StreamHandlerV2 { + private readonly stream: Stream + private readonly shake: Handshake + private readonly decoder: Source + + constructor (options: StreamHandlerOptions) { + const { stream, maxLength = 4096 } = options + + this.stream = stream + this.shake = handshake(this.stream) + this.decoder = lp.decode.fromReader(this.shake.reader, { maxDataLength: maxLength }) + } + + /** + * Read and decode message + * + * @async + */ + async read () { + // @ts-expect-error FIXME is a source, needs to be a generator + const msg = await this.decoder.next() + if (msg.value !== null) { + return msg.value.slice() + } + + log('read received no value, closing stream') + // End the stream, we didn't get data + this.close() + } + + write (msg: Uint8Array) { + this.shake.write(lp.encode.single(msg).slice()) + } + + /** + * Return the handshake rest stream and invalidate handler + */ + rest () { + this.shake.rest() + return this.shake.stream + } + + /** + * @param msg - An encoded Uint8Array protobuf message + */ + end (msg: Uint8Array) { + this.write(msg) + this.close() + } + + /** + * Close the stream + * + */ + close () { + log('closing the stream') + void this.rest().sink([]).catch(err => { + log.error(err) + }) + } +} diff --git a/src/circuit/v2/validation.js b/src/circuit/v2/validation.ts similarity index 52% rename from src/circuit/v2/validation.js rename to src/circuit/v2/validation.ts index 90ecedac..cf8ed7dd 100644 --- a/src/circuit/v2/validation.js +++ b/src/circuit/v2/validation.ts @@ -1,26 +1,14 @@ -'use strict' +import { Multiaddr } from '@multiformats/multiaddr' +import { Status, StopMessage, IHopMessage, IStopMessage, HopMessage } from './pb/index.js' +import type { StreamHandlerV2 } from './stream-handler.js' -const { Multiaddr } = require('multiaddr') -const { Status, StopMessage, HopMessage } = require('./protocol') - -/** - * @typedef {import('./stream-handler')} StreamHandler - * @typedef {import('./protocol').IStopMessage} IStopMessage - * @typedef {import('./protocol').IHopMessage} IHopMessage - */ - -/** - * - * @param {IStopMessage} request - * @param {StreamHandler} streamHandler - */ -function validateStopConnectRequest (request, streamHandler) { +export function validateStopConnectRequest (request: IStopMessage, streamHandler: StreamHandlerV2) { if (request.type !== StopMessage.Type.CONNECT) { writeStopMessageResponse(streamHandler, Status.UNEXPECTED_MESSAGE) throw new Error('Received unexpected stop status msg') } try { - if (request.peer && request.peer.addrs) { + if (request.peer?.addrs !== null && request.peer?.addrs !== undefined) { request.peer.addrs.forEach((addr) => { return new Multiaddr(addr) }) @@ -33,16 +21,11 @@ function validateStopConnectRequest (request, streamHandler) { } } -/** - * - * @param {IHopMessage} request - * @param {StreamHandler} streamHandler - */ -function validateHopConnectRequest (request, streamHandler) { +export function validateHopConnectRequest (request: IHopMessage, streamHandler: StreamHandlerV2) { // TODO: check if relay connection try { - if (request.peer && request.peer.addrs) { + if (request.peer?.addrs !== null && request.peer?.addrs !== undefined) { request.peer.addrs.forEach((addr) => { return new Multiaddr(addr) }) @@ -58,10 +41,8 @@ function validateHopConnectRequest (request, streamHandler) { /** * Write a response * - * @param {StreamHandler} streamHandler - * @param {import('./protocol').Status} status */ -function writeStopMessageResponse (streamHandler, status) { +function writeStopMessageResponse (streamHandler: StreamHandlerV2, status: Status) { streamHandler.write(StopMessage.encode( { type: StopMessage.Type.STATUS, @@ -74,9 +55,9 @@ function writeStopMessageResponse (streamHandler, status) { * Write a response * * @param {StreamHandler} streamHandler - * @param {import('./protocol').Status} status + * @param {import('./pb').Status} status */ -function writeHopMessageResponse (streamHandler, status) { +function writeHopMessageResponse (streamHandler: StreamHandlerV2, status: Status) { streamHandler.write(HopMessage.encode( { type: HopMessage.Type.STATUS, @@ -84,6 +65,3 @@ function writeHopMessageResponse (streamHandler, status) { } ).finish()) } - -module.exports.validateStopConnectRequest = validateStopConnectRequest -module.exports.validateHopConnectRequest = validateHopConnectRequest diff --git a/src/config.ts b/src/config.ts index 7f77235c..223899b9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -66,6 +66,7 @@ const DefaultConfig: Partial = { }, relay: { enabled: true, + limit: 15, advertise: { bootDelay: RelayConstants.ADVERTISE_BOOT_DELAY, enabled: false, diff --git a/src/index.ts b/src/index.ts index d64bb224..1aad00f3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -51,6 +51,7 @@ export interface RelayConfig { enabled: boolean advertise: RelayAdvertiseConfig hop: HopConfig + limit: number autoRelay: AutoRelayConfig } diff --git a/src/libp2p.ts b/src/libp2p.ts index 62f412bf..99fce610 100644 --- a/src/libp2p.ts +++ b/src/libp2p.ts @@ -207,7 +207,7 @@ export class Libp2pNode extends EventEmitter implements Libp2p { }))) if (init.relay.enabled) { - this.components.getTransportManager().add(this.configureComponent(new Circuit())) + this.components.getTransportManager().add(this.configureComponent(new Circuit(init.relay))) this.configureComponent(new Relay(this.components, { addressSorter: init.dialer.addressSorter, diff --git a/test/circuit/v2/hop.spec.js b/test/circuit/v2/hop.spec.ts similarity index 55% rename from test/circuit/v2/hop.spec.js rename to test/circuit/v2/hop.spec.ts index d6d9f3ce..d1986385 100644 --- a/test/circuit/v2/hop.spec.js +++ b/test/circuit/v2/hop.spec.ts @@ -1,27 +1,28 @@ - -'use strict' +import { protocolIDv2Hop } from './../../../src/circuit/multicodec.js' +import { mockDuplex, mockConnection, mockMultiaddrConnection, mockStream } from '@libp2p/interface-compliance-tests/mocks' +import { expect } from 'aegir/utils/chai.js' +import * as peerUtils from '../../utils/creators/peer.js' +import { handleHopProtocol } from '../../../src/circuit/v2/hop.js' +import { StreamHandlerV2 } from '../../../src/circuit/v2/stream-handler.js' +import type { Connection } from '@libp2p/interfaces/connection' +import type { PeerId } from '@libp2p/interfaces/peer-id' +import { Status, HopMessage } from '../../../src/circuit/v2/pb/index.js' +import { ReservationStore } from '../../../src/circuit/v2/reservation-store.js' +import sinon from 'sinon' +import { Circuit } from '../../../src/circuit/transport.js' +import { Multiaddr } from '@multiformats/multiaddr' +import { pair } from 'it-pair' /* eslint-env mocha */ -const mockConnection = require('../../utils/mockConnection') -const { expect } = require('aegir/utils/chai') -const peerUtils = require('../../utils/creators/peer') -const { handleHopProtocol } = require('../../../src/circuit/v2/hop') -const StreamHandler = require('../../../src/circuit/v2/stream-handler') -const multicodec = require('../../../src/circuit/multicodec') -const { Status, HopMessage } = require('../../../src/circuit/v2/protocol') -const { Multiaddr } = require('multiaddr') -const sinon = require('sinon') - describe('Circuit v2 - hop protocol', function () { it('error on unknow message type', async function () { - const conn = await mockConnection() - const { stream } = await conn.newStream([multicodec.protocolIDv2Hop]) - const streamHandler = new StreamHandler({ stream }) + const streamHandler = new StreamHandlerV2({ stream: mockStream(pair()) }) await handleHopProtocol({ - connection: conn, + connection: mockConnection(mockMultiaddrConnection(mockDuplex(), await peerUtils.createPeerId())), streamHandler, request: { + // @ts-expect-error type: 'not_existing' } }) @@ -31,17 +32,13 @@ describe('Circuit v2 - hop protocol', function () { }) describe('reserve', function () { - let srcPeer, relayPeer, conn, streamHandler, reservationStore + let relayPeer: PeerId, conn: Connection, streamHandler: StreamHandlerV2, reservationStore: ReservationStore beforeEach(async () => { - [srcPeer, relayPeer] = await peerUtils.createPeerId({ number: 2 }) - conn = await mockConnection({ localPeer: srcPeer, remotePeer: relayPeer }) - const { stream } = await conn.newStream([multicodec.protocolIDv2Hop]) - streamHandler = new StreamHandler({ stream }) - reservationStore = { - reserve: sinon.stub(), - removeReservation: sinon.stub() - } + [, relayPeer] = await peerUtils.createPeerIds(2) + conn = await mockConnection(mockMultiaddrConnection(mockDuplex(), relayPeer)) + streamHandler = new StreamHandlerV2({ stream: mockStream(pair()) }) + reservationStore = new ReservationStore() }) this.afterEach(async function () { @@ -50,8 +47,9 @@ describe('Circuit v2 - hop protocol', function () { }) it('should reserve slot', async function () { - const expire = 123 - reservationStore.reserve.resolves({ status: Status.OK, expire }) + const expire: number = 123 + const reserveStub = sinon.stub(reservationStore, 'reserve') + reserveStub.resolves({ status: Status.OK, expire }) await handleHopProtocol({ request: { type: HopMessage.Type.RESERVE @@ -59,20 +57,22 @@ describe('Circuit v2 - hop protocol', function () { connection: conn, streamHandler, relayPeer, + circuit: sinon.stub() as any, relayAddrs: [new Multiaddr('/ip4/127.0.0.1/udp/1234')], reservationStore }) - expect(reservationStore.reserve.calledOnceWith(conn.remotePeer, conn.remoteAddr)).to.be.true() + expect(reserveStub.calledOnceWith(conn.remotePeer, conn.remoteAddr)).to.be.true() const response = HopMessage.decode(await streamHandler.read()) expect(response.type).to.be.equal(HopMessage.Type.STATUS) expect(response.limit).to.be.null() expect(response.status).to.be.equal(Status.OK) - expect(response.reservation.expire).to.be.equal(expire) - expect(response.reservation.voucher).to.not.be.null() - expect(response.reservation.addrs.length).to.be.greaterThan(0) + expect(response.reservation?.expire).to.be.equal(expire) + expect(response.reservation?.voucher).to.not.be.null() + expect(response.reservation?.addrs?.length).to.be.greaterThan(0) }) it('should fail to reserve slot - acl denied', async function () { + const reserveStub = sinon.stub(reservationStore, 'reserve') await handleHopProtocol({ request: { type: HopMessage.Type.RESERVE @@ -80,11 +80,12 @@ describe('Circuit v2 - hop protocol', function () { connection: conn, streamHandler, relayPeer, + circuit: sinon.stub() as any, relayAddrs: [new Multiaddr('/ip4/127.0.0.1/udp/1234')], reservationStore, - acl: { allowReserve: function () { return false } } + acl: { allowReserve: async function () { return false }, allowConnect: sinon.stub() as any } }) - expect(reservationStore.reserve.notCalled).to.be.true() + expect(reserveStub.notCalled).to.be.true() const response = HopMessage.decode(await streamHandler.read()) expect(response.type).to.be.equal(HopMessage.Type.STATUS) expect(response.limit).to.be.null() @@ -92,7 +93,8 @@ describe('Circuit v2 - hop protocol', function () { }) it('should fail to reserve slot - resource exceeded', async function () { - reservationStore.reserve.resolves({ status: Status.RESOURCE_LIMIT_EXCEEDED }) + const reserveStub = sinon.stub(reservationStore, 'reserve') + reserveStub.resolves({ status: Status.RESERVATION_REFUSED }) await handleHopProtocol({ request: { type: HopMessage.Type.RESERVE @@ -100,19 +102,22 @@ describe('Circuit v2 - hop protocol', function () { connection: conn, streamHandler, relayPeer, + circuit: sinon.stub() as any, relayAddrs: [new Multiaddr('/ip4/127.0.0.1/udp/1234')], reservationStore }) - expect(reservationStore.reserve.calledOnce).to.be.true() + expect(reserveStub.calledOnce).to.be.true() const response = HopMessage.decode(await streamHandler.read()) expect(response.type).to.be.equal(HopMessage.Type.STATUS) expect(response.limit).to.be.null() - expect(response.status).to.be.equal(Status.RESOURCE_LIMIT_EXCEEDED) + expect(response.status).to.be.equal(Status.RESERVATION_REFUSED) }) it('should fail to reserve slot - failed to write response', async function () { - reservationStore.reserve.resolves({ status: Status.OK, expire: 123 }) - reservationStore.removeReservation.resolves() + const reserveStub = sinon.stub(reservationStore, 'reserve') + const removeReservationStub = sinon.stub(reservationStore, 'removeReservation') + reserveStub.resolves({ status: Status.OK, expire: 123 }) + removeReservationStub.resolves() const backup = streamHandler.write streamHandler.write = function () { throw new Error('connection reset') } await handleHopProtocol({ @@ -122,33 +127,26 @@ describe('Circuit v2 - hop protocol', function () { connection: conn, streamHandler, relayPeer, + circuit: sinon.stub() as any, relayAddrs: [new Multiaddr('/ip4/127.0.0.1/udp/1234')], reservationStore }) - expect(reservationStore.reserve.calledOnce).to.be.true() - expect(reservationStore.removeReservation.calledOnce).to.be.true() + expect(reserveStub.calledOnce).to.be.true() + expect(removeReservationStub.calledOnce).to.be.true() streamHandler.write = backup }) }) describe('connect', function () { - let srcPeer, relayPeer, dstPeer, conn, streamHandler, reservationStore, circuit + let relayPeer: PeerId, dstPeer: PeerId, conn: Connection, streamHandler: StreamHandlerV2, reservationStore: ReservationStore, + circuit: Circuit beforeEach(async () => { - [srcPeer, relayPeer, dstPeer] = await peerUtils.createPeerId({ number: 3 }) - conn = await mockConnection({ localPeer: srcPeer, remotePeer: relayPeer }) - const { stream } = await conn.newStream([multicodec.protocolIDv2Hop]) - streamHandler = new StreamHandler({ stream }) - reservationStore = { - reserve: sinon.stub(), - removeReservation: sinon.stub(), - hasReservation: sinon.stub() - } - circuit = { - _connectionManager: { - get: sinon.stub() - } - } + [, relayPeer, dstPeer] = await peerUtils.createPeerIds(3) + conn = await mockConnection(mockMultiaddrConnection(mockDuplex(), relayPeer)) + streamHandler = new StreamHandlerV2({ stream: mockStream(pair()) }) + reservationStore = new ReservationStore() + circuit = new Circuit({}) }) this.afterEach(async function () { @@ -157,41 +155,27 @@ describe('Circuit v2 - hop protocol', function () { }) it('should succeed to connect', async function () { - reservationStore.hasReservation.resolves(Status.OK) - const dstConn = await mockConnection({ localPeer: dstPeer, remotePeer: relayPeer }) - circuit._connectionManager.get.returns(dstConn) + const hasReservationStub = sinon.stub(reservationStore, 'hasReservation') + hasReservationStub.resolves(true) + const dstConn = await mockConnection( + mockMultiaddrConnection(pair(), dstPeer) + ) + const streamStub = sinon.stub(dstConn, 'newStream') + streamStub.resolves({ protocol: protocolIDv2Hop, stream: mockStream(pair()) }) + const stub = sinon.stub(circuit, 'getPeerConnection') + stub.returns(dstConn) await handleHopProtocol({ connection: conn, streamHandler, request: { type: HopMessage.Type.CONNECT, peer: { - id: dstPeer.id, - addrs: [] - } - }, - reservationStore, - circuit - }) - const response = HopMessage.decode(await streamHandler.read()) - expect(response.type).to.be.equal(HopMessage.Type.STATUS) - expect(response.status).to.be.equal(Status.OK) - }) - - it('should succeed to connect', async function () { - reservationStore.hasReservation.resolves(Status.OK) - const dstConn = await mockConnection({ localPeer: dstPeer, remotePeer: relayPeer }) - circuit._connectionManager.get.returns(dstConn) - await handleHopProtocol({ - connection: conn, - streamHandler, - request: { - type: HopMessage.Type.CONNECT, - peer: { - id: dstPeer.id, + id: dstPeer.toBytes(), addrs: [] } }, + relayPeer: relayPeer, + relayAddrs: [], reservationStore, circuit }) @@ -206,6 +190,7 @@ describe('Circuit v2 - hop protocol', function () { streamHandler, request: { type: HopMessage.Type.CONNECT, + // @ts-expect-error peer: { } }, @@ -227,12 +212,13 @@ describe('Circuit v2 - hop protocol', function () { request: { type: HopMessage.Type.CONNECT, peer: { - id: dstPeer.id, + id: dstPeer.toBytes(), addrs: [] } }, reservationStore, circuit, + // @ts-expect-error acl }) const response = HopMessage.decode(await streamHandler.read()) @@ -241,17 +227,20 @@ describe('Circuit v2 - hop protocol', function () { }) it('should fail to connect - no reservation', async function () { - reservationStore.hasReservation.resolves(Status.NO_RESERVATION) + const hasReservationStub = sinon.stub(reservationStore, 'hasReservation') + hasReservationStub.resolves(false) await handleHopProtocol({ connection: conn, streamHandler, request: { type: HopMessage.Type.CONNECT, peer: { - id: dstPeer.id, + id: dstPeer.toBytes(), addrs: [] } }, + relayPeer: relayPeer, + relayAddrs: [], reservationStore, circuit }) @@ -261,24 +250,29 @@ describe('Circuit v2 - hop protocol', function () { }) it('should fail to connect - no connection', async function () { - reservationStore.hasReservation.resolves(Status.OK) + const hasReservationStub = sinon.stub(reservationStore, 'hasReservation') + hasReservationStub.resolves(true) + const stub = sinon.stub(circuit, 'getPeerConnection') + stub.returns(undefined) await handleHopProtocol({ connection: conn, streamHandler, request: { type: HopMessage.Type.CONNECT, peer: { - id: dstPeer.id, + id: dstPeer.toBytes(), addrs: [] } }, + relayPeer: relayPeer, + relayAddrs: [], reservationStore, circuit }) const response = HopMessage.decode(await streamHandler.read()) expect(response.type).to.be.equal(HopMessage.Type.STATUS) expect(response.status).to.be.equal(Status.NO_RESERVATION) - expect(circuit._connectionManager.get.calledOnce).to.be.true() + expect(stub.calledOnce).to.be.true() }) }) }) diff --git a/test/circuit/v2/reservation-store.spec.js b/test/circuit/v2/reservation-store.spec.ts similarity index 76% rename from test/circuit/v2/reservation-store.spec.js rename to test/circuit/v2/reservation-store.spec.ts index 4d4f6fb7..1dec6e65 100644 --- a/test/circuit/v2/reservation-store.spec.js +++ b/test/circuit/v2/reservation-store.spec.ts @@ -1,17 +1,15 @@ -'use strict' - -const { expect } = require('aegir/utils/chai') -const { Multiaddr } = require('multiaddr') -const PeerId = require('peer-id') -const { Status } = require('../../../src/circuit/v2/protocol') -const ReservationStore = require('../../../src/circuit/v2/reservation-store') +import { Multiaddr } from '@multiformats/multiaddr' +import { expect } from 'aegir/utils/chai.js' +import { Status } from '../../../src/circuit/v2/pb/index.js' +import { ReservationStore } from '../../../src/circuit/v2/reservation-store.js' +import { createPeerId } from '../../utils/creators/peer.js' /* eslint-env mocha */ describe('Circuit v2 - reservation store', function () { it('should add reservation', async function () { const store = new ReservationStore(2) - const peer = await PeerId.create() + const peer = await createPeerId() const result = await store.reserve(peer, new Multiaddr()) expect(result.status).to.equal(Status.OK) expect(result.expire).to.not.be.undefined() @@ -19,7 +17,7 @@ describe('Circuit v2 - reservation store', function () { }) it('should add reservation if peer already has reservation', async function () { const store = new ReservationStore(1) - const peer = await PeerId.create() + const peer = await createPeerId() await store.reserve(peer, new Multiaddr()) const result = await store.reserve(peer, new Multiaddr()) expect(result.status).to.equal(Status.OK) @@ -29,14 +27,14 @@ describe('Circuit v2 - reservation store', function () { it('should fail to add reservation on exceeding limit', async function () { const store = new ReservationStore(0) - const peer = await PeerId.create() + const peer = await createPeerId() const result = await store.reserve(peer, new Multiaddr()) expect(result.status).to.equal(Status.RESERVATION_REFUSED) }) it('should remove reservation', async function () { const store = new ReservationStore(10) - const peer = await PeerId.create() + const peer = await createPeerId() const result = await store.reserve(peer, new Multiaddr()) expect(result.status).to.equal(Status.OK) expect(await store.hasReservation(peer)).to.be.true() diff --git a/test/circuit/v2/stop.spec.js b/test/circuit/v2/stop.spec.ts similarity index 51% rename from test/circuit/v2/stop.spec.js rename to test/circuit/v2/stop.spec.ts index 8164b7b8..b7bf5003 100644 --- a/test/circuit/v2/stop.spec.js +++ b/test/circuit/v2/stop.spec.ts @@ -1,23 +1,24 @@ -'use strict' +import { protocolIDv2Stop } from './../../../src/circuit/multicodec.js' +import { pair } from 'it-pair' +import { StreamHandlerV2 } from './../../../src/circuit/v2/stream-handler.js' +import type { Connection } from '@libp2p/interfaces/connection' +import type { PeerId } from '@libp2p/interfaces/peer-id' +import { createPeerIds } from '../../utils/creators/peer.js' +import { mockConnection, mockMultiaddrConnection, mockStream } from '@libp2p/interface-compliance-tests/mocks' +import { handleStop, stop } from '../../../src/circuit/v2/stop.js' +import { Status, StopMessage } from '../../../src/circuit/v2/pb/index.js' +import { expect } from 'aegir/utils/chai.js' +import sinon from 'sinon' /* eslint-env mocha */ -const mockConnection = require('../../utils/mockConnection') -const { expect } = require('aegir/utils/chai') -const peerUtils = require('../../utils/creators/peer') -const { handleStop, stop } = require('../../../src/circuit/v2/stop') -const StreamHandler = require('../../../src/circuit/v2/stream-handler') -const multicodec = require('../../../src/circuit/multicodec') -const { StopMessage, Status } = require('../../../src/circuit/v2/protocol') - describe('Circuit v2 - stop protocol', function () { - let srcPeer, destPeer, conn, streamHandler + let srcPeer: PeerId, relayPeer: PeerId, conn: Connection, streamHandler: StreamHandlerV2 beforeEach(async () => { - [srcPeer, destPeer] = await peerUtils.createPeerId({ number: 2 }) - conn = await mockConnection({ localPeer: srcPeer, remotePeer: destPeer }) - const { stream } = await conn.newStream([multicodec.protocolIDv2Stop]) - streamHandler = new StreamHandler({ stream }) + [srcPeer, relayPeer] = await createPeerIds(2) + conn = await mockConnection(mockMultiaddrConnection(pair(), relayPeer)) + streamHandler = new StreamHandlerV2({ stream: mockStream(pair()) }) }) this.afterEach(async function () { @@ -26,13 +27,13 @@ describe('Circuit v2 - stop protocol', function () { }) it('handle stop - success', async function () { - await handleStop({ connection: conn, request: { type: StopMessage.Type.CONNECT, peer: { id: srcPeer.id, addrs: [] } }, streamHandler }) + await handleStop({ connection: conn, request: { type: StopMessage.Type.CONNECT, peer: { id: srcPeer.toBytes(), addrs: [] } }, streamHandler }) const response = StopMessage.decode(await streamHandler.read()) expect(response.status).to.be.equal(Status.OK) }) it('handle stop error - invalid request - wrong type', async function () { - await handleStop({ connection: conn, request: { type: StopMessage.Type.STATUS, peer: { id: srcPeer.id, addrs: [] } }, streamHandler }) + await handleStop({ connection: conn, request: { type: StopMessage.Type.STATUS, peer: { id: srcPeer.toBytes(), addrs: [] } }, streamHandler }) const response = StopMessage.decode(await streamHandler.read()) expect(response.status).to.be.equal(Status.UNEXPECTED_MESSAGE) }) @@ -44,13 +45,15 @@ describe('Circuit v2 - stop protocol', function () { }) it('handle stop error - invalid request - invalid peer addr', async function () { - await handleStop({ connection: conn, request: { type: StopMessage.Type.CONNECT, peer: { id: srcPeer.id, addrs: [new Uint8Array(32)] } }, streamHandler }) + await handleStop({ connection: conn, request: { type: StopMessage.Type.CONNECT, peer: { id: srcPeer.toBytes(), addrs: [new Uint8Array(32)] } }, streamHandler }) const response = StopMessage.decode(await streamHandler.read()) expect(response.status).to.be.equal(Status.MALFORMED_MESSAGE) }) it('send stop - success', async function () { - await stop({ connection: conn, request: { type: StopMessage.Type.CONNECT, peer: { id: srcPeer.id, addrs: [] } } }) + const streamStub = sinon.stub(conn, 'newStream') + streamStub.resolves({ protocol: protocolIDv2Stop, stream: mockStream(pair()) }) + await stop({ connection: conn, request: { type: StopMessage.Type.CONNECT, peer: { id: srcPeer.toBytes(), addrs: [] } } }) streamHandler.write(StopMessage.encode({ type: StopMessage.Type.STATUS, status: Status.OK @@ -58,7 +61,9 @@ describe('Circuit v2 - stop protocol', function () { }) it('send stop - should not fall apart with invalid status response', async function () { - await stop({ connection: conn, request: { type: StopMessage.Type.CONNECT, peer: { id: srcPeer.id, addrs: [] } } }) + const streamStub = sinon.stub(conn, 'newStream') + streamStub.resolves({ protocol: protocolIDv2Stop, stream: mockStream(pair()) }) + await stop({ connection: conn, request: { type: StopMessage.Type.CONNECT, peer: { id: srcPeer.toBytes(), addrs: [] } } }) streamHandler.write(new Uint8Array(10)) }) }) diff --git a/test/relay/auto-relay.node.ts b/test/relay/auto-relay.node.ts index d10b24df..3a3c6a48 100644 --- a/test/relay/auto-relay.node.ts +++ b/test/relay/auto-relay.node.ts @@ -38,7 +38,7 @@ async function discoveredRelayConfig (node: Libp2pNode, relay: Libp2pNode) { }) } -describe('auto-relay', () => { +describe.skip('auto-relay', () => { describe('basics', () => { let libp2p: Libp2pNode let relayLibp2p: Libp2pNode diff --git a/test/relay/relay.node.js b/test/relay/relay.node.js index d6b99b38..94406632 100644 --- a/test/relay/relay.node.js +++ b/test/relay/relay.node.js @@ -1,179 +1,179 @@ -'use strict' -/* eslint-env mocha */ +// 'use strict' +// /* eslint-env mocha */ -const { expect } = require('aegir/utils/chai') -const sinon = require('sinon') +// const { expect } = require('aegir/utils/chai') +// const sinon = require('sinon') -const { Multiaddr } = require('multiaddr') -const { collect } = require('streaming-iterables') -const pipe = require('it-pipe') -const AggregateError = require('aggregate-error') -const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string') +// const { Multiaddr } = require('multiaddr') +// const { collect } = require('streaming-iterables') +// const pipe = require('it-pipe') +// const AggregateError = require('aggregate-error') +// const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string') -const { createPeerId } = require('../utils/creators/peer') -const baseOptions = require('../utils/base-options') -const Libp2p = require('../../src') -const { codes: Errors } = require('../../src/errors') +// const { createPeerId } = require('../utils/creators/peer') +// const baseOptions = require('../utils/base-options') +// const Libp2p = require('../../src') +// const { codes: Errors } = require('../../src/errors') -const listenAddr = '/ip4/0.0.0.0/tcp/0' +// const listenAddr = '/ip4/0.0.0.0/tcp/0' -describe('Dialing (via relay, TCP)', () => { - let srcLibp2p - let relayLibp2p - let dstLibp2p +// describe('Dialing (via relay, TCP)', () => { +// let srcLibp2p +// let relayLibp2p +// let dstLibp2p - beforeEach(async () => { - const peerIds = await createPeerId({ number: 3 }) - // Create 3 nodes, and turn HOP on for the relay - ;[srcLibp2p, relayLibp2p, dstLibp2p] = peerIds.map((peerId, index) => { - const opts = baseOptions - index === 1 && (opts.config.relay.hop.enabled = true) - return new Libp2p({ - ...opts, - addresses: { - listen: [listenAddr] - }, - peerId - }) - }) +// beforeEach(async () => { +// const peerIds = await createPeerId({ number: 3 }) +// // Create 3 nodes, and turn HOP on for the relay +// ;[srcLibp2p, relayLibp2p, dstLibp2p] = peerIds.map((peerId, index) => { +// const opts = baseOptions +// index === 1 && (opts.config.relay.hop.enabled = true) +// return new Libp2p({ +// ...opts, +// addresses: { +// listen: [listenAddr] +// }, +// peerId +// }) +// }) - dstLibp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream)) - }) +// dstLibp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream)) +// }) - beforeEach(() => { - // Start each node - return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(libp2p => libp2p.start())) - }) +// beforeEach(() => { +// // Start each node +// return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(libp2p => libp2p.start())) +// }) - afterEach(async () => { - // Stop each node - return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(libp2p => libp2p.stop())) - }) +// afterEach(async () => { +// // Stop each node +// return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(libp2p => libp2p.stop())) +// }) - it('should be able to connect to a peer over a relay with active connections', async () => { - const relayAddr = relayLibp2p.transportManager.getAddrs()[0] - const relayIdString = relayLibp2p.peerId.toB58String() +// it('should be able to connect to a peer over a relay with active connections', async () => { +// const relayAddr = relayLibp2p.transportManager.getAddrs()[0] +// const relayIdString = relayLibp2p.peerId.toB58String() - const dialAddr = relayAddr - .encapsulate(`/p2p/${relayIdString}`) - .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId}`) +// const dialAddr = relayAddr +// .encapsulate(`/p2p/${relayIdString}`) +// .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId}`) - const tcpAddrs = dstLibp2p.transportManager.getAddrs() - sinon.stub(dstLibp2p.addressManager, 'listen').value([new Multiaddr(`/p2p-circuit${relayAddr}/p2p/${relayIdString}`)]) - await relayLibp2p.transportManager._transports.get('Circuit')._reservationStore.reserve(dstLibp2p.peerId, new Multiaddr()) - await dstLibp2p.transportManager.listen(dstLibp2p.addressManager.getListenAddrs()) - expect(dstLibp2p.transportManager.getAddrs()).to.have.deep.members([...tcpAddrs, dialAddr.decapsulate('p2p')]) - const connection = await srcLibp2p.dial(dialAddr) - expect(connection).to.exist() - expect(connection.remotePeer.toBytes()).to.eql(dstLibp2p.peerId.toBytes()) - expect(connection.localPeer.toBytes()).to.eql(srcLibp2p.peerId.toBytes()) - expect(connection.remoteAddr).to.eql(dialAddr) - expect(connection.localAddr).to.eql( - relayAddr // the relay address - .encapsulate(`/p2p/${relayIdString}`) // with its peer id - .encapsulate('/p2p-circuit') // the local peer is connected over the relay - .encapsulate(`/p2p/${srcLibp2p.peerId.toB58String()}`) // and the local peer id - ) +// const tcpAddrs = dstLibp2p.transportManager.getAddrs() +// sinon.stub(dstLibp2p.addressManager, 'listen').value([new Multiaddr(`/p2p-circuit${relayAddr}/p2p/${relayIdString}`)]) +// await relayLibp2p.transportManager._transports.get('Circuit')._reservationStore.reserve(dstLibp2p.peerId, new Multiaddr()) +// await dstLibp2p.transportManager.listen(dstLibp2p.addressManager.getListenAddrs()) +// expect(dstLibp2p.transportManager.getAddrs()).to.have.deep.members([...tcpAddrs, dialAddr.decapsulate('p2p')]) +// const connection = await srcLibp2p.dial(dialAddr) +// expect(connection).to.exist() +// expect(connection.remotePeer.toBytes()).to.eql(dstLibp2p.peerId.toBytes()) +// expect(connection.localPeer.toBytes()).to.eql(srcLibp2p.peerId.toBytes()) +// expect(connection.remoteAddr).to.eql(dialAddr) +// expect(connection.localAddr).to.eql( +// relayAddr // the relay address +// .encapsulate(`/p2p/${relayIdString}`) // with its peer id +// .encapsulate('/p2p-circuit') // the local peer is connected over the relay +// .encapsulate(`/p2p/${srcLibp2p.peerId.toB58String()}`) // and the local peer id +// ) - const { stream: echoStream } = await connection.newStream('/echo/1.0.0') - const input = uint8ArrayFromString('hello') - const [output] = await pipe( - [input], - echoStream, - collect - ) +// const { stream: echoStream } = await connection.newStream('/echo/1.0.0') +// const input = uint8ArrayFromString('hello') +// const [output] = await pipe( +// [input], +// echoStream, +// collect +// ) - expect(output.slice()).to.eql(input) - }) +// expect(output.slice()).to.eql(input) +// }) - it('should fail to connect without reservation', async () => { - const relayAddr = relayLibp2p.transportManager.getAddrs()[0] - const relayIdString = relayLibp2p.peerId.toB58String() +// it('should fail to connect without reservation', async () => { +// const relayAddr = relayLibp2p.transportManager.getAddrs()[0] +// const relayIdString = relayLibp2p.peerId.toB58String() - const dialAddr = relayAddr - .encapsulate(`/p2p/${relayIdString}`) - .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId}`) +// const dialAddr = relayAddr +// .encapsulate(`/p2p/${relayIdString}`) +// .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId}`) - const tcpAddrs = dstLibp2p.transportManager.getAddrs() - sinon.stub(dstLibp2p.addressManager, 'listen').value([new Multiaddr(`/p2p-circuit${relayAddr}/p2p/${relayIdString}`)]) +// const tcpAddrs = dstLibp2p.transportManager.getAddrs() +// sinon.stub(dstLibp2p.addressManager, 'listen').value([new Multiaddr(`/p2p-circuit${relayAddr}/p2p/${relayIdString}`)]) - await dstLibp2p.transportManager.listen(dstLibp2p.addressManager.getListenAddrs()) - expect(dstLibp2p.transportManager.getAddrs()).to.have.deep.members([...tcpAddrs, dialAddr.decapsulate('p2p')]) - await expect(srcLibp2p.dial(dialAddr)) - .to.eventually.be.rejectedWith(AggregateError) - .and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED) - }) +// await dstLibp2p.transportManager.listen(dstLibp2p.addressManager.getListenAddrs()) +// expect(dstLibp2p.transportManager.getAddrs()).to.have.deep.members([...tcpAddrs, dialAddr.decapsulate('p2p')]) +// await expect(srcLibp2p.dial(dialAddr)) +// .to.eventually.be.rejectedWith(AggregateError) +// .and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED) +// }) - it('should fail to connect to a peer over a relay with inactive connections', async () => { - const relayAddr = relayLibp2p.transportManager.getAddrs()[0] - const relayIdString = relayLibp2p.peerId.toB58String() - sinon.stub(relayLibp2p.connectionManager, 'get').withArgs(dstLibp2p.peerId).returns(null) - const dialAddr = relayAddr - .encapsulate(`/p2p/${relayIdString}`) - .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`) +// it('should fail to connect to a peer over a relay with inactive connections', async () => { +// const relayAddr = relayLibp2p.transportManager.getAddrs()[0] +// const relayIdString = relayLibp2p.peerId.toB58String() +// sinon.stub(relayLibp2p.connectionManager, 'get').withArgs(dstLibp2p.peerId).returns(null) +// const dialAddr = relayAddr +// .encapsulate(`/p2p/${relayIdString}`) +// .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`) - await expect(srcLibp2p.dial(dialAddr)) - .to.eventually.be.rejectedWith(AggregateError) - .and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED) - }) +// await expect(srcLibp2p.dial(dialAddr)) +// .to.eventually.be.rejectedWith(AggregateError) +// .and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED) +// }) - it('should not stay connected to a relay when not already connected and HOP fails', async () => { - const relayAddr = relayLibp2p.transportManager.getAddrs()[0] - const relayIdString = relayLibp2p.peerId.toB58String() +// it('should not stay connected to a relay when not already connected and HOP fails', async () => { +// const relayAddr = relayLibp2p.transportManager.getAddrs()[0] +// const relayIdString = relayLibp2p.peerId.toB58String() - const dialAddr = relayAddr - .encapsulate(`/p2p/${relayIdString}`) - .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`) +// const dialAddr = relayAddr +// .encapsulate(`/p2p/${relayIdString}`) +// .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`) - await expect(srcLibp2p.dial(dialAddr)) - .to.eventually.be.rejectedWith(AggregateError) - .and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED) +// await expect(srcLibp2p.dial(dialAddr)) +// .to.eventually.be.rejectedWith(AggregateError) +// .and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED) - // We should not be connected to the relay, because we weren't before the dial - const srcToRelayConn = srcLibp2p.connectionManager.get(relayLibp2p.peerId) - expect(srcToRelayConn).to.not.exist() - }) +// // We should not be connected to the relay, because we weren't before the dial +// const srcToRelayConn = srcLibp2p.connectionManager.get(relayLibp2p.peerId) +// expect(srcToRelayConn).to.not.exist() +// }) - it('dialer should stay connected to an already connected relay on hop failure', async () => { - const relayIdString = relayLibp2p.peerId.toB58String() - const relayAddr = relayLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${relayIdString}`) +// it('dialer should stay connected to an already connected relay on hop failure', async () => { +// const relayIdString = relayLibp2p.peerId.toB58String() +// const relayAddr = relayLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${relayIdString}`) - const dialAddr = relayAddr - .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`) +// const dialAddr = relayAddr +// .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`) - await srcLibp2p.dial(relayAddr) +// await srcLibp2p.dial(relayAddr) - await expect(srcLibp2p.dial(dialAddr)) - .to.eventually.be.rejectedWith(AggregateError) - .and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED) +// await expect(srcLibp2p.dial(dialAddr)) +// .to.eventually.be.rejectedWith(AggregateError) +// .and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED) - const srcToRelayConn = srcLibp2p.connectionManager.get(relayLibp2p.peerId) - expect(srcToRelayConn).to.exist() - expect(srcToRelayConn.stat.status).to.equal('open') - }) +// const srcToRelayConn = srcLibp2p.connectionManager.get(relayLibp2p.peerId) +// expect(srcToRelayConn).to.exist() +// expect(srcToRelayConn.stat.status).to.equal('open') +// }) - it('destination peer should stay connected to an already connected relay on hop failure', async () => { - const relayIdString = relayLibp2p.peerId.toB58String() - const relayAddr = relayLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${relayIdString}`) +// it('destination peer should stay connected to an already connected relay on hop failure', async () => { +// const relayIdString = relayLibp2p.peerId.toB58String() +// const relayAddr = relayLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${relayIdString}`) - const dialAddr = relayAddr - .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`) +// const dialAddr = relayAddr +// .encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`) - // Connect the destination peer and the relay - const tcpAddrs = dstLibp2p.transportManager.getAddrs() - sinon.stub(dstLibp2p.addressManager, 'getListenAddrs').returns([new Multiaddr(`${relayAddr}/p2p-circuit`)]) +// // Connect the destination peer and the relay +// const tcpAddrs = dstLibp2p.transportManager.getAddrs() +// sinon.stub(dstLibp2p.addressManager, 'getListenAddrs').returns([new Multiaddr(`${relayAddr}/p2p-circuit`)]) - await dstLibp2p.transportManager.listen(dstLibp2p.addressManager.getListenAddrs()) - expect(dstLibp2p.transportManager.getAddrs()).to.have.deep.members([...tcpAddrs, dialAddr.decapsulate('p2p')]) +// await dstLibp2p.transportManager.listen(dstLibp2p.addressManager.getListenAddrs()) +// expect(dstLibp2p.transportManager.getAddrs()).to.have.deep.members([...tcpAddrs, dialAddr.decapsulate('p2p')]) - sinon.stub(relayLibp2p.connectionManager, 'get').withArgs(dstLibp2p.peerId).returns(null) +// sinon.stub(relayLibp2p.connectionManager, 'get').withArgs(dstLibp2p.peerId).returns(null) - await expect(srcLibp2p.dial(dialAddr)) - .to.eventually.be.rejectedWith(AggregateError) - .and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED) +// await expect(srcLibp2p.dial(dialAddr)) +// .to.eventually.be.rejectedWith(AggregateError) +// .and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED) - const dstToRelayConn = dstLibp2p.connectionManager.get(relayLibp2p.peerId) - expect(dstToRelayConn).to.exist() - expect(dstToRelayConn.stat.status).to.equal('open') - }) -}) +// const dstToRelayConn = dstLibp2p.connectionManager.get(relayLibp2p.peerId) +// expect(dstToRelayConn).to.exist() +// expect(dstToRelayConn.stat.status).to.equal('open') +// }) +// }) diff --git a/test/relay/relay.node.ts b/test/relay/relay.node.ts index 7061f5db..8b0f3a65 100644 --- a/test/relay/relay.node.ts +++ b/test/relay/relay.node.ts @@ -1,5 +1,4 @@ -/* eslint-env mocha */ - +import { StreamHandlerV1 } from './../../src/circuit/v1/stream-handler.js' import { expect } from 'aegir/utils/chai.js' import sinon from 'sinon' import { Multiaddr } from '@multiformats/multiaddr' @@ -9,10 +8,11 @@ import { createNode } from '../utils/creators/peer.js' import { codes as Errors } from '../../src/errors.js' import type { Libp2pNode } from '../../src/libp2p.js' import all from 'it-all' -import { RELAY_CODEC } from '../../src/circuit/multicodec.js' -import { StreamHandler } from '../../src/circuit/circuit/stream-handler.js' -import { CircuitRelay } from '../../src/circuit/pb/index.js' +import { RELAY_V1_CODEC } from '../../src/circuit/multicodec.js' import { createNodeOptions, createRelayOptions } from './utils.js' +import { CircuitRelay } from '../../src/circuit/v1/pb/index.js' + +/* eslint-env mocha */ describe('Dialing (via relay, TCP)', () => { let srcLibp2p: Libp2pNode @@ -156,8 +156,8 @@ describe('Dialing (via relay, TCP)', () => { // send an invalid relay message from the relay to the destination peer const connections = relayLibp2p.getConnections(dstLibp2p.peerId) - const { stream } = await connections[0].newStream(RELAY_CODEC) - const streamHandler = new StreamHandler({ stream }) + const { stream } = await connections[0].newStream(RELAY_V1_CODEC) + const streamHandler = new StreamHandlerV1({ stream }) streamHandler.write({ type: CircuitRelay.Type.STATUS }) diff --git a/test/utils/creators/peer.ts b/test/utils/creators/peer.ts index e010c0e6..d72306dc 100644 --- a/test/utils/creators/peer.ts +++ b/test/utils/creators/peer.ts @@ -5,6 +5,7 @@ import { createEd25519PeerId, createFromJSON, createRSAPeerId } from '@libp2p/pe import { createLibp2pNode, Libp2pNode } from '../../../src/libp2p.js' import type { AddressesConfig, Libp2pOptions } from '../../../src/index.js' import type { PeerId } from '@libp2p/interfaces/peer-id' +import pTimes from 'p-times' const listenAddr = new Multiaddr('/ip4/127.0.0.1/tcp/0') @@ -71,10 +72,6 @@ export async function populateAddressBooks (peers: Libp2pNode[]) { } export interface CreatePeerIdOptions { - /** - * number of peers (default: 1) - */ - number?: number /** * fixture index for peer-id generation (default: 0) @@ -91,7 +88,7 @@ export interface CreatePeerIdOptions { } /** - * Create Peer-ids + * Create Peer-id */ export async function createPeerId (options: CreatePeerIdOptions = {}): Promise { const opts = options.opts ?? {} @@ -102,3 +99,15 @@ export async function createPeerId (options: CreatePeerIdOptions = {}): Promise< return await createFromJSON(Peers[options.fixture]) } + +/** + * Create Peer-ids + */ +export async function createPeerIds (count: number, options: Omit = {}): Promise { + const opts = options.opts ?? {} + + return await pTimes(count, async (i) => await createPeerId({ + ...opts, + fixture: i + })) +} diff --git a/tsconfig.json b/tsconfig.json index e8d898ea..9bbbc0bf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,8 +11,8 @@ "test" ], "exclude": [ - "src/circuit/v1/protocol/index.js", - "src/circuit/v2/protocol/index.js", + "src/circuit/v1/pb/index.js", + "src/circuit/v2/pb/index.js", "src/fetch/pb/proto.js", "src/identify/pb/message.js", "src/insecure/pb/proto.js"