Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions packages/libp2p/src/connection-manager/dial-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ export class DialQueue {
const peerId = options.peerId
const multiaddrs = options.multiaddrs
const failedMultiaddrs = new Set<string>()
const hadInitialMultiaddrs = options.multiaddrs.size > 0
let didPeerRoutingLookupAfterDialFailure = false

// if we have no multiaddrs, only a peer id, set a flag so we will look the
// peer up in the peer routing to obtain multiaddrs
Expand Down Expand Up @@ -331,6 +333,31 @@ export class DialQueue {
errors.push(err)
}
}

// If user-supplied multiaddrs for a peer all failed, try one peer routing
// lookup to fetch updated addresses before giving up.
if (peerId != null && hadInitialMultiaddrs && addrsToDial.length > 0 && didPeerRoutingLookupAfterDialFailure === false && signal.aborted === false) {
didPeerRoutingLookupAfterDialFailure = true

this.log('looking up multiaddrs for %p in the peer routing after dial failures', peerId)

try {
const peerInfo = await this.components.peerRouting.findPeer(peerId, {
...options,
signal
})

for (const ma of peerInfo.multiaddrs) {
multiaddrs.add(ma.toString())
}
} catch (err: any) {
if (err.name === 'NoPeerRoutersError') {
this.log('no peer routers configured', peerId)
} else {
this.log.error('looking up multiaddrs for %p in the peer routing failed - %e', peerId, err)
}
}
}
}

if (errors.length === 1) {
Expand Down
46 changes: 46 additions & 0 deletions packages/libp2p/test/connection-manager/dial-queue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,52 @@ describe('dial queue', () => {
await expect(dialer.dial(peerId)).to.eventually.equal(connection)
})

it('should look up peer routing after user-supplied multiaddrs are exhausted', async () => {
const peerId = peerIdFromPrivateKey(await generateKeyPair('Ed25519'))
const connection = stubInterface<Connection>()
const provided1 = multiaddr(`/ip4/127.0.0.1/tcp/1231/p2p/${peerId}`)
const provided2 = multiaddr(`/ip4/127.0.0.1/tcp/1232/p2p/${peerId}`)
const routed = multiaddr('/ip4/127.0.0.1/tcp/4001')
const routedWithPeer = routed.encapsulate(`/p2p/${peerId}`)

let dialAttempts = 0

components.peerRouting.findPeer.callsFake(async (id) => {
expect(id.equals(peerId)).to.equal(true)
expect(dialAttempts).to.equal(2)

return {
id: peerId,
multiaddrs: [routed]
}
})

const actions: Record<string, () => Promise<Connection>> = {
[provided1.toString()]: async () => Promise.reject(new Error('dial failure')),
[provided2.toString()]: async () => Promise.reject(new Error('dial failure')),
[routedWithPeer.toString()]: async () => Promise.resolve(connection)
}

components.transportManager.dialTransportForMultiaddr.returns(stubInterface<Transport>())
components.transportManager.dial.callsFake(async ma => {
dialAttempts++
const action = actions[ma.toString()]

if (action != null) {
return action()
}

throw new Error(`No action found for multiaddr ${ma.toString()}`)
})

dialer = new DialQueue(components)

await expect(dialer.dial([provided1, provided2])).to.eventually.equal(connection)

expect(components.peerRouting.findPeer).to.have.property('callCount', 1)
expect(components.transportManager.dial.getCalls().map(c => c.args[0].toString())).to.include(routedWithPeer.toString())
})

it('should end when a single multiaddr dials succeeds even when a final dial fails', async () => {
const connection = stubInterface<Connection>()
const deferredConn = pDefer<Connection>()
Expand Down
Loading