UNPKG

@libp2p/peer-store

Version:

Stores information about peers libp2p knows on the network

197 lines 7.35 kB
/* eslint-disable complexity */ import { publicKeyToProtobuf } from '@libp2p/crypto/keys'; import { InvalidParametersError } from '@libp2p/interface'; import { equals as uint8ArrayEquals } from 'uint8arrays/equals'; import { dedupeFilterAndSortAddresses } from './dedupe-addresses.js'; export async function toPeerPB(peerId, data, strategy, options) { if (data == null) { throw new InvalidParametersError('Invalid PeerData'); } if (data.publicKey != null && peerId.publicKey != null && !data.publicKey.equals(peerId.publicKey)) { throw new InvalidParametersError('publicKey bytes do not match peer id publicKey bytes'); } const existingPeer = options.existingPeer?.peer; if (existingPeer != null && !peerId.equals(existingPeer.id)) { throw new InvalidParametersError('peer id did not match existing peer id'); } let addresses = existingPeer?.addresses ?? []; let protocols = new Set(existingPeer?.protocols ?? []); let metadata = existingPeer?.metadata ?? new Map(); let tags = existingPeer?.tags ?? new Map(); let peerRecordEnvelope = existingPeer?.peerRecordEnvelope; // when patching, we replace the original fields with passed values if (strategy === 'patch') { if (data.multiaddrs != null || data.addresses != null) { addresses = []; if (data.multiaddrs != null) { addresses.push(...data.multiaddrs.map(multiaddr => ({ isCertified: false, multiaddr }))); } if (data.addresses != null) { addresses.push(...data.addresses); } } if (data.protocols != null) { protocols = new Set(data.protocols); } if (data.metadata != null) { const metadataEntries = data.metadata instanceof Map ? [...data.metadata.entries()] : Object.entries(data.metadata); metadata = createSortedMap(metadataEntries, { validate: validateMetadata }); } if (data.tags != null) { const tagsEntries = data.tags instanceof Map ? [...data.tags.entries()] : Object.entries(data.tags); tags = createSortedMap(tagsEntries, { validate: validateTag, map: mapTag }); } if (data.peerRecordEnvelope != null) { peerRecordEnvelope = data.peerRecordEnvelope; } } // when merging, we join the original fields with passed values if (strategy === 'merge') { if (data.multiaddrs != null) { addresses.push(...data.multiaddrs.map(multiaddr => ({ isCertified: false, multiaddr }))); } if (data.addresses != null) { addresses.push(...data.addresses); } if (data.protocols != null) { protocols = new Set([...protocols, ...data.protocols]); } if (data.metadata != null) { const metadataEntries = data.metadata instanceof Map ? [...data.metadata.entries()] : Object.entries(data.metadata); for (const [key, value] of metadataEntries) { if (value == null) { metadata.delete(key); } else { metadata.set(key, value); } } metadata = createSortedMap([...metadata.entries()], { validate: validateMetadata }); } if (data.tags != null) { const tagsEntries = data.tags instanceof Map ? [...data.tags.entries()] : Object.entries(data.tags); const mergedTags = new Map(tags); for (const [key, value] of tagsEntries) { if (value == null) { mergedTags.delete(key); } else { mergedTags.set(key, value); } } tags = createSortedMap([...mergedTags.entries()], { validate: validateTag, map: mapTag }); } if (data.peerRecordEnvelope != null) { peerRecordEnvelope = data.peerRecordEnvelope; } } let publicKey; if (existingPeer?.id.publicKey != null) { publicKey = publicKeyToProtobuf(existingPeer.id.publicKey); } else if (data.publicKey != null) { publicKey = publicKeyToProtobuf(data.publicKey); } else if (peerId.publicKey != null) { publicKey = publicKeyToProtobuf(peerId.publicKey); } const output = { addresses: await dedupeFilterAndSortAddresses(peerId, options.addressFilter ?? (async () => true), addresses, options.existingPeer?.peerPB.addresses), protocols: [...protocols.values()].sort((a, b) => { return a.localeCompare(b); }), metadata, tags, publicKey, peerRecordEnvelope }; // add observed addresses to multiaddrs output.addresses.forEach(addr => { addr.observed = options.existingPeer?.peerPB.addresses?.find(addr => uint8ArrayEquals(addr.multiaddr, addr.multiaddr))?.observed ?? Date.now(); }); // Ed25519 and secp256k1 have their public key embedded in them so no need to duplicate it if (peerId.type !== 'RSA') { delete output.publicKey; } return output; } /** * In JS maps are ordered by insertion order so create a new map with the * keys inserted in alphabetical order. */ function createSortedMap(entries, options) { const output = new Map(); for (const [key, value] of entries) { if (value == null) { continue; } options.validate(key, value); } for (const [key, value] of entries.sort(([a], [b]) => { return a.localeCompare(b); })) { if (value != null) { output.set(key, options.map?.(key, value) ?? value); } } return output; } function validateMetadata(key, value) { if (typeof key !== 'string') { throw new InvalidParametersError('Metadata key must be a string'); } if (!(value instanceof Uint8Array)) { throw new InvalidParametersError('Metadata value must be a Uint8Array'); } } function validateTag(key, tag) { if (typeof key !== 'string') { throw new InvalidParametersError('Tag name must be a string'); } if (tag.value != null) { if (parseInt(`${tag.value}`, 10) !== tag.value) { throw new InvalidParametersError('Tag value must be an integer'); } if (tag.value < 0 || tag.value > 100) { throw new InvalidParametersError('Tag value must be between 0-100'); } } if (tag.ttl != null) { if (parseInt(`${tag.ttl}`, 10) !== tag.ttl) { throw new InvalidParametersError('Tag ttl must be an integer'); } if (tag.ttl < 0) { throw new InvalidParametersError('Tag ttl must be between greater than 0'); } } } function mapTag(key, tag) { let expiry; if (tag.expiry != null) { expiry = tag.expiry; } if (tag.ttl != null) { expiry = BigInt(Date.now() + Number(tag.ttl)); } return { value: tag.value ?? 0, expiry }; } //# sourceMappingURL=to-peer-pb.js.map