2018-02-07 07:48:37 +00:00
|
|
|
'use strict'
|
|
|
|
|
2018-10-19 16:28:28 +02:00
|
|
|
const errCode = require('err-code')
|
2021-01-21 12:41:27 +00:00
|
|
|
const { messages, codes } = require('../errors')
|
|
|
|
const {
|
|
|
|
storeAddresses,
|
|
|
|
uniquePeers,
|
|
|
|
requirePeers,
|
|
|
|
maybeLimitSource
|
|
|
|
} = require('./utils')
|
2019-12-01 22:54:59 +01:00
|
|
|
|
2021-01-21 12:41:27 +00:00
|
|
|
const merge = require('it-merge')
|
|
|
|
const { pipe } = require('it-pipe')
|
2018-10-19 16:28:28 +02:00
|
|
|
|
2020-12-10 14:48:14 +01:00
|
|
|
/**
|
|
|
|
* @typedef {import('peer-id')} PeerId
|
2021-04-15 09:40:02 +02:00
|
|
|
* @typedef {import('multiaddr').Multiaddr} Multiaddr
|
2021-07-09 07:43:34 +01:00
|
|
|
* @typedef {import('multiformats/cid').CID} CID
|
2021-04-30 13:20:12 +02:00
|
|
|
* @typedef {import('libp2p-interfaces/src/content-routing/types').ContentRouting} ContentRoutingModule
|
2020-12-10 14:48:14 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {Object} GetData
|
|
|
|
* @property {PeerId} from
|
|
|
|
* @property {Uint8Array} val
|
|
|
|
*/
|
|
|
|
|
|
|
|
class ContentRouting {
|
|
|
|
/**
|
|
|
|
* @class
|
2021-01-21 12:41:27 +00:00
|
|
|
* @param {import('..')} libp2p
|
2020-12-10 14:48:14 +01:00
|
|
|
*/
|
|
|
|
constructor (libp2p) {
|
|
|
|
this.libp2p = libp2p
|
2021-04-15 09:40:02 +02:00
|
|
|
/** @type {ContentRoutingModule[]} */
|
2020-12-10 14:48:14 +01:00
|
|
|
this.routers = libp2p._modules.contentRouting || []
|
|
|
|
this.dht = libp2p._dht
|
|
|
|
|
2021-01-21 12:41:27 +00:00
|
|
|
// If we have the dht, add it to the available content routers
|
2021-02-11 14:37:34 +01:00
|
|
|
if (this.dht && libp2p._config.dht.enabled) {
|
2021-01-21 12:41:27 +00:00
|
|
|
this.routers.push(this.dht)
|
2020-12-10 14:48:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-01-21 12:41:27 +00:00
|
|
|
* Iterates over all content routers in parallel to find providers of the given key.
|
2020-12-10 14:48:14 +01:00
|
|
|
*
|
|
|
|
* @param {CID} key - The CID key of the content to find
|
|
|
|
* @param {object} [options]
|
|
|
|
* @param {number} [options.timeout] - How long the query should run
|
|
|
|
* @param {number} [options.maxNumProviders] - maximum number of providers to find
|
|
|
|
* @returns {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>}
|
|
|
|
*/
|
2021-01-21 12:41:27 +00:00
|
|
|
async * findProviders (key, options = {}) {
|
2020-12-10 14:48:14 +01:00
|
|
|
if (!this.routers.length) {
|
|
|
|
throw errCode(new Error('No content this.routers available'), 'NO_ROUTERS_AVAILABLE')
|
|
|
|
}
|
|
|
|
|
2021-01-21 12:41:27 +00:00
|
|
|
yield * pipe(
|
|
|
|
merge(
|
|
|
|
...this.routers.map(router => router.findProviders(key, options))
|
|
|
|
),
|
|
|
|
(source) => storeAddresses(source, this.libp2p.peerStore),
|
|
|
|
(source) => uniquePeers(source),
|
|
|
|
(source) => maybeLimitSource(source, options.maxNumProviders),
|
|
|
|
(source) => requirePeers(source)
|
2020-12-10 14:48:14 +01:00
|
|
|
)
|
2018-10-19 16:28:28 +02:00
|
|
|
}
|
|
|
|
|
2020-12-10 14:48:14 +01:00
|
|
|
/**
|
|
|
|
* Iterates over all content routers in parallel to notify it is
|
|
|
|
* a provider of the given key.
|
|
|
|
*
|
|
|
|
* @param {CID} key - The CID key of the content to find
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
*/
|
|
|
|
async provide (key) {
|
|
|
|
if (!this.routers.length) {
|
|
|
|
throw errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE')
|
2019-12-01 22:54:59 +01:00
|
|
|
}
|
2020-12-10 14:48:14 +01:00
|
|
|
|
|
|
|
await Promise.all(this.routers.map((router) => router.provide(key)))
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Store the given key/value pair in the DHT.
|
|
|
|
*
|
|
|
|
* @param {Uint8Array} key
|
|
|
|
* @param {Uint8Array} value
|
|
|
|
* @param {Object} [options] - put options
|
|
|
|
* @param {number} [options.minPeers] - minimum number of peers required to successfully put
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
*/
|
|
|
|
put (key, value, options) {
|
|
|
|
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
|
|
|
|
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.dht.put(key, value, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the value to the given key.
|
|
|
|
* Times out after 1 minute by default.
|
|
|
|
*
|
|
|
|
* @param {Uint8Array} key
|
|
|
|
* @param {Object} [options] - get options
|
|
|
|
* @param {number} [options.timeout] - optional timeout (default: 60000)
|
|
|
|
* @returns {Promise<GetData>}
|
|
|
|
*/
|
|
|
|
get (key, options) {
|
|
|
|
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
|
|
|
|
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.dht.get(key, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the `n` values to the given key without sorting.
|
|
|
|
*
|
|
|
|
* @param {Uint8Array} key
|
|
|
|
* @param {number} nVals
|
|
|
|
* @param {Object} [options] - get options
|
|
|
|
* @param {number} [options.timeout] - optional timeout (default: 60000)
|
|
|
|
* @returns {Promise<GetData[]>}
|
|
|
|
*/
|
|
|
|
async getMany (key, nVals, options) { // eslint-disable-line require-await
|
|
|
|
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
|
|
|
|
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.dht.getMany(key, nVals, options)
|
2018-02-07 07:48:37 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-10 14:48:14 +01:00
|
|
|
|
|
|
|
module.exports = ContentRouting
|