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)
- [Configuring Dialing](#configuring-dialing)
- [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 Metrics](#configuring-metrics)
- [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
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-filter": "^1.0.1",
"it-first": "^1.0.4",
"it-foreach": "^0.1.1",
"it-handshake": "^2.0.0",
"it-length-prefixed": "^5.0.2",
"it-map": "^1.0.4",

View File

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

View File

@ -1,6 +1,9 @@
'use strict'
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'), {
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').Address} Address
* @typedef {import('../transport-manager')} TransportManager
* @typedef {import('../types').ConnectionGater} ConnectionGater
*/
/**
* @typedef {Object} DialerProperties
* @property {PeerStore} peerStore
* @property {TransportManager} transportManager
* @property {ConnectionGater} connectionGater
*
* @typedef {(addr:Multiaddr) => Promise<string[]>} Resolver
*
@ -70,6 +75,7 @@ class Dialer {
constructor ({
transportManager,
peerStore,
connectionGater,
addressSorter = publicAddressesFirst,
maxParallelDials = MAX_PARALLEL_DIALS,
maxAddrsToDial = MAX_ADDRS_TO_DIAL,
@ -78,6 +84,7 @@ class Dialer {
resolvers = {},
metrics
}) {
this.connectionGater = connectionGater
this.transportManager = transportManager
this.peerStore = peerStore
this.addressSorter = addressSorter
@ -136,6 +143,12 @@ class Dialer {
* @returns {Promise<Connection>}
*/
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)
if (!dialTarget.addrs.length) {
@ -203,7 +216,13 @@ class Dialer {
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
// 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',
DHT_NOT_STARTED: 'ERR_DHT_NOT_STARTED',
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_CONNECTION_ENDED: 'ERR_CONNECTION_ENDED',
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('interface-datastore').Datastore} Datastore
* @typedef {import('./pnet')} Protector
* @typedef {import('./types').ConnectionGater} ConnectionGater
* @typedef {Object} PersistentPeerStoreOptions
* @property {number} [threshold]
*/
@ -106,6 +107,7 @@ const { updateSelfPeerRecord } = require('./record/utils')
* @property {Libp2pModules} modules libp2p modules to use
* @property {import('./address-manager').AddressManagerOptions} [addresses]
* @property {import('./connection-manager').ConnectionManagerOptions} [connectionManager]
* @property {Partial<import('./types').ConnectionGater>} [connectionGater]
* @property {Datastore} [datastore]
* @property {import('./dialer').DialerOptions} [dialer]
* @property {import('./identify/index').HostProperties} [host] libp2p host
@ -172,10 +174,25 @@ class Libp2p extends EventEmitter {
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} */
this.peerStore = new PeerStore({
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}
@ -220,6 +237,7 @@ class Libp2p extends EventEmitter {
// Setup the Upgrader
this.upgrader = new Upgrader({
connectionGater: this.connectionGater,
localPeer: this.peerId,
metrics: this.metrics,
onConnection: (connection) => this.connectionManager.onConnect(connection),
@ -262,6 +280,7 @@ class Libp2p extends EventEmitter {
this.dialer = new Dialer({
transportManager: this.transportManager,
connectionGater: this.connectionGater,
peerStore: this.peerStore,
metrics: this.metrics,
...this._options.dialer

View File

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

View File

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

View File

@ -100,13 +100,26 @@ class PersistentStore {
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({
addresses: peer.addresses.sort((a, b) => {
return a.multiaddr.toString().localeCompare(b.multiaddr.toString())
}).map(({ multiaddr, isCertified }) => ({
multiaddr: multiaddr.bytes,
isCertified
})),
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())
})
.map(({ multiaddr, isCertified }) => ({
multiaddr: multiaddr.bytes,
isCertified
})),
protocols: peer.protocols.sort(),
pubKey: peer.pubKey ? marshalPublicKey(peer.pubKey) : undefined,
metadata: [...peer.metadata.keys()].sort().map(key => ({ key, value: peer.metadata.get(key) })),

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/connection').Connection} Connection
* @typedef {import('multiaddr').Multiaddr} Multiaddr
* @typedef {import('./types').ConnectionGater} ConnectionGater
*/
/**
@ -35,6 +36,8 @@ class Upgrader {
/**
* @param {object} options
* @param {PeerId} options.localPeer
* @param {ConnectionGater} options.connectionGater
*
* @param {import('./metrics')} [options.metrics]
* @param {Map<string, Crypto>} [options.cryptos]
* @param {Map<string, MuxerFactory>} [options.muxers]
@ -44,11 +47,13 @@ class Upgrader {
constructor ({
localPeer,
metrics,
connectionGater,
cryptos = new Map(),
muxers = new Map(),
onConnectionEnd = () => {},
onConnection = () => {}
}) {
this.connectionGater = connectionGater
this.localPeer = localPeer
this.metrics = metrics
this.cryptos = cryptos
@ -76,6 +81,10 @@ class Upgrader {
let setPeer
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) {
({ setTarget: setPeer, proxy: proxyPeer } = mutableProxy())
const idString = (Math.random() * 1e9).toString(36) + Date.now()
@ -99,6 +108,10 @@ class Upgrader {
protocol: cryptoProtocol
} = 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
if (this.muxers.size) {
({ stream: upgradedConn, Muxer } = await this._multiplexInbound(encryptedConn, this.muxers))
@ -111,6 +124,10 @@ class Upgrader {
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) {
this.metrics.updatePlaceholder(proxyPeer, remotePeer)
setPeer(remotePeer)
@ -143,6 +160,10 @@ class Upgrader {
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 remotePeer
let upgradedConn
@ -174,6 +195,10 @@ class Upgrader {
protocol: cryptoProtocol
} = 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
if (this.muxers.size) {
({ stream: upgradedConn, Muxer } = await this._multiplexOutbound(encryptedConn, this.muxers))
@ -186,6 +211,10 @@ class Upgrader {
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) {
this.metrics.updatePlaceholder(proxyPeer, remotePeer)
setPeer(remotePeer)

View File

@ -7,12 +7,11 @@ const { CLOSED } = require('libp2p-interfaces/src/connection/status')
const delay = require('delay')
const pWaitFor = require('p-wait-for')
const peerUtils = require('../utils/creators/peer')
const mockConnection = require('../utils/mockConnection')
const baseOptions = require('../utils/base-options.browser')
const listenMultiaddr = '/ip4/127.0.0.1/tcp/15002/ws'
const { codes } = require('../../src/errors')
const { Multiaddr } = require('multiaddr')
describe('Connection Manager', () => {
let libp2p
@ -27,7 +26,7 @@ describe('Connection Manager', () => {
config: {
peerId: peerIds[0],
addresses: {
listen: [listenMultiaddr]
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
modules: baseOptions.modules
}
@ -305,4 +304,230 @@ describe('libp2p.connections', () => {
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 Protector = require('../../src/pnet')
const swarmKeyBuffer = uint8ArrayFromString(require('../fixtures/swarm.key'))
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const mockUpgrader = require('../utils/mockUpgrader')
const createMockConnection = require('../utils/mockConnection')
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')
describe('Dialing (direct, TCP)', () => {
const connectionGater = mockConnectionGater()
let remoteTM
let localTM
let peerStore
@ -50,7 +51,8 @@ describe('Dialing (direct, TCP)', () => {
peerStore = new PeerStore({
peerId: remotePeerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
remoteTM = new TransportManager({
libp2p: {
@ -67,7 +69,8 @@ describe('Dialing (direct, TCP)', () => {
peerId: localPeerId,
peerStore: new PeerStore({
peerId: localPeerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
},
upgrader: mockUpgrader
@ -86,7 +89,11 @@ describe('Dialing (direct, TCP)', () => {
})
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)
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 () => {
const dialer = new Dialer({ transportManager: localTM, peerStore })
const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
const connection = await dialer.connectToPeer(remoteAddr.toString())
expect(connection).to.exist()
await connection.close()
})
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))
.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 () => {
const dialer = new Dialer({ transportManager: localTM, peerStore })
const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
const peerId = await PeerId.createFromJSON(Peers[1])
await expect(dialer.connectToPeer(peerId))
@ -121,11 +140,13 @@ describe('Dialing (direct, TCP)', () => {
const peerId = await PeerId.createFromJSON(Peers[0])
const peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
const dialer = new Dialer({
transportManager: localTM,
peerStore
peerStore,
connectionGater
})
await peerStore.addressBook.set(peerId, remoteTM.getAddrs())
@ -143,7 +164,8 @@ describe('Dialing (direct, TCP)', () => {
add: () => {},
getMultiaddrsForPeer: () => [unsupportedAddr]
}
}
},
connectionGater
})
const peerId = await PeerId.createFromJSON(Peers[0])
@ -161,7 +183,8 @@ describe('Dialing (direct, TCP)', () => {
add: () => { },
getMultiaddrsForPeer: () => [...remoteAddrs, unsupportedAddr]
}
}
},
connectionGater
})
const peerId = await PeerId.createFromJSON(Peers[0])
@ -176,7 +199,8 @@ describe('Dialing (direct, TCP)', () => {
const dialer = new Dialer({
transportManager: localTM,
peerStore,
dialTimeout: 50
dialTimeout: 50,
connectionGater
})
sinon.stub(localTM, 'dial').callsFake(async (addr, options) => {
expect(options.signal).to.exist()
@ -206,7 +230,8 @@ describe('Dialing (direct, TCP)', () => {
add: () => {},
getMultiaddrsForPeer: () => addrs
}
}
},
connectionGater
})
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 TransportManager = require('../../src/transport-manager')
const Libp2p = require('../../src')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
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]
describe('Dialing (direct, WebSockets)', () => {
const connectionGater = mockConnectionGater()
let localTM
let peerStore
let peerId
@ -38,7 +40,8 @@ describe('Dialing (direct, WebSockets)', () => {
[peerId] = await createPeerId()
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
localTM = new TransportManager({
libp2p: {},
@ -54,13 +57,21 @@ describe('Dialing (direct, WebSockets)', () => {
})
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.timeout).to.equal(Constants.DIAL_TIMEOUT)
})
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
expect(dialer.tokens).to.have.length(Constants.MAX_PARALLEL_DIALS)
const tokens = dialer.getTokens(maxPerPeer + 1)
@ -69,14 +80,22 @@ describe('Dialing (direct, WebSockets)', () => {
})
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([])
const tokens = dialer.getTokens(1)
expect(tokens.length).to.equal(0)
})
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)
expect(tokens).to.have.length(1)
expect(dialer.tokens).to.have.length(Constants.MAX_PARALLEL_DIALS - 1)
@ -93,7 +112,8 @@ describe('Dialing (direct, WebSockets)', () => {
add: () => {},
getMultiaddrsForPeer: () => [remoteAddr]
}
}
},
connectionGater
})
const connection = await dialer.connectToPeer(remoteAddr)
@ -109,7 +129,8 @@ describe('Dialing (direct, WebSockets)', () => {
add: () => {},
getMultiaddrsForPeer: () => [remoteAddr]
}
}
},
connectionGater
})
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 () => {
const dialer = new Dialer({ transportManager: localTM, peerStore })
const dialer = new Dialer({
transportManager: localTM,
peerStore,
connectionGater
})
await expect(dialer.connectToPeer(unsupportedAddr))
.to.eventually.be.rejectedWith(AggregateError)
@ -132,7 +157,8 @@ describe('Dialing (direct, WebSockets)', () => {
add: () => {},
getMultiaddrsForPeer: () => [remoteAddr]
}
}
},
connectionGater
})
const connection = await dialer.connectToPeer(peerId)
@ -148,7 +174,8 @@ describe('Dialing (direct, WebSockets)', () => {
set: () => {},
getMultiaddrsForPeer: () => [unsupportedAddr]
}
}
},
connectionGater
})
await expect(dialer.connectToPeer(peerId))
@ -164,7 +191,8 @@ describe('Dialing (direct, WebSockets)', () => {
add: () => {},
getMultiaddrsForPeer: () => [remoteAddr]
}
}
},
connectionGater
})
sinon.stub(localTM, 'dial').callsFake(async (addr, options) => {
expect(options.signal).to.exist()
@ -191,7 +219,8 @@ describe('Dialing (direct, WebSockets)', () => {
add: () => { },
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))
@ -214,7 +243,8 @@ describe('Dialing (direct, WebSockets)', () => {
transportManager: localTM,
addressSorter: addressSort.publicAddressesFirst,
maxParallelDials: 3,
peerStore
peerStore,
connectionGater
})
// Inject data in the AddressBook
@ -240,7 +270,8 @@ describe('Dialing (direct, WebSockets)', () => {
set: () => {},
getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
}
}
},
connectionGater
})
expect(dialer.tokens).to.have.length(2)
@ -278,7 +309,8 @@ describe('Dialing (direct, WebSockets)', () => {
set: () => {},
getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
}
}
},
connectionGater
})
expect(dialer.tokens).to.have.length(2)
@ -320,7 +352,8 @@ describe('Dialing (direct, WebSockets)', () => {
addressBook: {
set: () => { }
}
}
},
connectionGater
})
sinon.stub(dialer, '_createDialTarget').callsFake(() => {
@ -365,7 +398,8 @@ describe('Dialing (direct, WebSockets)', () => {
filter: filters.all
}
}
}
},
connectionGater
})
expect(libp2p.dialer).to.exist()

View File

@ -23,10 +23,12 @@ const pkg = require('../../package.json')
const AddressManager = require('../../src/address-manager')
const { MemoryDatastore } = require('datastore-core/memory')
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const remoteAddr = MULTIADDRS_WEBSOCKETS[0]
const listenMaddrs = [new Multiaddr('/ip4/127.0.0.1/tcp/15002/ws')]
describe('Identify', () => {
const connectionGater = mockConnectionGater()
let localPeer, localPeerStore, localAddressManager
let remotePeer, remotePeerStore, remoteAddressManager
const protocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH]
@ -39,13 +41,15 @@ describe('Identify', () => {
localPeerStore = new PeerStore({
peerId: localPeer,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
await localPeerStore.protoBook.set(localPeer, protocols)
remotePeerStore = new PeerStore({
peerId: remotePeer,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
await remotePeerStore.protoBook.set(remotePeer, protocols)
@ -230,7 +234,8 @@ describe('Identify', () => {
const agentVersion = 'js-project/1.0.0'
const peerStore = new PeerStore({
peerId: localPeer,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
sinon.spy(peerStore.metadataBook, 'setValue')
@ -272,7 +277,8 @@ describe('Identify', () => {
const localPeerStore = new PeerStore({
peerId: localPeer,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
await localPeerStore.protoBook.set(localPeer, storedProtocols)
@ -290,7 +296,8 @@ describe('Identify', () => {
const remotePeerStore = new PeerStore({
peerId: remotePeer,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
await remotePeerStore.protoBook.set(remotePeer, storedProtocols)
@ -352,7 +359,8 @@ describe('Identify', () => {
const localPeerStore = new PeerStore({
peerId: localPeer,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
await localPeerStore.protoBook.set(localPeer, storedProtocols)
@ -370,7 +378,8 @@ describe('Identify', () => {
const remotePeerStore = new PeerStore({
peerId: remotePeer,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
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 Envelope = require('../../src/record/envelope')
const PeerRecord = require('../../src/record/peer-record')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const peerUtils = require('../utils/creators/peer')
const {
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')
describe('addressBook', () => {
const connectionGater = mockConnectionGater()
let peerId
before(async () => {
@ -44,7 +45,8 @@ describe('addressBook', () => {
beforeEach(() => {
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
ab = peerStore.addressBook
})
@ -164,7 +166,8 @@ describe('addressBook', () => {
beforeEach(() => {
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
ab = peerStore.addressBook
})
@ -323,7 +326,8 @@ describe('addressBook', () => {
beforeEach(() => {
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
ab = peerStore.addressBook
})
@ -364,7 +368,8 @@ describe('addressBook', () => {
beforeEach(() => {
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
ab = peerStore.addressBook
})
@ -418,7 +423,8 @@ describe('addressBook', () => {
beforeEach(() => {
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
ab = peerStore.addressBook
})
@ -478,7 +484,8 @@ describe('addressBook', () => {
beforeEach(() => {
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
ab = peerStore.addressBook
})
@ -670,7 +677,8 @@ describe('addressBook', () => {
beforeEach(() => {
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
ab = peerStore.addressBook
})

View File

@ -8,6 +8,7 @@ const { Multiaddr } = require('multiaddr')
const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string')
const { MemoryDatastore } = require('datastore-core/memory')
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 addr2 = new Multiaddr('/ip4/127.0.0.1/tcp/8001')
@ -23,6 +24,7 @@ const proto3 = '/protocol3'
*/
describe('peer-store', () => {
const connectionGater = mockConnectionGater()
let peerIds
before(async () => {
peerIds = await peerUtils.createPeerId({
@ -37,7 +39,8 @@ describe('peer-store', () => {
beforeEach(() => {
peerStore = new PeerStore({
peerId: peerIds[4],
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
})
@ -66,7 +69,8 @@ describe('peer-store', () => {
beforeEach(async () => {
peerStore = new PeerStore({
peerId: peerIds[4],
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
// Add peer0 with { addr1, addr2 } and { proto1 }
@ -170,7 +174,8 @@ describe('peer-store', () => {
beforeEach(() => {
peerStore = new PeerStore({
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 PeerStore = require('../../src/peer-store')
const Registrar = require('../../src/registrar')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const createMockConnection = require('../utils/mockConnection')
const peerUtils = require('../utils/creators/peer')
const baseOptions = require('../utils/base-options.browser')
@ -17,6 +17,7 @@ const baseOptions = require('../utils/base-options.browser')
const multicodec = '/test/1.0.0'
describe('registrar', () => {
const connectionGater = mockConnectionGater()
let peerStore
let registrar
let peerId
@ -29,7 +30,8 @@ describe('registrar', () => {
beforeEach(() => {
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
datastore: new MemoryDatastore(),
addressFilter: connectionGater.filterMultiaddrForPeer
})
registrar = new Registrar({ peerStore, connectionManager: new EventEmitter() })
})

View File

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

View File

@ -18,7 +18,7 @@ const swarmKeyBuffer = uint8ArrayFromString(require('../fixtures/swarm.key'))
const Libp2p = require('../../src')
const Upgrader = require('../../src/upgrader')
const { codes } = require('../../src/errors')
const { mockConnectionGater } = require('../utils/mock-connection-gater')
const mockMultiaddrConnPair = require('../utils/mockMultiaddrConn')
const Peers = require('../fixtures/peers')
const addrs = [
@ -31,6 +31,17 @@ describe('Upgrader', () => {
let remoteUpgrader
let localPeer
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 () => {
([
@ -42,10 +53,14 @@ describe('Upgrader', () => {
]))
localUpgrader = new Upgrader({
localPeer
connectionManager: mockConnectionManager,
localPeer,
connectionGater
})
remoteUpgrader = new Upgrader({
localPeer: remotePeer
connectionManager: mockConnectionManager,
localPeer: remotePeer,
connectionGater
})
localUpgrader.protocols.set('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
@ -321,6 +336,7 @@ describe('Upgrader', () => {
describe('libp2p.upgrader', () => {
let peers
let libp2p
const connectionGater = mockConnectionGater()
before(async () => {
peers = await Promise.all([
@ -392,8 +408,10 @@ describe('libp2p.upgrader', () => {
const remoteUpgrader = new Upgrader({
localPeer: remotePeer,
connectionManager: libp2p.connectionManager,
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)
@ -424,8 +442,10 @@ describe('libp2p.upgrader', () => {
const remoteUpgrader = new Upgrader({
localPeer: remotePeer,
connectionManager: libp2p.connectionManager,
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 })

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
}