feat: connection gater (#1142)

Port of https://github.com/libp2p/go-libp2p-core/blob/master/connmgr/gater.go

Adds a new configuration key `connectionGater` which allows denying the dialing of certain peers, individual multiaddrs and the creation of connections at certain points in the connection flow.

Fixes: https://github.com/libp2p/js-libp2p/issues/175
Refs: https://github.com/libp2p/js-libp2p/issues/744
Refs: https://github.com/libp2p/js-libp2p/issues/769

Co-authored-by: mzdws <8580712+mzdws@user.noreply.gitee.com>
This commit is contained in:
Alex Potsides 2022-01-25 16:27:01 +00:00 committed by GitHub
parent 9b22c6e2f9
commit ff32eba6a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 770 additions and 98 deletions

View File

@ -23,6 +23,9 @@
- [Setup with Keychain](#setup-with-keychain) - [Setup with Keychain](#setup-with-keychain)
- [Configuring Dialing](#configuring-dialing) - [Configuring Dialing](#configuring-dialing)
- [Configuring Connection Manager](#configuring-connection-manager) - [Configuring Connection Manager](#configuring-connection-manager)
- [Configuring Connection Gater](#configuring-connection-gater)
- [Outgoing connections](#outgoing-connections)
- [Incoming connections](#incoming-connections)
- [Configuring Transport Manager](#configuring-transport-manager) - [Configuring Transport Manager](#configuring-transport-manager)
- [Configuring Metrics](#configuring-metrics) - [Configuring Metrics](#configuring-metrics)
- [Configuring PeerStore](#configuring-peerstore) - [Configuring PeerStore](#configuring-peerstore)
@ -590,6 +593,127 @@ const node = await Libp2p.create({
}) })
``` ```
#### Configuring Connection Gater
The Connection Gater allows us to prevent making incoming and outgoing connections to peers and storing
multiaddrs in the address book.
The order in which methods are called is as follows:
##### Outgoing connections
1. `connectionGater.denyDialPeer(...)`
2. `connectionGater.denyDialMultiaddr(...)`
3. `connectionGater.denyOutboundConnection(...)`
4. `connectionGater.denyOutboundEncryptedConnection(...)`
5. `connectionGater.denyOutboundUpgradedConnection(...)`
##### Incoming connections
1. `connectionGater.denyInboundConnection(...)`
2. `connectionGater.denyInboundEncryptedConnection(...)`
3. `connectionGater.denyInboundUpgradedConnection(...)`
```js
const node = await Libp2p.create({
// .. other config
connectionGater: {
/**
* denyDialMultiaddr tests whether we're permitted to Dial the
* specified peer.
*
* This is called by the dialer.connectToPeer implementation before
* dialling a peer.
*
* Return true to prevent dialing the passed peer.
*/
denyDialPeer: (peerId: PeerId) => Promise<boolean>
/**
* denyDialMultiaddr tests whether we're permitted to dial the specified
* multiaddr for the given peer.
*
* This is called by the dialer.connectToPeer implementation after it has
* resolved the peer's addrs, and prior to dialling each.
*
* Return true to prevent dialing the passed peer on the passed multiaddr.
*/
denyDialMultiaddr: (peerId: PeerId, multiaddr: Multiaddr) => Promise<boolean>
/**
* denyInboundConnection tests whether an incipient inbound connection is allowed.
*
* This is called by the upgrader, or by the transport directly (e.g. QUIC,
* Bluetooth), straight after it has accepted a connection from its socket.
*
* Return true to deny the incoming passed connection.
*/
denyInboundConnection: (maConn: MultiaddrConnection) => Promise<boolean>
/**
* denyOutboundConnection tests whether an incipient outbound connection is allowed.
*
* This is called by the upgrader, or by the transport directly (e.g. QUIC,
* Bluetooth), straight after it has created a connection with its socket.
*
* Return true to deny the incoming passed connection.
*/
denyOutboundConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
/**
* denyInboundEncryptedConnection tests whether a given connection, now encrypted,
* is allowed.
*
* This is called by the upgrader, after it has performed the security
* handshake, and before it negotiates the muxer, or by the directly by the
* transport, at the exact same checkpoint.
*
* Return true to deny the passed secured connection.
*/
denyInboundEncryptedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
/**
* denyOutboundEncryptedConnection tests whether a given connection, now encrypted,
* is allowed.
*
* This is called by the upgrader, after it has performed the security
* handshake, and before it negotiates the muxer, or by the directly by the
* transport, at the exact same checkpoint.
*
* Return true to deny the passed secured connection.
*/
denyOutboundEncryptedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
/**
* denyInboundUpgradedConnection tests whether a fully capable connection is allowed.
*
* This is called after encryption has been negotiated and the connection has been
* multiplexed, if a multiplexer is configured.
*
* Return true to deny the passed upgraded connection.
*/
denyInboundUpgradedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
/**
* denyOutboundUpgradedConnection tests whether a fully capable connection is allowed.
*
* This is called after encryption has been negotiated and the connection has been
* multiplexed, if a multiplexer is configured.
*
* Return true to deny the passed upgraded connection.
*/
denyOutboundUpgradedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
/**
* Used by the address book to filter passed addresses.
*
* Return true to allow storing the passed multiaddr for the passed peer.
*/
filterMultiaddrForPeer: (peer: PeerId, multiaddr: Multiaddr) => Promise<boolean>
}
})
```
#### Configuring Transport Manager #### Configuring Transport Manager
The Transport Manager is responsible for managing the libp2p transports life cycle. This includes starting listeners for the provided listen addresses, closing these listeners and dialing using the provided transports. By default, if a libp2p node has a list of multiaddrs for listening on and there are no valid transports for those multiaddrs, libp2p will throw an error on startup and shutdown. However, for some applications it is perfectly acceptable for libp2p nodes to start in dial only mode if all the listen multiaddrs failed. This error tolerance can be enabled as follows: The Transport Manager is responsible for managing the libp2p transports life cycle. This includes starting listeners for the provided listen addresses, closing these listeners and dialing using the provided transports. By default, if a libp2p node has a list of multiaddrs for listening on and there are no valid transports for those multiaddrs, libp2p will throw an error on startup and shutdown. However, for some applications it is perfectly acceptable for libp2p nodes to start in dial only mode if all the listen multiaddrs failed. This error tolerance can be enabled as follows:

View File

@ -95,6 +95,7 @@
"it-drain": "^1.0.3", "it-drain": "^1.0.3",
"it-filter": "^1.0.1", "it-filter": "^1.0.1",
"it-first": "^1.0.4", "it-first": "^1.0.4",
"it-foreach": "^0.1.1",
"it-handshake": "^2.0.0", "it-handshake": "^2.0.0",
"it-length-prefixed": "^5.0.2", "it-length-prefixed": "^5.0.2",
"it-map": "^1.0.4", "it-map": "^1.0.4",

View File

@ -13,6 +13,7 @@ const { FaultTolerance } = require('./transport-manager')
/** /**
* @typedef {import('multiaddr').Multiaddr} Multiaddr * @typedef {import('multiaddr').Multiaddr} Multiaddr
* @typedef {import('./types').ConnectionGater} ConnectionGater
* @typedef {import('.').Libp2pOptions} Libp2pOptions * @typedef {import('.').Libp2pOptions} Libp2pOptions
* @typedef {import('.').constructorOptions} constructorOptions * @typedef {import('.').constructorOptions} constructorOptions
*/ */
@ -27,6 +28,7 @@ const DefaultConfig = {
connectionManager: { connectionManager: {
minConnections: 25 minConnections: 25
}, },
connectionGater: /** @type {ConnectionGater} */ {},
transportManager: { transportManager: {
faultTolerance: FaultTolerance.FATAL_ALL faultTolerance: FaultTolerance.FATAL_ALL
}, },

View File

@ -1,6 +1,9 @@
'use strict' 'use strict'
const debug = require('debug') const debug = require('debug')
const all = require('it-all')
const filter = require('it-filter')
const { pipe } = require('it-pipe')
const log = Object.assign(debug('libp2p:dialer'), { const log = Object.assign(debug('libp2p:dialer'), {
error: debug('libp2p:dialer:err') error: debug('libp2p:dialer:err')
}) })
@ -33,12 +36,14 @@ const METRICS_PENDING_DIAL_TARGETS = 'pending-dial-targets'
* @typedef {import('../peer-store/types').PeerStore} PeerStore * @typedef {import('../peer-store/types').PeerStore} PeerStore
* @typedef {import('../peer-store/types').Address} Address * @typedef {import('../peer-store/types').Address} Address
* @typedef {import('../transport-manager')} TransportManager * @typedef {import('../transport-manager')} TransportManager
* @typedef {import('../types').ConnectionGater} ConnectionGater
*/ */
/** /**
* @typedef {Object} DialerProperties * @typedef {Object} DialerProperties
* @property {PeerStore} peerStore * @property {PeerStore} peerStore
* @property {TransportManager} transportManager * @property {TransportManager} transportManager
* @property {ConnectionGater} connectionGater
* *
* @typedef {(addr:Multiaddr) => Promise<string[]>} Resolver * @typedef {(addr:Multiaddr) => Promise<string[]>} Resolver
* *
@ -70,6 +75,7 @@ class Dialer {
constructor ({ constructor ({
transportManager, transportManager,
peerStore, peerStore,
connectionGater,
addressSorter = publicAddressesFirst, addressSorter = publicAddressesFirst,
maxParallelDials = MAX_PARALLEL_DIALS, maxParallelDials = MAX_PARALLEL_DIALS,
maxAddrsToDial = MAX_ADDRS_TO_DIAL, maxAddrsToDial = MAX_ADDRS_TO_DIAL,
@ -78,6 +84,7 @@ class Dialer {
resolvers = {}, resolvers = {},
metrics metrics
}) { }) {
this.connectionGater = connectionGater
this.transportManager = transportManager this.transportManager = transportManager
this.peerStore = peerStore this.peerStore = peerStore
this.addressSorter = addressSorter this.addressSorter = addressSorter
@ -136,6 +143,12 @@ class Dialer {
* @returns {Promise<Connection>} * @returns {Promise<Connection>}
*/ */
async connectToPeer (peer, options = {}) { async connectToPeer (peer, options = {}) {
const { id } = getPeer(peer)
if (await this.connectionGater.denyDialPeer(id)) {
throw errCode(new Error('The dial request is blocked by gater.allowDialPeer'), codes.ERR_PEER_DIAL_INTERCEPTED)
}
const dialTarget = await this._createCancellableDialTarget(peer) const dialTarget = await this._createCancellableDialTarget(peer)
if (!dialTarget.addrs.length) { if (!dialTarget.addrs.length) {
@ -203,7 +216,13 @@ class Dialer {
await this.peerStore.addressBook.add(id, multiaddrs) await this.peerStore.addressBook.add(id, multiaddrs)
} }
let knownAddrs = await this.peerStore.addressBook.getMultiaddrsForPeer(id, this.addressSorter) || [] let knownAddrs = await pipe(
await this.peerStore.addressBook.getMultiaddrsForPeer(id, this.addressSorter),
(source) => filter(source, async (multiaddr) => {
return !(await this.connectionGater.denyDialMultiaddr(id, multiaddr))
}),
(source) => all(source)
)
// If received a multiaddr to dial, it should be the first to use // If received a multiaddr to dial, it should be the first to use
// But, if we know other multiaddrs for the peer, we should try them too. // But, if we know other multiaddrs for the peer, we should try them too.

View File

@ -12,6 +12,8 @@ exports.codes = {
PUBSUB_NOT_STARTED: 'ERR_PUBSUB_NOT_STARTED', PUBSUB_NOT_STARTED: 'ERR_PUBSUB_NOT_STARTED',
DHT_NOT_STARTED: 'ERR_DHT_NOT_STARTED', DHT_NOT_STARTED: 'ERR_DHT_NOT_STARTED',
CONN_ENCRYPTION_REQUIRED: 'ERR_CONN_ENCRYPTION_REQUIRED', CONN_ENCRYPTION_REQUIRED: 'ERR_CONN_ENCRYPTION_REQUIRED',
ERR_PEER_DIAL_INTERCEPTED: 'ERR_PEER_DIAL_INTERCEPTED',
ERR_CONNECTION_INTERCEPTED: 'ERR_CONNECTION_INTERCEPTED',
ERR_INVALID_PROTOCOLS_FOR_STREAM: 'ERR_INVALID_PROTOCOLS_FOR_STREAM', ERR_INVALID_PROTOCOLS_FOR_STREAM: 'ERR_INVALID_PROTOCOLS_FOR_STREAM',
ERR_CONNECTION_ENDED: 'ERR_CONNECTION_ENDED', ERR_CONNECTION_ENDED: 'ERR_CONNECTION_ENDED',
ERR_CONNECTION_FAILED: 'ERR_CONNECTION_FAILED', ERR_CONNECTION_FAILED: 'ERR_CONNECTION_FAILED',

View File

@ -48,6 +48,7 @@ const { updateSelfPeerRecord } = require('./record/utils')
* @typedef {import('libp2p-interfaces/src/pubsub').PubsubOptions} PubsubOptions * @typedef {import('libp2p-interfaces/src/pubsub').PubsubOptions} PubsubOptions
* @typedef {import('interface-datastore').Datastore} Datastore * @typedef {import('interface-datastore').Datastore} Datastore
* @typedef {import('./pnet')} Protector * @typedef {import('./pnet')} Protector
* @typedef {import('./types').ConnectionGater} ConnectionGater
* @typedef {Object} PersistentPeerStoreOptions * @typedef {Object} PersistentPeerStoreOptions
* @property {number} [threshold] * @property {number} [threshold]
*/ */
@ -106,6 +107,7 @@ const { updateSelfPeerRecord } = require('./record/utils')
* @property {Libp2pModules} modules libp2p modules to use * @property {Libp2pModules} modules libp2p modules to use
* @property {import('./address-manager').AddressManagerOptions} [addresses] * @property {import('./address-manager').AddressManagerOptions} [addresses]
* @property {import('./connection-manager').ConnectionManagerOptions} [connectionManager] * @property {import('./connection-manager').ConnectionManagerOptions} [connectionManager]
* @property {Partial<import('./types').ConnectionGater>} [connectionGater]
* @property {Datastore} [datastore] * @property {Datastore} [datastore]
* @property {import('./dialer').DialerOptions} [dialer] * @property {import('./dialer').DialerOptions} [dialer]
* @property {import('./identify/index').HostProperties} [host] libp2p host * @property {import('./identify/index').HostProperties} [host] libp2p host
@ -172,10 +174,25 @@ class Libp2p extends EventEmitter {
this.metrics = metrics this.metrics = metrics
} }
/** @type {ConnectionGater} */
this.connectionGater = {
denyDialPeer: async () => Promise.resolve(false),
denyDialMultiaddr: async () => Promise.resolve(false),
denyInboundConnection: async () => Promise.resolve(false),
denyOutboundConnection: async () => Promise.resolve(false),
denyInboundEncryptedConnection: async () => Promise.resolve(false),
denyOutboundEncryptedConnection: async () => Promise.resolve(false),
denyInboundUpgradedConnection: async () => Promise.resolve(false),
denyOutboundUpgradedConnection: async () => Promise.resolve(false),
filterMultiaddrForPeer: async () => Promise.resolve(true),
...this._options.connectionGater
}
/** @type {import('./peer-store/types').PeerStore} */ /** @type {import('./peer-store/types').PeerStore} */
this.peerStore = new PeerStore({ this.peerStore = new PeerStore({
peerId: this.peerId, peerId: this.peerId,
datastore: (this.datastore && this._options.peerStore.persistence) ? this.datastore : new MemoryDatastore() datastore: (this.datastore && this._options.peerStore.persistence) ? this.datastore : new MemoryDatastore(),
addressFilter: this.connectionGater.filterMultiaddrForPeer
}) })
// Addresses {listen, announce, noAnnounce} // Addresses {listen, announce, noAnnounce}
@ -220,6 +237,7 @@ class Libp2p extends EventEmitter {
// Setup the Upgrader // Setup the Upgrader
this.upgrader = new Upgrader({ this.upgrader = new Upgrader({
connectionGater: this.connectionGater,
localPeer: this.peerId, localPeer: this.peerId,
metrics: this.metrics, metrics: this.metrics,
onConnection: (connection) => this.connectionManager.onConnect(connection), onConnection: (connection) => this.connectionManager.onConnect(connection),
@ -262,6 +280,7 @@ class Libp2p extends EventEmitter {
this.dialer = new Dialer({ this.dialer = new Dialer({
transportManager: this.transportManager, transportManager: this.transportManager,
connectionGater: this.connectionGater,
peerStore: this.peerStore, peerStore: this.peerStore,
metrics: this.metrics, metrics: this.metrics,
...this._options.dialer ...this._options.dialer

View File

@ -7,6 +7,11 @@ const PeerId = require('peer-id')
const { codes } = require('../errors') const { codes } = require('../errors')
const PeerRecord = require('../record/peer-record') const PeerRecord = require('../record/peer-record')
const Envelope = require('../record/envelope') const Envelope = require('../record/envelope')
const { pipe } = require('it-pipe')
const all = require('it-all')
const filter = require('it-filter')
const map = require('it-map')
const each = require('it-foreach')
/** /**
* @typedef {import('./types').PeerStore} PeerStore * @typedef {import('./types').PeerStore} PeerStore
@ -27,10 +32,12 @@ class PeerStoreAddressBook {
/** /**
* @param {PeerStore["emit"]} emit * @param {PeerStore["emit"]} emit
* @param {import('./types').Store} store * @param {import('./types').Store} store
* @param {(peerId: PeerId, multiaddr: Multiaddr) => Promise<boolean>} addressFilter
*/ */
constructor (emit, store) { constructor (emit, store, addressFilter) {
this._emit = emit this._emit = emit
this._store = store this._store = store
this._addressFilter = addressFilter
} }
/** /**
@ -88,7 +95,7 @@ class PeerStoreAddressBook {
// Replace unsigned addresses by the new ones from the record // Replace unsigned addresses by the new ones from the record
// TODO: Once we have ttls for the addresses, we should merge these in // TODO: Once we have ttls for the addresses, we should merge these in
updatedPeer = await this._store.patchOrCreate(peerId, { updatedPeer = await this._store.patchOrCreate(peerId, {
addresses: convertMultiaddrsToAddresses(multiaddrs, true), addresses: await filterMultiaddrs(peerId, multiaddrs, this._addressFilter, true),
peerRecordEnvelope: envelope.marshal() peerRecordEnvelope: envelope.marshal()
}) })
@ -180,6 +187,11 @@ class PeerStoreAddressBook {
throw errcode(new Error('peerId must be an instance of peer-id'), codes.ERR_INVALID_PARAMETERS) throw errcode(new Error('peerId must be an instance of peer-id'), codes.ERR_INVALID_PARAMETERS)
} }
if (!Array.isArray(multiaddrs)) {
log.error('multiaddrs must be an array of Multiaddrs')
throw errcode(new Error('multiaddrs must be an array of Multiaddrs'), codes.ERR_INVALID_PARAMETERS)
}
log('set await write lock') log('set await write lock')
const release = await this._store.lock.writeLock() const release = await this._store.lock.writeLock()
log('set got write lock') log('set got write lock')
@ -188,7 +200,7 @@ class PeerStoreAddressBook {
let updatedPeer let updatedPeer
try { try {
const addresses = convertMultiaddrsToAddresses(multiaddrs) const addresses = await filterMultiaddrs(peerId, multiaddrs, this._addressFilter)
// No valid addresses found // No valid addresses found
if (!addresses.length) { if (!addresses.length) {
@ -238,6 +250,11 @@ class PeerStoreAddressBook {
throw errcode(new Error('peerId must be an instance of peer-id'), codes.ERR_INVALID_PARAMETERS) throw errcode(new Error('peerId must be an instance of peer-id'), codes.ERR_INVALID_PARAMETERS)
} }
if (!Array.isArray(multiaddrs)) {
log.error('multiaddrs must be an array of Multiaddrs')
throw errcode(new Error('multiaddrs must be an array of Multiaddrs'), codes.ERR_INVALID_PARAMETERS)
}
log('add await write lock') log('add await write lock')
const release = await this._store.lock.writeLock() const release = await this._store.lock.writeLock()
log('add got write lock') log('add got write lock')
@ -246,7 +263,7 @@ class PeerStoreAddressBook {
let updatedPeer let updatedPeer
try { try {
const addresses = convertMultiaddrsToAddresses(multiaddrs) const addresses = await filterMultiaddrs(peerId, multiaddrs, this._addressFilter)
// No valid addresses found // No valid addresses found
if (!addresses.length) { if (!addresses.length) {
@ -337,33 +354,29 @@ class PeerStoreAddressBook {
} }
/** /**
* Transforms received multiaddrs into Address. * @param {PeerId} peerId
*
* @private
* @param {Multiaddr[]} multiaddrs * @param {Multiaddr[]} multiaddrs
* @param {boolean} [isCertified] * @param {(peerId: PeerId, multiaddr: Multiaddr) => Promise<boolean>} addressFilter
* @returns {Address[]} * @param {boolean} isCertified
*/ */
function convertMultiaddrsToAddresses (multiaddrs, isCertified = false) { function filterMultiaddrs (peerId, multiaddrs, addressFilter, isCertified = false) {
if (!multiaddrs) { return pipe(
log.error('multiaddrs must be provided to store data') multiaddrs,
throw errcode(new Error('multiaddrs must be provided'), codes.ERR_INVALID_PARAMETERS) (source) => each(source, (multiaddr) => {
if (!Multiaddr.isMultiaddr(multiaddr)) {
log.error('multiaddr must be an instance of Multiaddr')
throw errcode(new Error('multiaddr must be an instance of Multiaddr'), codes.ERR_INVALID_PARAMETERS)
} }
}),
// create Address for each address with no duplicates (source) => filter(source, (multiaddr) => addressFilter(peerId, multiaddr)),
return Array.from( (source) => map(source, (multiaddr) => {
new Set(multiaddrs.map(ma => ma.toString()))
)
.map(addr => {
try {
return { return {
multiaddr: new Multiaddr(addr), multiaddr: new Multiaddr(multiaddr.toString()),
isCertified isCertified
} }
} catch (err) { }),
throw errcode(err, codes.ERR_INVALID_PARAMETERS) (source) => all(source)
} )
})
} }
module.exports = PeerStoreAddressBook module.exports = PeerStoreAddressBook

View File

@ -12,6 +12,7 @@ const Store = require('./store')
* @typedef {import('./types').PeerStore} PeerStore * @typedef {import('./types').PeerStore} PeerStore
* @typedef {import('./types').Peer} Peer * @typedef {import('./types').Peer} Peer
* @typedef {import('peer-id')} PeerId * @typedef {import('peer-id')} PeerId
* @typedef {import('multiaddr').Multiaddr} Multiaddr
*/ */
const log = Object.assign(debug('libp2p:peer-store'), { const log = Object.assign(debug('libp2p:peer-store'), {
@ -28,14 +29,15 @@ class DefaultPeerStore extends EventEmitter {
* @param {object} properties * @param {object} properties
* @param {PeerId} properties.peerId * @param {PeerId} properties.peerId
* @param {import('interface-datastore').Datastore} properties.datastore * @param {import('interface-datastore').Datastore} properties.datastore
* @param {(peerId: PeerId, multiaddr: Multiaddr) => Promise<boolean>} properties.addressFilter
*/ */
constructor ({ peerId, datastore }) { constructor ({ peerId, datastore, addressFilter }) {
super() super()
this._peerId = peerId this._peerId = peerId
this._store = new Store(datastore) this._store = new Store(datastore)
this.addressBook = new AddressBook(this.emit.bind(this), this._store) this.addressBook = new AddressBook(this.emit.bind(this), this._store, addressFilter)
this.keyBook = new KeyBook(this.emit.bind(this), this._store) this.keyBook = new KeyBook(this.emit.bind(this), this._store)
this.metadataBook = new MetadataBook(this.emit.bind(this), this._store) this.metadataBook = new MetadataBook(this.emit.bind(this), this._store)
this.protoBook = new ProtoBook(this.emit.bind(this), this._store) this.protoBook = new ProtoBook(this.emit.bind(this), this._store)

View File

@ -100,10 +100,23 @@ class PersistentStore {
throw errcode(new Error('publicKey bytes do not match peer id publicKey bytes'), codes.ERR_INVALID_PARAMETERS) throw errcode(new Error('publicKey bytes do not match peer id publicKey bytes'), codes.ERR_INVALID_PARAMETERS)
} }
// dedupe addresses
const addressSet = new Set()
const buf = PeerPB.encode({ const buf = PeerPB.encode({
addresses: peer.addresses.sort((a, b) => { addresses: peer.addresses
.filter(address => {
if (addressSet.has(address.multiaddr.toString())) {
return false
}
addressSet.add(address.multiaddr.toString())
return true
})
.sort((a, b) => {
return a.multiaddr.toString().localeCompare(b.multiaddr.toString()) return a.multiaddr.toString().localeCompare(b.multiaddr.toString())
}).map(({ multiaddr, isCertified }) => ({ })
.map(({ multiaddr, isCertified }) => ({
multiaddr: multiaddr.bytes, multiaddr: multiaddr.bytes,
isCertified isCertified
})), })),

98
src/types.ts Normal file
View File

@ -0,0 +1,98 @@
import type PeerId from 'peer-id'
import type { Multiaddr } from 'multiaddr'
import type { MultiaddrConnection } from 'libp2p-interfaces/src/transport/types'
export interface ConnectionGater {
/**
* denyDialMultiaddr tests whether we're permitted to Dial the
* specified peer.
*
* This is called by the dialer.connectToPeer implementation before
* dialling a peer.
*
* Return true to prevent dialing the passed peer.
*/
denyDialPeer: (peerId: PeerId) => Promise<boolean>
/**
* denyDialMultiaddr tests whether we're permitted to dial the specified
* multiaddr for the given peer.
*
* This is called by the dialer.connectToPeer implementation after it has
* resolved the peer's addrs, and prior to dialling each.
*
* Return true to prevent dialing the passed peer on the passed multiaddr.
*/
denyDialMultiaddr: (peerId: PeerId, multiaddr: Multiaddr) => Promise<boolean>
/**
* denyInboundConnection tests whether an incipient inbound connection is allowed.
*
* This is called by the upgrader, or by the transport directly (e.g. QUIC,
* Bluetooth), straight after it has accepted a connection from its socket.
*
* Return true to deny the incoming passed connection.
*/
denyInboundConnection: (maConn: MultiaddrConnection) => Promise<boolean>
/**
* denyOutboundConnection tests whether an incipient outbound connection is allowed.
*
* This is called by the upgrader, or by the transport directly (e.g. QUIC,
* Bluetooth), straight after it has created a connection with its socket.
*
* Return true to deny the incoming passed connection.
*/
denyOutboundConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
/**
* denyInboundEncryptedConnection tests whether a given connection, now encrypted,
* is allowed.
*
* This is called by the upgrader, after it has performed the security
* handshake, and before it negotiates the muxer, or by the directly by the
* transport, at the exact same checkpoint.
*
* Return true to deny the passed secured connection.
*/
denyInboundEncryptedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
/**
* denyOutboundEncryptedConnection tests whether a given connection, now encrypted,
* is allowed.
*
* This is called by the upgrader, after it has performed the security
* handshake, and before it negotiates the muxer, or by the directly by the
* transport, at the exact same checkpoint.
*
* Return true to deny the passed secured connection.
*/
denyOutboundEncryptedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
/**
* denyInboundUpgradedConnection tests whether a fully capable connection is allowed.
*
* This is called after encryption has been negotiated and the connection has been
* multiplexed, if a multiplexer is configured.
*
* Return true to deny the passed upgraded connection.
*/
denyInboundUpgradedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
/**
* denyOutboundUpgradedConnection tests whether a fully capable connection is allowed.
*
* This is called after encryption has been negotiated and the connection has been
* multiplexed, if a multiplexer is configured.
*
* Return true to deny the passed upgraded connection.
*/
denyOutboundUpgradedConnection: (peerId: PeerId, maConn: MultiaddrConnection) => Promise<boolean>
/**
* Used by the address book to filter passed addresses.
*
* Return true to allow storing the passed multiaddr for the passed peer.
*/
filterMultiaddrForPeer: (peer: PeerId, multiaddr: Multiaddr) => Promise<boolean>
}

View File

@ -22,6 +22,7 @@ const { codes } = require('./errors')
* @typedef {import('libp2p-interfaces/src/crypto/types').Crypto} Crypto * @typedef {import('libp2p-interfaces/src/crypto/types').Crypto} Crypto
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection * @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('multiaddr').Multiaddr} Multiaddr * @typedef {import('multiaddr').Multiaddr} Multiaddr
* @typedef {import('./types').ConnectionGater} ConnectionGater
*/ */
/** /**
@ -35,6 +36,8 @@ class Upgrader {
/** /**
* @param {object} options * @param {object} options
* @param {PeerId} options.localPeer * @param {PeerId} options.localPeer
* @param {ConnectionGater} options.connectionGater
*
* @param {import('./metrics')} [options.metrics] * @param {import('./metrics')} [options.metrics]
* @param {Map<string, Crypto>} [options.cryptos] * @param {Map<string, Crypto>} [options.cryptos]
* @param {Map<string, MuxerFactory>} [options.muxers] * @param {Map<string, MuxerFactory>} [options.muxers]
@ -44,11 +47,13 @@ class Upgrader {
constructor ({ constructor ({
localPeer, localPeer,
metrics, metrics,
connectionGater,
cryptos = new Map(), cryptos = new Map(),
muxers = new Map(), muxers = new Map(),
onConnectionEnd = () => {}, onConnectionEnd = () => {},
onConnection = () => {} onConnection = () => {}
}) { }) {
this.connectionGater = connectionGater
this.localPeer = localPeer this.localPeer = localPeer
this.metrics = metrics this.metrics = metrics
this.cryptos = cryptos this.cryptos = cryptos
@ -76,6 +81,10 @@ class Upgrader {
let setPeer let setPeer
let proxyPeer let proxyPeer
if (await this.connectionGater.denyInboundConnection(maConn)) {
throw errCode(new Error('The multiaddr connection is blocked by gater.acceptConnection'), codes.ERR_CONNECTION_INTERCEPTED)
}
if (this.metrics) { if (this.metrics) {
({ setTarget: setPeer, proxy: proxyPeer } = mutableProxy()) ({ setTarget: setPeer, proxy: proxyPeer } = mutableProxy())
const idString = (Math.random() * 1e9).toString(36) + Date.now() const idString = (Math.random() * 1e9).toString(36) + Date.now()
@ -99,6 +108,10 @@ class Upgrader {
protocol: cryptoProtocol protocol: cryptoProtocol
} = await this._encryptInbound(this.localPeer, protectedConn, this.cryptos)) } = await this._encryptInbound(this.localPeer, protectedConn, this.cryptos))
if (await this.connectionGater.denyInboundEncryptedConnection(remotePeer, encryptedConn)) {
throw errCode(new Error('The multiaddr connection is blocked by gater.acceptEncryptedConnection'), codes.ERR_CONNECTION_INTERCEPTED)
}
// Multiplex the connection // Multiplex the connection
if (this.muxers.size) { if (this.muxers.size) {
({ stream: upgradedConn, Muxer } = await this._multiplexInbound(encryptedConn, this.muxers)) ({ stream: upgradedConn, Muxer } = await this._multiplexInbound(encryptedConn, this.muxers))
@ -111,6 +124,10 @@ class Upgrader {
throw err throw err
} }
if (await this.connectionGater.denyInboundUpgradedConnection(remotePeer, encryptedConn)) {
throw errCode(new Error('The multiaddr connection is blocked by gater.acceptEncryptedConnection'), codes.ERR_CONNECTION_INTERCEPTED)
}
if (this.metrics) { if (this.metrics) {
this.metrics.updatePlaceholder(proxyPeer, remotePeer) this.metrics.updatePlaceholder(proxyPeer, remotePeer)
setPeer(remotePeer) setPeer(remotePeer)
@ -143,6 +160,10 @@ class Upgrader {
const remotePeerId = PeerId.createFromB58String(idStr) const remotePeerId = PeerId.createFromB58String(idStr)
if (await this.connectionGater.denyOutboundConnection(remotePeerId, maConn)) {
throw errCode(new Error('The multiaddr connection is blocked by connectionGater.denyOutboundConnection'), codes.ERR_CONNECTION_INTERCEPTED)
}
let encryptedConn let encryptedConn
let remotePeer let remotePeer
let upgradedConn let upgradedConn
@ -174,6 +195,10 @@ class Upgrader {
protocol: cryptoProtocol protocol: cryptoProtocol
} = await this._encryptOutbound(this.localPeer, protectedConn, remotePeerId, this.cryptos)) } = await this._encryptOutbound(this.localPeer, protectedConn, remotePeerId, this.cryptos))
if (await this.connectionGater.denyOutboundEncryptedConnection(remotePeer, encryptedConn)) {
throw errCode(new Error('The multiaddr connection is blocked by gater.acceptEncryptedConnection'), codes.ERR_CONNECTION_INTERCEPTED)
}
// Multiplex the connection // Multiplex the connection
if (this.muxers.size) { if (this.muxers.size) {
({ stream: upgradedConn, Muxer } = await this._multiplexOutbound(encryptedConn, this.muxers)) ({ stream: upgradedConn, Muxer } = await this._multiplexOutbound(encryptedConn, this.muxers))
@ -186,6 +211,10 @@ class Upgrader {
throw err throw err
} }
if (await this.connectionGater.denyOutboundUpgradedConnection(remotePeer, encryptedConn)) {
throw errCode(new Error('The multiaddr connection is blocked by gater.acceptEncryptedConnection'), codes.ERR_CONNECTION_INTERCEPTED)
}
if (this.metrics) { if (this.metrics) {
this.metrics.updatePlaceholder(proxyPeer, remotePeer) this.metrics.updatePlaceholder(proxyPeer, remotePeer)
setPeer(remotePeer) setPeer(remotePeer)

View File

@ -7,12 +7,11 @@ const { CLOSED } = require('libp2p-interfaces/src/connection/status')
const delay = require('delay') const delay = require('delay')
const pWaitFor = require('p-wait-for') const pWaitFor = require('p-wait-for')
const peerUtils = require('../utils/creators/peer') const peerUtils = require('../utils/creators/peer')
const mockConnection = require('../utils/mockConnection') const mockConnection = require('../utils/mockConnection')
const baseOptions = require('../utils/base-options.browser') const baseOptions = require('../utils/base-options.browser')
const { codes } = require('../../src/errors')
const listenMultiaddr = '/ip4/127.0.0.1/tcp/15002/ws' const { Multiaddr } = require('multiaddr')
describe('Connection Manager', () => { describe('Connection Manager', () => {
let libp2p let libp2p
@ -27,7 +26,7 @@ describe('Connection Manager', () => {
config: { config: {
peerId: peerIds[0], peerId: peerIds[0],
addresses: { addresses: {
listen: [listenMultiaddr] listen: ['/ip4/127.0.0.1/tcp/0/ws']
}, },
modules: baseOptions.modules modules: baseOptions.modules
} }
@ -305,4 +304,230 @@ describe('libp2p.connections', () => {
await remoteLibp2p.stop() await remoteLibp2p.stop()
}) })
}) })
describe('connection gater', () => {
let libp2p
let remoteLibp2p
beforeEach(async () => {
[remoteLibp2p] = await peerUtils.createPeer({
config: {
peerId: peerIds[1],
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules
}
})
})
afterEach(async () => {
remoteLibp2p && await remoteLibp2p.stop()
libp2p && await libp2p.stop()
})
it('intercept peer dial', async () => {
const denyDialPeer = sinon.stub().returns(true)
;[libp2p] = await peerUtils.createPeer({
config: {
peerId: peerIds[0],
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules,
connectionGater: {
denyDialPeer
}
}
})
await libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.multiaddrs)
await expect(libp2p.dial(remoteLibp2p.peerId))
.to.eventually.be.rejected().with.property('code', codes.ERR_PEER_DIAL_INTERCEPTED)
})
it('intercept addr dial', async () => {
const denyDialMultiaddr = sinon.stub().returns(false)
;[libp2p] = await peerUtils.createPeer({
config: {
peerId: peerIds[0],
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules,
connectionGater: {
denyDialMultiaddr
}
}
})
await libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.multiaddrs)
await libp2p.dialer.connectToPeer(remoteLibp2p.peerId)
const peerIdMultiaddr = new Multiaddr(`/p2p/${remoteLibp2p.peerId}`)
for (const multiaddr of remoteLibp2p.multiaddrs) {
expect(denyDialMultiaddr.calledWith(remoteLibp2p.peerId, multiaddr.encapsulate(peerIdMultiaddr))).to.be.true()
}
})
it('intercept multiaddr store during multiaddr dial', async () => {
const filterMultiaddrForPeer = sinon.stub().returns(true)
;[libp2p] = await peerUtils.createPeer({
config: {
peerId: peerIds[0],
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules,
connectionGater: {
filterMultiaddrForPeer
}
}
})
const peerIdMultiaddr = new Multiaddr(`/p2p/${remoteLibp2p.peerId}`)
const fullMultiaddr = remoteLibp2p.multiaddrs[0].encapsulate(peerIdMultiaddr)
await libp2p.dialer.connectToPeer(fullMultiaddr)
expect(filterMultiaddrForPeer.callCount).to.equal(2)
const args = filterMultiaddrForPeer.getCall(1).args
expect(args[0].toString()).to.equal(remoteLibp2p.peerId.toString())
expect(args[1].toString()).to.equal(fullMultiaddr.toString())
})
it('intercept accept inbound connection', async () => {
const denyInboundConnection = sinon.stub().returns(false)
;[libp2p] = await peerUtils.createPeer({
config: {
peerId: peerIds[0],
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules,
connectionGater: {
denyInboundConnection
}
}
})
await remoteLibp2p.peerStore.addressBook.set(libp2p.peerId, libp2p.multiaddrs)
await remoteLibp2p.dial(libp2p.peerId)
expect(denyInboundConnection.called).to.be.true()
})
it('intercept accept outbound connection', async () => {
const denyOutboundConnection = sinon.stub().returns(false)
;[libp2p] = await peerUtils.createPeer({
config: {
peerId: peerIds[0],
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules,
connectionGater: {
denyOutboundConnection
}
}
})
await libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.multiaddrs)
await libp2p.dial(remoteLibp2p.peerId)
expect(denyOutboundConnection.called).to.be.true()
})
it('intercept inbound encrypted', async () => {
const denyInboundEncryptedConnection = sinon.stub().returns(false)
;[libp2p] = await peerUtils.createPeer({
config: {
peerId: peerIds[0],
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules,
connectionGater: {
denyInboundEncryptedConnection
}
}
})
await remoteLibp2p.peerStore.addressBook.set(libp2p.peerId, libp2p.multiaddrs)
await remoteLibp2p.dial(libp2p.peerId)
expect(denyInboundEncryptedConnection.called).to.be.true()
expect(denyInboundEncryptedConnection.getCall(0)).to.have.nested.property('args[0].id').that.equalBytes(remoteLibp2p.peerId.id)
})
it('intercept outbound encrypted', async () => {
const denyOutboundEncryptedConnection = sinon.stub().returns(false)
;[libp2p] = await peerUtils.createPeer({
config: {
peerId: peerIds[0],
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules,
connectionGater: {
denyOutboundEncryptedConnection
}
}
})
await libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.multiaddrs)
await libp2p.dial(remoteLibp2p.peerId)
expect(denyOutboundEncryptedConnection.called).to.be.true()
expect(denyOutboundEncryptedConnection.getCall(0)).to.have.nested.property('args[0].id').that.equalBytes(remoteLibp2p.peerId.id)
})
it('intercept inbound upgraded', async () => {
const denyInboundUpgradedConnection = sinon.stub().returns(false)
;[libp2p] = await peerUtils.createPeer({
config: {
peerId: peerIds[0],
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules,
connectionGater: {
denyInboundUpgradedConnection
}
}
})
await remoteLibp2p.peerStore.addressBook.set(libp2p.peerId, libp2p.multiaddrs)
await remoteLibp2p.dial(libp2p.peerId)
expect(denyInboundUpgradedConnection.called).to.be.true()
expect(denyInboundUpgradedConnection.getCall(0)).to.have.nested.property('args[0].id').that.equalBytes(remoteLibp2p.peerId.id)
})
it('intercept outbound upgraded', async () => {
const denyOutboundUpgradedConnection = sinon.stub().returns(false)
;[libp2p] = await peerUtils.createPeer({
config: {
peerId: peerIds[0],
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules,
connectionGater: {
denyOutboundUpgradedConnection
}
}
})
await libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.multiaddrs)
await libp2p.dial(remoteLibp2p.peerId)
expect(denyOutboundUpgradedConnection.called).to.be.true()
expect(denyOutboundUpgradedConnection.getCall(0)).to.have.nested.property('args[0].id').that.equalBytes(remoteLibp2p.peerId.id)
})
})
}) })

View File

@ -27,7 +27,7 @@ const TransportManager = require('../../src/transport-manager')
const { codes: ErrorCodes } = require('../../src/errors') const { codes: ErrorCodes } = require('../../src/errors')
const Protector = require('../../src/pnet') const Protector = require('../../src/pnet')
const swarmKeyBuffer = uint8ArrayFromString(require('../fixtures/swarm.key')) const swarmKeyBuffer = uint8ArrayFromString(require('../fixtures/swarm.key'))
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const mockUpgrader = require('../utils/mockUpgrader') const mockUpgrader = require('../utils/mockUpgrader')
const createMockConnection = require('../utils/mockConnection') const createMockConnection = require('../utils/mockConnection')
const Peers = require('../fixtures/peers') const Peers = require('../fixtures/peers')
@ -37,6 +37,7 @@ const listenAddr = new Multiaddr('/ip4/127.0.0.1/tcp/0')
const unsupportedAddr = new Multiaddr('/ip4/127.0.0.1/tcp/9999/ws/p2p/QmckxVrJw1Yo8LqvmDJNUmdAsKtSbiKWmrXJFyKmUraBoN') const unsupportedAddr = new Multiaddr('/ip4/127.0.0.1/tcp/9999/ws/p2p/QmckxVrJw1Yo8LqvmDJNUmdAsKtSbiKWmrXJFyKmUraBoN')
describe('Dialing (direct, TCP)', () => { describe('Dialing (direct, TCP)', () => {
const connectionGater = mockConnectionGater()
let remoteTM let remoteTM
let localTM let localTM
let peerStore let peerStore
@ -50,7 +51,8 @@ describe('Dialing (direct, TCP)', () => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId: remotePeerId, peerId: remotePeerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
remoteTM = new TransportManager({ remoteTM = new TransportManager({
libp2p: { libp2p: {
@ -67,7 +69,8 @@ describe('Dialing (direct, TCP)', () => {
peerId: localPeerId, peerId: localPeerId,
peerStore: new PeerStore({ peerStore: new PeerStore({
peerId: localPeerId, peerId: localPeerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
}, },
upgrader: mockUpgrader upgrader: mockUpgrader
@ -86,7 +89,11 @@ describe('Dialing (direct, TCP)', () => {
}) })
it('should be able to connect to a remote node via its multiaddr', async () => { it('should be able to connect to a remote node via its multiaddr', async () => {
const dialer = new Dialer({ transportManager: localTM, peerStore }) const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
const connection = await dialer.connectToPeer(remoteAddr) const connection = await dialer.connectToPeer(remoteAddr)
expect(connection).to.exist() expect(connection).to.exist()
@ -94,14 +101,22 @@ describe('Dialing (direct, TCP)', () => {
}) })
it('should be able to connect to a remote node via its stringified multiaddr', async () => { it('should be able to connect to a remote node via its stringified multiaddr', async () => {
const dialer = new Dialer({ transportManager: localTM, peerStore }) const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
const connection = await dialer.connectToPeer(remoteAddr.toString()) const connection = await dialer.connectToPeer(remoteAddr.toString())
expect(connection).to.exist() expect(connection).to.exist()
await connection.close() await connection.close()
}) })
it('should fail to connect to an unsupported multiaddr', async () => { it('should fail to connect to an unsupported multiaddr', async () => {
const dialer = new Dialer({ transportManager: localTM, peerStore }) const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
await expect(dialer.connectToPeer(unsupportedAddr)) await expect(dialer.connectToPeer(unsupportedAddr))
.to.eventually.be.rejectedWith(Error) .to.eventually.be.rejectedWith(Error)
@ -109,7 +124,11 @@ describe('Dialing (direct, TCP)', () => {
}) })
it('should fail to connect if peer has no known addresses', async () => { it('should fail to connect if peer has no known addresses', async () => {
const dialer = new Dialer({ transportManager: localTM, peerStore }) const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
const peerId = await PeerId.createFromJSON(Peers[1]) const peerId = await PeerId.createFromJSON(Peers[1])
await expect(dialer.connectToPeer(peerId)) await expect(dialer.connectToPeer(peerId))
@ -121,11 +140,13 @@ describe('Dialing (direct, TCP)', () => {
const peerId = await PeerId.createFromJSON(Peers[0]) const peerId = await PeerId.createFromJSON(Peers[0])
const peerStore = new PeerStore({ const peerStore = new PeerStore({
peerId, peerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
const dialer = new Dialer({ const dialer = new Dialer({
transportManager: localTM, transportManager: localTM,
peerStore peerStore,
connectionGater
}) })
await peerStore.addressBook.set(peerId, remoteTM.getAddrs()) await peerStore.addressBook.set(peerId, remoteTM.getAddrs())
@ -143,7 +164,8 @@ describe('Dialing (direct, TCP)', () => {
add: () => {}, add: () => {},
getMultiaddrsForPeer: () => [unsupportedAddr] getMultiaddrsForPeer: () => [unsupportedAddr]
} }
} },
connectionGater
}) })
const peerId = await PeerId.createFromJSON(Peers[0]) const peerId = await PeerId.createFromJSON(Peers[0])
@ -161,7 +183,8 @@ describe('Dialing (direct, TCP)', () => {
add: () => { }, add: () => { },
getMultiaddrsForPeer: () => [...remoteAddrs, unsupportedAddr] getMultiaddrsForPeer: () => [...remoteAddrs, unsupportedAddr]
} }
} },
connectionGater
}) })
const peerId = await PeerId.createFromJSON(Peers[0]) const peerId = await PeerId.createFromJSON(Peers[0])
@ -176,7 +199,8 @@ describe('Dialing (direct, TCP)', () => {
const dialer = new Dialer({ const dialer = new Dialer({
transportManager: localTM, transportManager: localTM,
peerStore, peerStore,
dialTimeout: 50 dialTimeout: 50,
connectionGater
}) })
sinon.stub(localTM, 'dial').callsFake(async (addr, options) => { sinon.stub(localTM, 'dial').callsFake(async (addr, options) => {
expect(options.signal).to.exist() expect(options.signal).to.exist()
@ -206,7 +230,8 @@ describe('Dialing (direct, TCP)', () => {
add: () => {}, add: () => {},
getMultiaddrsForPeer: () => addrs getMultiaddrsForPeer: () => addrs
} }
} },
connectionGater
}) })
expect(dialer.tokens).to.have.length(2) expect(dialer.tokens).to.have.length(2)

View File

@ -21,6 +21,7 @@ const addressSort = require('libp2p-utils/src/address-sort')
const PeerStore = require('../../src/peer-store') const PeerStore = require('../../src/peer-store')
const TransportManager = require('../../src/transport-manager') const TransportManager = require('../../src/transport-manager')
const Libp2p = require('../../src') const Libp2p = require('../../src')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser') const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
const mockUpgrader = require('../utils/mockUpgrader') const mockUpgrader = require('../utils/mockUpgrader')
@ -30,6 +31,7 @@ const unsupportedAddr = new Multiaddr('/ip4/127.0.0.1/tcp/9999/ws/p2p/QmckxVrJw1
const remoteAddr = MULTIADDRS_WEBSOCKETS[0] const remoteAddr = MULTIADDRS_WEBSOCKETS[0]
describe('Dialing (direct, WebSockets)', () => { describe('Dialing (direct, WebSockets)', () => {
const connectionGater = mockConnectionGater()
let localTM let localTM
let peerStore let peerStore
let peerId let peerId
@ -38,7 +40,8 @@ describe('Dialing (direct, WebSockets)', () => {
[peerId] = await createPeerId() [peerId] = await createPeerId()
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId, peerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
localTM = new TransportManager({ localTM = new TransportManager({
libp2p: {}, libp2p: {},
@ -54,13 +57,21 @@ describe('Dialing (direct, WebSockets)', () => {
}) })
it('should have appropriate defaults', () => { it('should have appropriate defaults', () => {
const dialer = new Dialer({ transportManager: localTM, peerStore }) const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
expect(dialer.maxParallelDials).to.equal(Constants.MAX_PARALLEL_DIALS) expect(dialer.maxParallelDials).to.equal(Constants.MAX_PARALLEL_DIALS)
expect(dialer.timeout).to.equal(Constants.DIAL_TIMEOUT) expect(dialer.timeout).to.equal(Constants.DIAL_TIMEOUT)
}) })
it('should limit the number of tokens it provides', () => { it('should limit the number of tokens it provides', () => {
const dialer = new Dialer({ transportManager: localTM, peerStore }) const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
const maxPerPeer = Constants.MAX_PER_PEER_DIALS const maxPerPeer = Constants.MAX_PER_PEER_DIALS
expect(dialer.tokens).to.have.length(Constants.MAX_PARALLEL_DIALS) expect(dialer.tokens).to.have.length(Constants.MAX_PARALLEL_DIALS)
const tokens = dialer.getTokens(maxPerPeer + 1) const tokens = dialer.getTokens(maxPerPeer + 1)
@ -69,14 +80,22 @@ describe('Dialing (direct, WebSockets)', () => {
}) })
it('should not return tokens if non are left', () => { it('should not return tokens if non are left', () => {
const dialer = new Dialer({ transportManager: localTM, peerStore }) const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
sinon.stub(dialer, 'tokens').value([]) sinon.stub(dialer, 'tokens').value([])
const tokens = dialer.getTokens(1) const tokens = dialer.getTokens(1)
expect(tokens.length).to.equal(0) expect(tokens.length).to.equal(0)
}) })
it('should NOT be able to return a token twice', () => { it('should NOT be able to return a token twice', () => {
const dialer = new Dialer({ transportManager: localTM, peerStore }) const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
const tokens = dialer.getTokens(1) const tokens = dialer.getTokens(1)
expect(tokens).to.have.length(1) expect(tokens).to.have.length(1)
expect(dialer.tokens).to.have.length(Constants.MAX_PARALLEL_DIALS - 1) expect(dialer.tokens).to.have.length(Constants.MAX_PARALLEL_DIALS - 1)
@ -93,7 +112,8 @@ describe('Dialing (direct, WebSockets)', () => {
add: () => {}, add: () => {},
getMultiaddrsForPeer: () => [remoteAddr] getMultiaddrsForPeer: () => [remoteAddr]
} }
} },
connectionGater
}) })
const connection = await dialer.connectToPeer(remoteAddr) const connection = await dialer.connectToPeer(remoteAddr)
@ -109,7 +129,8 @@ describe('Dialing (direct, WebSockets)', () => {
add: () => {}, add: () => {},
getMultiaddrsForPeer: () => [remoteAddr] getMultiaddrsForPeer: () => [remoteAddr]
} }
} },
connectionGater
}) })
const connection = await dialer.connectToPeer(remoteAddr.toString()) const connection = await dialer.connectToPeer(remoteAddr.toString())
@ -118,7 +139,11 @@ describe('Dialing (direct, WebSockets)', () => {
}) })
it('should fail to connect to an unsupported multiaddr', async () => { it('should fail to connect to an unsupported multiaddr', async () => {
const dialer = new Dialer({ transportManager: localTM, peerStore }) const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
await expect(dialer.connectToPeer(unsupportedAddr)) await expect(dialer.connectToPeer(unsupportedAddr))
.to.eventually.be.rejectedWith(AggregateError) .to.eventually.be.rejectedWith(AggregateError)
@ -132,7 +157,8 @@ describe('Dialing (direct, WebSockets)', () => {
add: () => {}, add: () => {},
getMultiaddrsForPeer: () => [remoteAddr] getMultiaddrsForPeer: () => [remoteAddr]
} }
} },
connectionGater
}) })
const connection = await dialer.connectToPeer(peerId) const connection = await dialer.connectToPeer(peerId)
@ -148,7 +174,8 @@ describe('Dialing (direct, WebSockets)', () => {
set: () => {}, set: () => {},
getMultiaddrsForPeer: () => [unsupportedAddr] getMultiaddrsForPeer: () => [unsupportedAddr]
} }
} },
connectionGater
}) })
await expect(dialer.connectToPeer(peerId)) await expect(dialer.connectToPeer(peerId))
@ -164,7 +191,8 @@ describe('Dialing (direct, WebSockets)', () => {
add: () => {}, add: () => {},
getMultiaddrsForPeer: () => [remoteAddr] getMultiaddrsForPeer: () => [remoteAddr]
} }
} },
connectionGater
}) })
sinon.stub(localTM, 'dial').callsFake(async (addr, options) => { sinon.stub(localTM, 'dial').callsFake(async (addr, options) => {
expect(options.signal).to.exist() expect(options.signal).to.exist()
@ -191,7 +219,8 @@ describe('Dialing (direct, WebSockets)', () => {
add: () => { }, add: () => { },
getMultiaddrsForPeer: () => Array.from({ length: 11 }, (_, i) => new Multiaddr(`/ip4/127.0.0.1/tcp/1500${i}/ws/p2p/12D3KooWHFKTMzwerBtsVmtz4ZZEQy2heafxzWw6wNn5PPYkBxJ5`)) getMultiaddrsForPeer: () => Array.from({ length: 11 }, (_, i) => new Multiaddr(`/ip4/127.0.0.1/tcp/1500${i}/ws/p2p/12D3KooWHFKTMzwerBtsVmtz4ZZEQy2heafxzWw6wNn5PPYkBxJ5`))
} }
} },
connectionGater
}) })
await expect(dialer.connectToPeer(remoteAddr)) await expect(dialer.connectToPeer(remoteAddr))
@ -214,7 +243,8 @@ describe('Dialing (direct, WebSockets)', () => {
transportManager: localTM, transportManager: localTM,
addressSorter: addressSort.publicAddressesFirst, addressSorter: addressSort.publicAddressesFirst,
maxParallelDials: 3, maxParallelDials: 3,
peerStore peerStore,
connectionGater
}) })
// Inject data in the AddressBook // Inject data in the AddressBook
@ -240,7 +270,8 @@ describe('Dialing (direct, WebSockets)', () => {
set: () => {}, set: () => {},
getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr] getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
} }
} },
connectionGater
}) })
expect(dialer.tokens).to.have.length(2) expect(dialer.tokens).to.have.length(2)
@ -278,7 +309,8 @@ describe('Dialing (direct, WebSockets)', () => {
set: () => {}, set: () => {},
getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr] getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
} }
} },
connectionGater
}) })
expect(dialer.tokens).to.have.length(2) expect(dialer.tokens).to.have.length(2)
@ -320,7 +352,8 @@ describe('Dialing (direct, WebSockets)', () => {
addressBook: { addressBook: {
set: () => { } set: () => { }
} }
} },
connectionGater
}) })
sinon.stub(dialer, '_createDialTarget').callsFake(() => { sinon.stub(dialer, '_createDialTarget').callsFake(() => {
@ -365,7 +398,8 @@ describe('Dialing (direct, WebSockets)', () => {
filter: filters.all filter: filters.all
} }
} }
} },
connectionGater
}) })
expect(libp2p.dialer).to.exist() expect(libp2p.dialer).to.exist()

View File

@ -23,10 +23,12 @@ const pkg = require('../../package.json')
const AddressManager = require('../../src/address-manager') const AddressManager = require('../../src/address-manager')
const { MemoryDatastore } = require('datastore-core/memory') const { MemoryDatastore } = require('datastore-core/memory')
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser') const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const remoteAddr = MULTIADDRS_WEBSOCKETS[0] const remoteAddr = MULTIADDRS_WEBSOCKETS[0]
const listenMaddrs = [new Multiaddr('/ip4/127.0.0.1/tcp/15002/ws')] const listenMaddrs = [new Multiaddr('/ip4/127.0.0.1/tcp/15002/ws')]
describe('Identify', () => { describe('Identify', () => {
const connectionGater = mockConnectionGater()
let localPeer, localPeerStore, localAddressManager let localPeer, localPeerStore, localAddressManager
let remotePeer, remotePeerStore, remoteAddressManager let remotePeer, remotePeerStore, remoteAddressManager
const protocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH] const protocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH]
@ -39,13 +41,15 @@ describe('Identify', () => {
localPeerStore = new PeerStore({ localPeerStore = new PeerStore({
peerId: localPeer, peerId: localPeer,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
await localPeerStore.protoBook.set(localPeer, protocols) await localPeerStore.protoBook.set(localPeer, protocols)
remotePeerStore = new PeerStore({ remotePeerStore = new PeerStore({
peerId: remotePeer, peerId: remotePeer,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
await remotePeerStore.protoBook.set(remotePeer, protocols) await remotePeerStore.protoBook.set(remotePeer, protocols)
@ -230,7 +234,8 @@ describe('Identify', () => {
const agentVersion = 'js-project/1.0.0' const agentVersion = 'js-project/1.0.0'
const peerStore = new PeerStore({ const peerStore = new PeerStore({
peerId: localPeer, peerId: localPeer,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
sinon.spy(peerStore.metadataBook, 'setValue') sinon.spy(peerStore.metadataBook, 'setValue')
@ -272,7 +277,8 @@ describe('Identify', () => {
const localPeerStore = new PeerStore({ const localPeerStore = new PeerStore({
peerId: localPeer, peerId: localPeer,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
await localPeerStore.protoBook.set(localPeer, storedProtocols) await localPeerStore.protoBook.set(localPeer, storedProtocols)
@ -290,7 +296,8 @@ describe('Identify', () => {
const remotePeerStore = new PeerStore({ const remotePeerStore = new PeerStore({
peerId: remotePeer, peerId: remotePeer,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
await remotePeerStore.protoBook.set(remotePeer, storedProtocols) await remotePeerStore.protoBook.set(remotePeer, storedProtocols)
@ -352,7 +359,8 @@ describe('Identify', () => {
const localPeerStore = new PeerStore({ const localPeerStore = new PeerStore({
peerId: localPeer, peerId: localPeer,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
await localPeerStore.protoBook.set(localPeer, storedProtocols) await localPeerStore.protoBook.set(localPeer, storedProtocols)
@ -370,7 +378,8 @@ describe('Identify', () => {
const remotePeerStore = new PeerStore({ const remotePeerStore = new PeerStore({
peerId: remotePeer, peerId: remotePeer,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
await remotePeerStore.protoBook.set(remotePeer, storedProtocols) await remotePeerStore.protoBook.set(remotePeer, storedProtocols)

View File

@ -13,7 +13,7 @@ const { MemoryDatastore } = require('datastore-core/memory')
const PeerStore = require('../../src/peer-store') const PeerStore = require('../../src/peer-store')
const Envelope = require('../../src/record/envelope') const Envelope = require('../../src/record/envelope')
const PeerRecord = require('../../src/record/peer-record') const PeerRecord = require('../../src/record/peer-record')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const peerUtils = require('../utils/creators/peer') const peerUtils = require('../utils/creators/peer')
const { const {
codes: { ERR_INVALID_PARAMETERS } codes: { ERR_INVALID_PARAMETERS }
@ -29,6 +29,7 @@ const addr2 = new Multiaddr('/ip4/20.0.0.1/tcp/8001')
const addr3 = new Multiaddr('/ip4/127.0.0.1/tcp/8002') const addr3 = new Multiaddr('/ip4/127.0.0.1/tcp/8002')
describe('addressBook', () => { describe('addressBook', () => {
const connectionGater = mockConnectionGater()
let peerId let peerId
before(async () => { before(async () => {
@ -44,7 +45,8 @@ describe('addressBook', () => {
beforeEach(() => { beforeEach(() => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId, peerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
ab = peerStore.addressBook ab = peerStore.addressBook
}) })
@ -164,7 +166,8 @@ describe('addressBook', () => {
beforeEach(() => { beforeEach(() => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId, peerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
ab = peerStore.addressBook ab = peerStore.addressBook
}) })
@ -323,7 +326,8 @@ describe('addressBook', () => {
beforeEach(() => { beforeEach(() => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId, peerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
ab = peerStore.addressBook ab = peerStore.addressBook
}) })
@ -364,7 +368,8 @@ describe('addressBook', () => {
beforeEach(() => { beforeEach(() => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId, peerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
ab = peerStore.addressBook ab = peerStore.addressBook
}) })
@ -418,7 +423,8 @@ describe('addressBook', () => {
beforeEach(() => { beforeEach(() => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId, peerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
ab = peerStore.addressBook ab = peerStore.addressBook
}) })
@ -478,7 +484,8 @@ describe('addressBook', () => {
beforeEach(() => { beforeEach(() => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId, peerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
ab = peerStore.addressBook ab = peerStore.addressBook
}) })
@ -670,7 +677,8 @@ describe('addressBook', () => {
beforeEach(() => { beforeEach(() => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId, peerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
ab = peerStore.addressBook ab = peerStore.addressBook
}) })

View File

@ -8,6 +8,7 @@ const { Multiaddr } = require('multiaddr')
const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string') const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string')
const { MemoryDatastore } = require('datastore-core/memory') const { MemoryDatastore } = require('datastore-core/memory')
const peerUtils = require('../utils/creators/peer') const peerUtils = require('../utils/creators/peer')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const addr1 = new Multiaddr('/ip4/127.0.0.1/tcp/8000') const addr1 = new Multiaddr('/ip4/127.0.0.1/tcp/8000')
const addr2 = new Multiaddr('/ip4/127.0.0.1/tcp/8001') const addr2 = new Multiaddr('/ip4/127.0.0.1/tcp/8001')
@ -23,6 +24,7 @@ const proto3 = '/protocol3'
*/ */
describe('peer-store', () => { describe('peer-store', () => {
const connectionGater = mockConnectionGater()
let peerIds let peerIds
before(async () => { before(async () => {
peerIds = await peerUtils.createPeerId({ peerIds = await peerUtils.createPeerId({
@ -37,7 +39,8 @@ describe('peer-store', () => {
beforeEach(() => { beforeEach(() => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId: peerIds[4], peerId: peerIds[4],
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
}) })
@ -66,7 +69,8 @@ describe('peer-store', () => {
beforeEach(async () => { beforeEach(async () => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId: peerIds[4], peerId: peerIds[4],
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
// Add peer0 with { addr1, addr2 } and { proto1 } // Add peer0 with { addr1, addr2 } and { proto1 }
@ -170,7 +174,8 @@ describe('peer-store', () => {
beforeEach(() => { beforeEach(() => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId: peerIds[4], peerId: peerIds[4],
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
}) })

View File

@ -9,7 +9,7 @@ const { MemoryDatastore } = require('datastore-core/memory')
const Topology = require('libp2p-interfaces/src/topology/multicodec-topology') const Topology = require('libp2p-interfaces/src/topology/multicodec-topology')
const PeerStore = require('../../src/peer-store') const PeerStore = require('../../src/peer-store')
const Registrar = require('../../src/registrar') const Registrar = require('../../src/registrar')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const createMockConnection = require('../utils/mockConnection') const createMockConnection = require('../utils/mockConnection')
const peerUtils = require('../utils/creators/peer') const peerUtils = require('../utils/creators/peer')
const baseOptions = require('../utils/base-options.browser') const baseOptions = require('../utils/base-options.browser')
@ -17,6 +17,7 @@ const baseOptions = require('../utils/base-options.browser')
const multicodec = '/test/1.0.0' const multicodec = '/test/1.0.0'
describe('registrar', () => { describe('registrar', () => {
const connectionGater = mockConnectionGater()
let peerStore let peerStore
let registrar let registrar
let peerId let peerId
@ -29,7 +30,8 @@ describe('registrar', () => {
beforeEach(() => { beforeEach(() => {
peerStore = new PeerStore({ peerStore = new PeerStore({
peerId, peerId,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
registrar = new Registrar({ peerStore, connectionManager: new EventEmitter() }) registrar = new Registrar({ peerStore, connectionManager: new EventEmitter() })
}) })

View File

@ -14,12 +14,14 @@ const mockUpgrader = require('../utils/mockUpgrader')
const sinon = require('sinon') const sinon = require('sinon')
const Peers = require('../fixtures/peers') const Peers = require('../fixtures/peers')
const pWaitFor = require('p-wait-for') const pWaitFor = require('p-wait-for')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const addrs = [ const addrs = [
new Multiaddr('/ip4/127.0.0.1/tcp/0'), new Multiaddr('/ip4/127.0.0.1/tcp/0'),
new Multiaddr('/ip4/127.0.0.1/tcp/0') new Multiaddr('/ip4/127.0.0.1/tcp/0')
] ]
describe('Transport Manager (TCP)', () => { describe('Transport Manager (TCP)', () => {
const connectionGater = mockConnectionGater()
let tm let tm
let localPeer let localPeer
@ -35,7 +37,8 @@ describe('Transport Manager (TCP)', () => {
addressManager: new AddressManager({ listen: addrs }), addressManager: new AddressManager({ listen: addrs }),
peerStore: new PeerStore({ peerStore: new PeerStore({
peerId: localPeer, peerId: localPeer,
datastore: new MemoryDatastore() datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
}) })
}, },
upgrader: mockUpgrader, upgrader: mockUpgrader,

View File

@ -18,7 +18,7 @@ const swarmKeyBuffer = uint8ArrayFromString(require('../fixtures/swarm.key'))
const Libp2p = require('../../src') const Libp2p = require('../../src')
const Upgrader = require('../../src/upgrader') const Upgrader = require('../../src/upgrader')
const { codes } = require('../../src/errors') const { codes } = require('../../src/errors')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const mockMultiaddrConnPair = require('../utils/mockMultiaddrConn') const mockMultiaddrConnPair = require('../utils/mockMultiaddrConn')
const Peers = require('../fixtures/peers') const Peers = require('../fixtures/peers')
const addrs = [ const addrs = [
@ -31,6 +31,17 @@ describe('Upgrader', () => {
let remoteUpgrader let remoteUpgrader
let localPeer let localPeer
let remotePeer let remotePeer
const connectionGater = mockConnectionGater()
const mockConnectionManager = {
gater: {
allowDialPeer: async () => true,
allowDialMultiaddr: async () => true,
acceptConnection: async () => true,
acceptEncryptedConnection: async () => true,
acceptUpgradedConnection: async () => true
}
}
before(async () => { before(async () => {
([ ([
@ -42,10 +53,14 @@ describe('Upgrader', () => {
])) ]))
localUpgrader = new Upgrader({ localUpgrader = new Upgrader({
localPeer connectionManager: mockConnectionManager,
localPeer,
connectionGater
}) })
remoteUpgrader = new Upgrader({ remoteUpgrader = new Upgrader({
localPeer: remotePeer connectionManager: mockConnectionManager,
localPeer: remotePeer,
connectionGater
}) })
localUpgrader.protocols.set('/echo/1.0.0', ({ stream }) => pipe(stream, stream)) localUpgrader.protocols.set('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
@ -321,6 +336,7 @@ describe('Upgrader', () => {
describe('libp2p.upgrader', () => { describe('libp2p.upgrader', () => {
let peers let peers
let libp2p let libp2p
const connectionGater = mockConnectionGater()
before(async () => { before(async () => {
peers = await Promise.all([ peers = await Promise.all([
@ -392,8 +408,10 @@ describe('libp2p.upgrader', () => {
const remoteUpgrader = new Upgrader({ const remoteUpgrader = new Upgrader({
localPeer: remotePeer, localPeer: remotePeer,
connectionManager: libp2p.connectionManager,
muxers: new Map([[Muxer.multicodec, Muxer]]), muxers: new Map([[Muxer.multicodec, Muxer]]),
cryptos: new Map([[Crypto.protocol, Crypto]]) cryptos: new Map([[Crypto.protocol, Crypto]]),
connectionGater
}) })
remoteUpgrader.protocols.set('/echo/1.0.0', echoHandler) remoteUpgrader.protocols.set('/echo/1.0.0', echoHandler)
@ -424,8 +442,10 @@ describe('libp2p.upgrader', () => {
const remoteUpgrader = new Upgrader({ const remoteUpgrader = new Upgrader({
localPeer: remotePeer, localPeer: remotePeer,
connectionManager: libp2p.connectionManager,
muxers: new Map([[Muxer.multicodec, Muxer]]), muxers: new Map([[Muxer.multicodec, Muxer]]),
cryptos: new Map([[Crypto.protocol, Crypto]]) cryptos: new Map([[Crypto.protocol, Crypto]]),
connectionGater
}) })
const { inbound, outbound } = mockMultiaddrConnPair({ addrs, remotePeer }) const { inbound, outbound } = mockMultiaddrConnPair({ addrs, remotePeer })

View File

@ -0,0 +1,19 @@
'use strict'
function mockConnectionGater () {
return {
denyDialPeer: async () => Promise.resolve(false),
denyDialMultiaddr: async () => Promise.resolve(false),
denyInboundConnection: async () => Promise.resolve(false),
denyOutboundConnection: async () => Promise.resolve(false),
denyInboundEncryptedConnection: async () => Promise.resolve(false),
denyOutboundEncryptedConnection: async () => Promise.resolve(false),
denyInboundUpgradedConnection: async () => Promise.resolve(false),
denyOutboundUpgradedConnection: async () => Promise.resolve(false),
filterMultiaddrForPeer: async () => Promise.resolve(true)
}
}
module.exports = {
mockConnectionGater
}