Skip to content

Commit

Permalink
fix: conform to peer schema
Browse files Browse the repository at this point in the history
  • Loading branch information
achingbrain committed Jan 16, 2024
1 parent 814f9ef commit 35f47da
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 47 deletions.
60 changes: 21 additions & 39 deletions packages/client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import PQueue from 'p-queue'
import { DelegatedRoutingV1HttpApiClientContentRouting, DelegatedRoutingV1HttpApiClientPeerRouting } from './routings.js'
import type { DelegatedRoutingV1HttpApiClient, DelegatedRoutingV1HttpApiClientInit, PeerRecord } from './index.js'
import type { ContentRouting, PeerRouting, AbortOptions, PeerId } from '@libp2p/interface'
import type { Multiaddr } from '@multiformats/multiaddr'
import type { CID } from 'multiformats'

const log = logger('delegated-routing-v1-http-api-client')
Expand Down Expand Up @@ -68,7 +69,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
this.started = false
}

async * getProviders (cid: CID, options: AbortOptions = {}): AsyncGenerator<PeerRecord, any, unknown> {
async * getProviders (cid: CID, options: AbortOptions = {}): AsyncGenerator<PeerRecord> {
log('getProviders starts: %c', cid)

const signal = anySignal([this.shutDownController.signal, options.signal, AbortSignal.timeout(this.timeout)])
Expand Down Expand Up @@ -109,14 +110,14 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
const body = await res.json()

for (const provider of body.Providers) {
const record = this.#handleProviderRecords(provider)
const record = this.#conformToPeerSchema(provider)
if (record != null) {
yield record
}
}
} else {
for await (const provider of ndjson(toIt(res.body))) {
const record = this.#handleProviderRecords(provider)
const record = this.#conformToPeerSchema(provider)
if (record != null) {
yield record
}
Expand All @@ -131,7 +132,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
}
}

async * getPeers (peerId: PeerId, options: AbortOptions | undefined = {}): AsyncGenerator<PeerRecord, any, unknown> {
async * getPeers (peerId: PeerId, options: AbortOptions | undefined = {}): AsyncGenerator<PeerRecord> {
log('getPeers starts: %c', peerId)

const signal = anySignal([this.shutDownController.signal, options.signal, AbortSignal.timeout(this.timeout)])
Expand Down Expand Up @@ -172,14 +173,14 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
const body = await res.json()

for (const peer of body.Peers) {
const record = this.#handlePeerRecords(peerId, peer)
const record = this.#conformToPeerSchema(peer)
if (record != null) {
yield record
}
}
} else {
for await (const peer of ndjson(toIt(res.body))) {
const record = this.#handlePeerRecords(peerId, peer)
const record = this.#conformToPeerSchema(peer)
if (record != null) {
yield record
}
Expand Down Expand Up @@ -272,44 +273,25 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
}
}

#handleProviderRecords (record: any): PeerRecord | undefined {
if (record.Schema === 'peer') {
// Peer schema can have additional, user-defined, fields.
record.ID = peerIdFromString(record.ID)
record.Addrs = record.Addrs?.map(multiaddr) ?? []
return record
}
#conformToPeerSchema (record: any): PeerRecord | undefined {
const protocols: string[] = []
const multiaddrs: Multiaddr[] = record.Addrs?.map(multiaddr) ?? []

if (record.Schema === 'bitswap') {
// Bitswap schema is deprecated, was incorrectly used when server had no
// information about actual protocols, so we convert it to peer result
// without protocol information
return {
Schema: 'peer',
ID: peerIdFromString(record.ID),
Addrs: record.Addrs?.map(multiaddr) ?? [],
Protocol: record.Protocol
}
if (record.Protocols != null) {
protocols.push(...record.Protocols)
}

if (record.ID != null && Array.isArray(record.Addrs)) {
return {
Schema: 'peer',
ID: peerIdFromString(record.ID),
Addrs: record.Addrs?.map(multiaddr) ?? [],
Protocol: record.Protocol
}
if (record.Protocol != null) {
protocols.push(record.Protocol)
delete record.Protocol
}
}

#handlePeerRecords (peerId: PeerId, record: any): PeerRecord | undefined {
if (record.Schema === 'peer') {
// Peer schema can have additional, user-defined, fields.
record.ID = peerIdFromString(record.ID)
record.Addrs = record.Addrs?.map(multiaddr) ?? []
if (peerId.equals(record.ID)) {
return record
}
return {
...record,
Schema: 'peer',
ID: peerIdFromString(record.ID),
Addrs: multiaddrs,
Protocols: protocols
}
}
}
16 changes: 13 additions & 3 deletions packages/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,22 @@ import type { Multiaddr } from '@multiformats/multiaddr'
import type { IPNSRecord } from 'ipns'
import type { CID } from 'multiformats/cid'

/**
* A peer that conforms to the [Peer Schema](https://specs.ipfs.tech/routing/http-routing-v1/#peer-schema).
*
* Note that legacy schemas may be reformatted internally by this module.
*
* If `Addrs` is empty, a caller may wish to perform a `findPeer` operation to
* ascertain the peer's multiaddrs.
*
* If `Protocols` is empty, a caller may wish to dial the peer and peform a
* libp2p identify operation to ascertain the peer's supported protocols.
*/
export interface PeerRecord {
Schema: 'peer'
ID: PeerId
Addrs?: Multiaddr[]
Protocol: string
Metadata?: string
Addrs: Multiaddr[]
Protocols: string[]
}

export interface DelegatedRoutingV1HttpApiClientInit {
Expand Down
48 changes: 43 additions & 5 deletions packages/client/test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-env mocha */

import { peerIdFromString } from '@libp2p/peer-id'
import { createEd25519PeerId } from '@libp2p/peer-id-factory'
import { multiaddr } from '@multiformats/multiaddr'
import { expect } from 'aegir/chai'
import { create as createIpnsRecord, marshal as marshalIpnsRecord } from 'ipns'
import all from 'it-all'
Expand Down Expand Up @@ -108,7 +110,7 @@ describe('delegated-routing-v1-http-api-client', () => {
expect(provs).to.be.empty()
})

it('should find peers and only accepts correct peer records', async () => {
it('should conform records to peer schema', async () => {
const peerId = await createEd25519PeerId()

const records = [{
Expand All @@ -124,7 +126,7 @@ describe('delegated-routing-v1-http-api-client', () => {
ID: peerId.toString(),
Addrs: ['/ip4/41.41.41.41/tcp/1234']
}, {
Protocol: 'transport-bitswap',
Protocols: ['transport-bitswap'],
Schema: 'peer',
Metadata: 'gBI=',
ID: peerId.toString(),
Expand All @@ -135,6 +137,40 @@ describe('delegated-routing-v1-http-api-client', () => {
Metadata: 'gBI=',
ID: (await createEd25519PeerId()).toString(),
Addrs: ['/ip4/42.42.42.42/tcp/1234']
}, {
Schema: 'peer',
ID: (await createEd25519PeerId()).toString()
}]

const peers = [{
Protocols: ['transport-bitswap'],
Schema: 'peer',
Metadata: 'gBI=',
ID: peerIdFromString(records[0].ID),
Addrs: [multiaddr('/ip4/41.41.41.41/tcp/1234')]
}, {
Protocols: ['transport-saddle'],
Schema: 'peer',
Metadata: 'gBI=',
ID: peerIdFromString(records[1].ID),
Addrs: [multiaddr('/ip4/41.41.41.41/tcp/1234')]
}, {
Protocols: ['transport-bitswap'],
Schema: 'peer',
Metadata: 'gBI=',
ID: peerIdFromString(records[2].ID),
Addrs: [multiaddr('/ip4/42.42.42.42/tcp/1234')]
}, {
Protocols: ['transport-bitswap'],
Schema: 'peer',
Metadata: 'gBI=',
ID: peerIdFromString(records[3].ID),
Addrs: [multiaddr('/ip4/42.42.42.42/tcp/1234')]
}, {
Protocols: [],
Schema: 'peer',
ID: peerIdFromString(records[4].ID),
Addrs: []
}]

// load peer for the router to fetch
Expand All @@ -145,12 +181,14 @@ describe('delegated-routing-v1-http-api-client', () => {

const peerRecords = await all(client.getPeers(peerId))
expect(peerRecords.map(peerRecord => ({
...peerRecord,
ID: peerRecord.ID.toString(),
Addrs: peerRecord.Addrs?.map(ma => ma.toString()) ?? []
}))).to.deep.equal(peers.map(peerRecord => ({
...peerRecord,
ID: peerRecord.ID.toString(),
Addrs: peerRecord.Addrs?.map(ma => ma.toString())
}))).to.deep.equal([
records[2]
])
})))
})

it('should get ipns record', async () => {
Expand Down

0 comments on commit 35f47da

Please sign in to comment.