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

feat: allow passing the id of a network peer to ipfs.id #3386

Merged
merged 7 commits into from
May 13, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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|PeerId` | `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
11 changes: 11 additions & 0 deletions packages/interface-ipfs-core/src/miscellaneous/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,16 @@ 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 () {
const ipfsB = (await common.spawn()).api
await ipfs.swarm.connect(ipfsB.peerId.addresses[0])

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

expect(result).to.deep.equal(ipfsB.peerId)
})
})
}
11 changes: 8 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 parseDuration = require('parse-duration').default

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 @@ -19,9 +23,10 @@ module.exports = {
}
},

async handler ({ ctx: { ipfs, print }, format, timeout }) {
async handler ({ ctx: { ipfs, print }, format, timeout, peerid: peerId }) {
const id = await ipfs.id({
timeout
timeout,
peerId
})

if (format) {
Expand Down
18 changes: 18 additions & 0 deletions packages/ipfs-cli/test/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
const { expect } = require('aegir/utils/chai')
const cli = require('./utils/cli')
const sinon = require('sinon')
const PeerId = require('peer-id')

const defaultOptions = {
timeout: undefined
Expand Down Expand Up @@ -54,4 +55,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')
})
})
62 changes: 49 additions & 13 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('../utils')
const uint8ArrayToString = require('uint8arrays/to-string')
const PeerId = require('peer-id')
const { NotStartedError } = require('../errors')

/**
* @param {Object} config
Expand All @@ -14,44 +16,71 @@ module.exports = ({ peerId, libp2p }) => {
/**
* Returns the identity of the Peer
*
* @param {import('../utils').AbortOptions} [_options]
* @param {IdOptions} [options]
* @returns {Promise<PeerId>}
* @example
* ```js
* const identity = await ipfs.id()
* console.log(identity)
* ```
*/
async function id (_options) { // eslint-disable-line require-await
const id = peerId.toB58String()
async function id (options = {}) { // eslint-disable-line require-await
options = options || {}

let id = peerId
let publicKey = id.pubKey
let addresses = []
let protocols = []
let agentVersion = `js-ipfs/${pkgversion}`
let protocolVersion = '9000'

if (options.peerId) {
if (PeerId.isPeerId(options.peerId)) {
id = options.peerId
} else {
id = PeerId.createFromB58String(options.peerId.toString())
}

if (!libp2p) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: 😭 I find inconsistency here really unfortunate. Not having running libp2p is fine if you ask your own addresses / protocols but is an error if you ask some other peer. I think it would be a lot better to either

  1. Throw if libp2p isn't running regardless of your other id
  2. Return empty arrays regardless of your other id

Better yet (certainly out of scope here) would be to have APIs that depend on a certain service / capability in namespace which is may or may not be available. That way you can check if (ipfs.network) { ipfs.network.id(...) } as opposed getting empty arrays or runtime exceptions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The peer store should work offline, so we should be able to query it for peers we knew about last time we were online.

The implementation here may need a little more thought.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe in the short term we should just return empty arrays instead of errors then

throw new NotStartedError()
}

if (libp2p) {
// only available while the node is running
addresses = libp2p.transportManager.getAddrs()
protocols = Array.from(libp2p.upgrader.protocols.keys())
publicKey = libp2p.peerStore.keyBook.get(id)
addresses = libp2p.peerStore.addressBook.getMultiaddrsForPeer(id) || []
protocols = libp2p.peerStore.protoBook.get(id) || []

const meta = libp2p.peerStore.metadataBook.get(id) || {}
agentVersion = meta.agentVersion
protocolVersion = meta.protocolVersion
} else {
if (libp2p) {
// only available while the node is running
addresses = libp2p.transportManager.getAddrs()
protocols = Array.from(libp2p.upgrader.protocols.keys())
}
}

const idStr = id.toB58String()

return {
id,
publicKey: uint8ArrayToString(peerId.pubKey.bytes, 'base64pad'),
id: idStr,
publicKey: publicKey ? uint8ArrayToString(publicKey.bytes, 'base64pad') : undefined,
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 => multiaddr(ma)),
agentVersion: `js-ipfs/${pkgversion}`,
protocolVersion: '9000',
agentVersion,
protocolVersion,
protocols: protocols.sort()
}
}
Expand All @@ -67,4 +96,11 @@ module.exports = ({ peerId, libp2p }) => {
* @property {string} agentVersion - The agent version
* @property {string} protocolVersion - The supported protocol version
* @property {string[]} protocols - The supported protocols
*
* @typedef {IdSettings & AbortOptions} IdOptions
*
* @typedef {Object} IdSettings
* @property {string|PeerId} [peerId] - The address of a remote peer
*
* @typedef {import('../utils').AbortOptions} AbortOptions
*/
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 @@ -13,7 +13,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.peerId()
})
.rename('arg', 'peerId', {
override: true,
ignoreUndefined: true
})
}
},
handler: async (request, h) => {
Expand All @@ -25,13 +30,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
23 changes: 23 additions & 0 deletions packages/ipfs-http-server/test/inject/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const testHttpMethod = require('../utils/test-http-method')
const http = require('../utils/http')
const sinon = require('sinon')
const { AbortSignal } = require('native-abort-controller')
const PeerId = require('peer-id')

const defaultOptions = {
signal: sinon.match.instanceOf(AbortSignal),
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 = PeerId.createFromB58String('QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D')

ipfs.id.withArgs({
...defaultOptions,
peerId: peerId.toString()
}).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)
})
})