From 4afd7a9c17b91a51f6d4e4aca442e3c2bf4a3834 Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Fri, 26 Apr 2024 20:13:16 +0100 Subject: [PATCH] feat: add peer filter (#2508) To make filtering peers in a memory-efficient way possible, add a peer filter based on a bloom filter using the binary representation of the peer id. --------- Co-authored-by: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> --- packages/peer-collections/README.md | 14 ++++++++++ packages/peer-collections/package.json | 3 ++- packages/peer-collections/src/filter.ts | 26 +++++++++++++++++++ packages/peer-collections/src/index.ts | 15 +++++++++++ packages/peer-collections/test/filter.spec.ts | 16 ++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 packages/peer-collections/src/filter.ts create mode 100644 packages/peer-collections/test/filter.spec.ts diff --git a/packages/peer-collections/README.md b/packages/peer-collections/README.md index 24a8cd4501..d4488345d8 100644 --- a/packages/peer-collections/README.md +++ b/packages/peer-collections/README.md @@ -108,6 +108,20 @@ const set = trackedPeerSet({ name: 'my_metric_name', metrics: libp2p.metrics }) set.add(peerId) ``` +## Example - Peer filters + +```TypeScript +import { peerFilter } from '@libp2p/peer-collections' +import { createEd25519PeerId } from '@libp2p/peer-id-factory' + +const peerId = await createEd25519PeerId() + +const filter = peerFilter(1024) +filter.has(peerId) // false +filter.add(peerId) +filter.has(peerId) // true +``` + # Install ```console diff --git a/packages/peer-collections/package.json b/packages/peer-collections/package.json index e59b13129b..0463efe782 100644 --- a/packages/peer-collections/package.json +++ b/packages/peer-collections/package.json @@ -55,7 +55,8 @@ }, "dependencies": { "@libp2p/interface": "^1.3.0", - "@libp2p/peer-id": "^4.1.0" + "@libp2p/peer-id": "^4.1.0", + "@libp2p/utils": "^5.3.2" }, "devDependencies": { "@libp2p/peer-id-factory": "^4.1.0", diff --git a/packages/peer-collections/src/filter.ts b/packages/peer-collections/src/filter.ts new file mode 100644 index 0000000000..59349cbc03 --- /dev/null +++ b/packages/peer-collections/src/filter.ts @@ -0,0 +1,26 @@ +import { BloomFilter } from '@libp2p/utils/bloom-filter' +import type { PeerId } from '@libp2p/interface' + +/** + * Uses a Bloom filter to implement a mechansim for deduplicating PeerIds in a + * way that uses a fixed amount of memory. + */ +export class PeerFilter { + private readonly filter: BloomFilter + + constructor (size: number, errorRate?: number) { + this.filter = BloomFilter.create(size, errorRate) + } + + has (peerId: PeerId): boolean { + return this.filter.has(peerId.toBytes()) + } + + add (peerId: PeerId): void { + this.filter.add(peerId.toBytes()) + } +} + +export function peerFilter (size: number): PeerFilter { + return new PeerFilter(size) +} diff --git a/packages/peer-collections/src/index.ts b/packages/peer-collections/src/index.ts index dee914c33c..19bca33f4a 100644 --- a/packages/peer-collections/src/index.ts +++ b/packages/peer-collections/src/index.ts @@ -84,11 +84,26 @@ * const set = trackedPeerSet({ name: 'my_metric_name', metrics: libp2p.metrics }) * set.add(peerId) * ``` + * + * @example Peer filters + * + * ```TypeScript + * import { peerFilter } from '@libp2p/peer-collections' + * import { createEd25519PeerId } from '@libp2p/peer-id-factory' + * + * const peerId = await createEd25519PeerId() + * + * const filter = peerFilter(1024) + * filter.has(peerId) // false + * filter.add(peerId) + * filter.has(peerId) // true + * ``` */ export { PeerMap, peerMap } from './map.js' export { PeerSet, peerSet } from './set.js' export { PeerList, peerList } from './list.js' +export { PeerFilter, peerFilter } from './filter.js' export { trackedPeerMap } from './tracked-map.js' export { trackedPeerSet } from './tracked-set.js' diff --git a/packages/peer-collections/test/filter.spec.ts b/packages/peer-collections/test/filter.spec.ts new file mode 100644 index 0000000000..c30a6ba007 --- /dev/null +++ b/packages/peer-collections/test/filter.spec.ts @@ -0,0 +1,16 @@ +import { createEd25519PeerId } from '@libp2p/peer-id-factory' +import { expect } from 'aegir/chai' +import { PeerFilter } from '../src/index.js' + +describe('peer-filter', () => { + it('should filter a peer', async () => { + const filter = new PeerFilter(1024) + const peer = await createEd25519PeerId() + + expect(filter.has(peer)).to.be.false() + + filter.add(peer) + + expect(filter.has(peer)).to.be.true() + }) +})