UNPKG

ipfs-core

Version:

JavaScript implementation of the IPFS specification

123 lines (104 loc) 3.6 kB
import errcode from 'err-code' import { logger } from '@libp2p/logger' import { IpnsPublisher } from './publisher.js' import { IpnsRepublisher } from './republisher.js' import { IpnsResolver } from './resolver.js' import { TLRU } from '../utils/tlru.js' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' const log = logger('ipfs:ipns') const defaultRecordTtl = 60 * 1000 /** * @typedef {import('@libp2p/interface-keys').PrivateKey} PrivateKey * @typedef {import('@libp2p/interface-peer-id').PeerId} PeerId * @typedef {import('@libp2p/interfaces').AbortOptions} AbortOptions */ export class IPNS { /** * @param {import('ipfs-core-types/src/utils').BufferStore} routing * @param {import('interface-datastore').Datastore} datastore * @param {PeerId} peerId * @param {import('@libp2p/interface-keychain').KeyChain} keychain * @param {object} options * @param {string} options.pass * @param {number} [options.initialBroadcastInterval] * @param {number} [options.broadcastInterval] */ constructor (routing, datastore, peerId, keychain, options) { this.publisher = new IpnsPublisher(routing, datastore) this.republisher = new IpnsRepublisher(this.publisher, datastore, peerId, keychain, options) this.resolver = new IpnsResolver(routing) this.cache = new TLRU(1000) this.routing = routing } /** * Publish * * @param {PeerId} peerId * @param {Uint8Array} value * @param {number} lifetime * @param {AbortOptions} [options] */ async publish (peerId, value, lifetime = IpnsPublisher.defaultRecordLifetime, options) { try { await this.publisher.publishWithEOL(peerId, value, lifetime, options) log(`IPNS value ${uint8ArrayToString(value, 'base32')} was published correctly`) // // Add to cache const id = peerId.toString() // @ts-expect-error - parseFloat expects string const ttEol = parseFloat(lifetime) const ttl = (ttEol < defaultRecordTtl) ? ttEol : defaultRecordTtl this.cache.set(id, value, ttl) log(`IPNS value ${uint8ArrayToString(value, 'base32')} was cached correctly`) return { name: id, value: value } } catch (/** @type {any} */ err) { log.error(err) throw err } } /** * Resolve * * @param {string} name * @param {object} options * @param {boolean} [options.nocache] * @param {boolean} [options.recursive] * @param {AbortSignal} [options.signal] */ async resolve (name, options = {}) { if (typeof name !== 'string') { throw errcode(new Error('name received is not valid'), 'ERR_INVALID_NAME') } // If recursive, we should not try to get the cached value if (!options.nocache && !options.recursive) { // Try to get the record from cache const id = name.split('/')[2] const result = this.cache.get(id) if (result) { return result } } try { const result = await this.resolver.resolve(name, options) log(`IPNS record from ${name} was resolved correctly`) return result } catch (/** @type {any} */ err) { log.error(err) throw err } } /** * Initialize keyspace * * Sets the ipns record for the given key to point to an empty directory * * @param {PeerId} peerId * @param {Uint8Array} value * @param {AbortOptions} [options] */ async initializeKeyspace (peerId, value, options) { // eslint-disable-line require-await return this.publish(peerId, value, IpnsPublisher.defaultRecordLifetime, options) } }