Skip to content

Commit

Permalink
fix: Connections to Allow list peers will not be pruned (#1564)
Browse files Browse the repository at this point in the history
If the connection manager needs to reduce the number of active connections, do not close connections to peers that are in the allow list.

Closes #1515
  • Loading branch information
maschad authored Mar 21, 2023
1 parent 3fe5e23 commit f9e5450
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 2 deletions.
10 changes: 9 additions & 1 deletion src/connection-manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,15 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven

for (const connection of sortedConnections) {
log('too many connections open - closing a connection to %p', connection.remotePeer)
toClose.push(connection)
// check allow list
const connectionInAllowList = this.allow.some((ma) => {
return connection.remoteAddr.toString().startsWith(ma.toString())
})

// Connections in the allow list should be excluded from pruning
if (!connectionInAllowList) {
toClose.push(connection)
}

if (toClose.length === toPrune) {
break
Expand Down
75 changes: 74 additions & 1 deletion test/connection-manager/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,80 @@ describe('Connection Manager', () => {
expect(shortestLivedWithLowestTagSpy).to.have.property('callCount', 1)
})

it('should close connection when the maximum has been reached even without tags', async () => {
it('should not close connection that is on the allowlist when pruning', async () => {
const max = 2
const remoteAddr = multiaddr('/ip4/83.13.55.32/tcp/59283')

libp2p = await createNode({
config: createBaseOptions({
connectionManager: {
maxConnections: max,
minConnections: 0,
allow: [
'/ip4/83.13.55.32'
]
}
}),
started: false
})

await libp2p.start()

const connectionManager = libp2p.connectionManager as DefaultConnectionManager
const connectionManagerMaybeDisconnectOneSpy = sinon.spy(connectionManager, '_pruneConnections')
const spies = new Map<number, sinon.SinonSpy<[], Promise<void>>>()

// Max out connections
for (let i = 0; i < max; i++) {
const connection = mockConnection(mockMultiaddrConnection(mockDuplex(), await createEd25519PeerId()))
const spy = sinon.spy(connection, 'close')
const value = (i + 1) * 10
spies.set(value, spy)
await libp2p.peerStore.tagPeer(connection.remotePeer, 'test-tag', {
value
})
await connectionManager._onConnect(new CustomEvent('connection', { detail: connection }))
}

// an outbound connection is opened from an address in the allow list
const remotePeer = await createEd25519PeerId()
const connection = mockConnection(mockMultiaddrConnection({
remoteAddr,
source: [],
sink: async () => {}
}, remotePeer))

const value = 0
const spy = sinon.spy(connection, 'close')
spies.set(value, spy)

// Tag that allowed peer with lowest value
await libp2p.peerStore.tagPeer(remotePeer, 'test-tag', {
value
})

await connectionManager._onConnect(new CustomEvent('connection', { detail: connection }))

// get the lowest value
const lowest = Array.from(spies.keys()).sort((a, b) => {
if (a > b) {
return 1
}

if (a < b) {
return -1
}

return 0
})[0]
const lowestSpy = spies.get(lowest)

expect(connectionManagerMaybeDisconnectOneSpy.callCount).to.equal(1)
// expect lowest value spy NOT to be called since the peer is in the allow list
expect(lowestSpy).to.have.property('callCount', 0)
})

it('should close connection when the maximum connections has been reached even without tags', async () => {
const max = 5
libp2p = await createNode({
config: createBaseOptions({
Expand Down

0 comments on commit f9e5450

Please sign in to comment.