Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

Commit

Permalink
feat: allow passing the id of a network peer to ipfs.id (#3386)
Browse files Browse the repository at this point in the history
Adds parity with go-IPFS and allows looking up known peer IDs using
the `ipfs.id` command.
  • Loading branch information
achingbrain authored May 13, 2021
1 parent bff33c4 commit 00fd709
Show file tree
Hide file tree
Showing 14 changed files with 151 additions and 44 deletions.
3 changes: 2 additions & 1 deletion docs/core-api/MISCELLANEOUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ An optional object which may have the following keys:
| ---- | ---- | ------- | ----------- |
| timeout | `Number` | `undefined` | A timeout in ms |
| signal | [AbortSignal][] | `undefined` | Can be used to cancel any long running requests started as a result of this call |
| peerId | `string` | `undefined` | Look up the identity for this peer instead of the current node |

### Returns

Expand Down Expand Up @@ -300,4 +301,4 @@ A great source of [examples](https://github.com/ipfs/js-ipfs/blob/master/package
[rs]: https://www.npmjs.com/package/readable-stream
[ps]: https://www.npmjs.com/package/pull-stream
[cid]: https://www.npmjs.com/package/cids
[AbortSignal]: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
[AbortSignal]: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
2 changes: 1 addition & 1 deletion examples/custom-libp2p/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"license": "MIT",
"dependencies": {
"ipfs": "^0.55.1",
"libp2p": "^0.31.0",
"libp2p": "^0.31.5",
"libp2p-bootstrap": "^0.12.3",
"libp2p-kad-dht": "^0.22.0",
"libp2p-mdns": "^0.16.0",
Expand Down
1 change: 1 addition & 0 deletions packages/interface-ipfs-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"nanoid": "^3.1.12",
"native-abort-controller": "^1.0.3",
"p-map": "^4.0.0",
"p-retry": "^4.5.0",
"peer-id": "^0.14.1",
"readable-stream": "^3.4.0",
"uint8arrays": "^2.1.3"
Expand Down
30 changes: 30 additions & 0 deletions packages/interface-ipfs-core/src/miscellaneous/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { getDescribe, getIt, expect } = require('../utils/mocha')
const { Multiaddr } = require('multiaddr')
const CID = require('cids')
const { isWebWorker } = require('ipfs-utils/src/env')
const retry = require('p-retry')

/** @typedef { import("ipfsd-ctl/src/factory") } Factory */
/**
Expand Down Expand Up @@ -66,5 +67,34 @@ module.exports = (common, options) => {

await expect(ipfs.id()).to.eventually.have.property('addresses').that.is.not.empty()
})

it('should get the id of another node in the swarm', async function () {
if (isWebWorker) {
// TODO: https://github.com/libp2p/js-libp2p-websockets/issues/129
return this.skip()
}

const ipfsB = (await common.spawn()).api
await ipfs.swarm.connect(ipfsB.peerId.addresses[0])

// have to wait for identify to complete before protocols etc are available for remote hosts
await retry(async () => {
const result = await ipfs.id({
peerId: ipfsB.peerId.id
})

expect(result).to.deep.equal(ipfsB.peerId)
}, { retries: 5 })
})

it('should get our own id when passed as an option', async function () {
const res = await ipfs.id()

const result = await ipfs.id({
peerId: res.id
})

expect(result).to.deep.equal(res)
})
})
}
12 changes: 9 additions & 3 deletions packages/ipfs-cli/src/commands/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
const { default: parseDuration } = require('parse-duration')

module.exports = {
command: 'id',
command: 'id [peerId]',

describe: 'Shows IPFS Node ID info',

builder: {
peerid: {
type: 'string',
describe: 'Peer.ID of node to look up'
},
format: {
alias: 'f',
type: 'string',
Expand All @@ -24,10 +28,12 @@ module.exports = {
* @param {import('../types').Context} argv.ctx
* @param {string} argv.format
* @param {number} argv.timeout
* @param {string} [argv.peerId]
*/
async handler ({ ctx: { ipfs, print }, format, timeout }) {
async handler ({ ctx: { ipfs, print }, format, timeout, peerId }) {
const id = await ipfs.id({
timeout
timeout,
peerId
})

if (format) {
Expand Down
21 changes: 20 additions & 1 deletion packages/ipfs-cli/test/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
const { expect } = require('aegir/utils/chai')
const cli = require('./utils/cli')
const sinon = require('sinon')
const PeerId = require('peer-id')

const defaultOptions = {
timeout: undefined
timeout: undefined,
peerId: undefined
}

describe('id', () => {
Expand Down Expand Up @@ -54,4 +56,21 @@ describe('id', () => {
expect(res).to.have.property('id', 'id')
expect(res).to.have.property('publicKey', 'publicKey')
})

it('get the id of another peer', async () => {
const peerId = PeerId.createFromB58String('QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D')

ipfs.id.withArgs({
...defaultOptions,
peerId: peerId.toString()
}).resolves({
id: 'id',
publicKey: 'publicKey'
})

const out = await cli(`id ${peerId}`, { ipfs })
const res = JSON.parse(out)
expect(res).to.have.property('id', 'id')
expect(res).to.have.property('publicKey', 'publicKey')
})
})
6 changes: 5 additions & 1 deletion packages/ipfs-core-types/src/root.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export interface API<OptionExtension = {}> {
* console.log(identity)
* ```
*/
id: (options?: AbortOptions & OptionExtension) => Promise<IDResult>
id: (options?: IDOptions & OptionExtension) => Promise<IDResult>

/**
* Returns the implementation version
Expand Down Expand Up @@ -289,6 +289,10 @@ export interface ListOptions extends AbortOptions, PreloadOptions {
includeContent?: boolean
}

export interface IDOptions extends AbortOptions {
peerId?: string
}

export interface IDResult {
id: string
publicKey: string
Expand Down
2 changes: 1 addition & 1 deletion packages/ipfs-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"it-map": "^1.0.4",
"it-pipe": "^1.1.0",
"just-safe-set": "^2.2.1",
"libp2p": "^0.31.2",
"libp2p": "^0.31.5",
"libp2p-bootstrap": "^0.12.3",
"libp2p-crypto": "^0.19.3",
"libp2p-floodsub": "^0.25.1",
Expand Down
69 changes: 39 additions & 30 deletions packages/ipfs-core/src/components/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const pkgversion = require('../../package.json').version
const { Multiaddr } = require('multiaddr')
const withTimeoutOption = require('ipfs-core-utils/src/with-timeout-option')
const uint8ArrayToString = require('uint8arrays/to-string')
const PeerId = require('peer-id')
const { NotStartedError } = require('../errors')

/**
* @param {Object} config
Expand All @@ -14,54 +16,61 @@ module.exports = ({ peerId, network }) => {
/**
* @type {import('ipfs-core-types/src/root').API["id"]}
*/
async function id (_options = {}) { // eslint-disable-line require-await
const id = peerId.toB58String()
/** @type {Multiaddr[]} */
let addresses = []
/** @type {string[]} */
let protocols = []
async function id (options = {}) { // eslint-disable-line require-await
if (options.peerId === peerId.toB58String()) {
delete options.peerId
}

const net = network.try()

if (net) {
const { libp2p } = net
// only available while the node is running
addresses = libp2p.multiaddrs
protocols = Array.from(libp2p.upgrader.protocols.keys())
if (!net) {
if (options.peerId) {
throw new NotStartedError()
}

const idStr = peerId.toB58String()

return {
id: idStr,
publicKey: uint8ArrayToString(peerId.pubKey.bytes, 'base64pad'),
addresses: [],
agentVersion: `js-ipfs/${pkgversion}`,
protocolVersion: '9000',
protocols: []
}
}

const id = options.peerId ? PeerId.createFromB58String(options.peerId.toString()) : peerId
const { libp2p } = net

const publicKey = options.peerId ? libp2p.peerStore.keyBook.get(id) : id.pubKey
const addresses = options.peerId ? libp2p.peerStore.addressBook.getMultiaddrsForPeer(id) : libp2p.multiaddrs
const protocols = options.peerId ? libp2p.peerStore.protoBook.get(id) : Array.from(libp2p.upgrader.protocols.keys())
const agentVersion = uint8ArrayToString(libp2p.peerStore.metadataBook.getValue(id, 'AgentVersion') || new Uint8Array())
const protocolVersion = uint8ArrayToString(libp2p.peerStore.metadataBook.getValue(id, 'ProtocolVersion') || new Uint8Array())
const idStr = id.toB58String()

return {
id,
publicKey: uint8ArrayToString(peerId.pubKey.bytes, 'base64pad'),
addresses: addresses
id: idStr,
publicKey: uint8ArrayToString(publicKey.bytes, 'base64pad'),
addresses: (addresses || [])
.map(ma => {
const str = ma.toString()

// some relay-style transports add our peer id to the ma for us
// so don't double-add
if (str.endsWith(`/p2p/${id}`)) {
if (str.endsWith(`/p2p/${idStr}`)) {
return str
}

return `${str}/p2p/${id}`
return `${str}/p2p/${idStr}`
})
.sort()
.map(ma => new Multiaddr(ma)),
agentVersion: `js-ipfs/${pkgversion}`,
protocolVersion: '9000',
protocols: protocols.sort()
agentVersion,
protocolVersion,
protocols: (protocols || []).sort()
}
}
return withTimeoutOption(id)
}

/**
* @typedef {object} ID
* The Peer identity
* @property {string} id - the Peer ID
* @property {string} publicKey - the public key of the peer as a base64 encoded string
* @property {Multiaddr[]} addresses - A list of multiaddrs this node is listening on
* @property {string} agentVersion - The agent version
* @property {string} protocolVersion - The supported protocol version
* @property {string[]} protocols - The supported protocols
*/
4 changes: 4 additions & 0 deletions packages/ipfs-core/src/components/libp2p.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const get = require('dlv')
const mergeOptions = require('merge-options')
const errCode = require('err-code')
const PubsubRouters = require('../runtime/libp2p-pubsub-routers-nodejs')
const pkgversion = require('../../package.json').version

/**
* @typedef {Object} KeychainConfig
Expand Down Expand Up @@ -134,6 +135,9 @@ function getLibp2pOptions ({ options, config, datastore, keys, keychainConfig, p
keychain: {
datastore: keys,
...keychainConfig
},
host: {
agentVersion: `js-ipfs/${pkgversion}`
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/ipfs-daemon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"ipfs-http-server": "^0.4.0",
"ipfs-utils": "^7.0.0",
"just-safe-set": "^2.2.1",
"libp2p": "^0.31.2",
"libp2p": "^0.31.5",
"libp2p-delegated-content-routing": "^0.10.0",
"libp2p-delegated-peer-routing": "^0.9.0",
"libp2p-webrtc-star": "^0.22.2",
Expand Down
5 changes: 4 additions & 1 deletion packages/ipfs-http-client/src/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ module.exports = configure(api => {
const res = await api.post('id', {
timeout: options.timeout,
signal: options.signal,
searchParams: toUrlSearchParams(options),
searchParams: toUrlSearchParams({
arg: options.peerId ? options.peerId.toString() : undefined,
...options
}),
headers: options.headers
})
const data = await res.json()
Expand Down
13 changes: 10 additions & 3 deletions packages/ipfs-http-server/src/api/resources/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ module.exports = {
stripUnknown: true
},
query: Joi.object().keys({
timeout: Joi.timeout()
timeout: Joi.timeout(),
peerId: Joi.string()
})
.rename('arg', 'peerId', {
override: true,
ignoreUndefined: true
})
}
},
/**
Expand All @@ -29,13 +34,15 @@ module.exports = {
}
},
query: {
timeout
timeout,
peerId
}
} = request

const id = await ipfs.id({
signal,
timeout
timeout,
peerId
})
return h.response({
ID: id.id,
Expand Down
25 changes: 24 additions & 1 deletion packages/ipfs-http-server/test/inject/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const { AbortSignal } = require('native-abort-controller')

const defaultOptions = {
signal: sinon.match.instanceOf(AbortSignal),
timeout: undefined
timeout: undefined,
peerId: undefined
}

describe('/id', () => {
Expand Down Expand Up @@ -65,4 +66,26 @@ describe('/id', () => {

expect(res).to.have.property('statusCode', 200)
})

it('get the id of another peer', async () => {
const peerId = 'QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D'

ipfs.id.withArgs({
...defaultOptions,
peerId
}).returns({
id: 'id',
publicKey: 'publicKey',
addresses: 'addresses',
agentVersion: 'agentVersion',
protocolVersion: 'protocolVersion'
})

const res = await http({
method: 'POST',
url: `/api/v0/id?peerId=${peerId}`
}, { ipfs })

expect(res).to.have.property('statusCode', 200)
})
})

0 comments on commit 00fd709

Please sign in to comment.