fix: attempt to fix some of the Filter issues (#2183)

* feat: lighten retry logic for LightPush

* update tests

* remove base protocol sdk from light push, add unit tests for light push

* remove replaced test

* ensure numPeersToUse is respected

* turn off check for missing messages

* fix recurring ping

* add useful logs

* skip tests

* remove comment

* feat: check filter subscriptions against lightPush (#2185)
This commit is contained in:
Sasha 2024-10-17 01:01:21 +02:00 committed by GitHub
parent 4049123f14
commit ded994f8ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 219 additions and 147 deletions

View File

@ -20,7 +20,7 @@ export type SubscriptionCallback<T extends IDecodedMessage> = {
export type SubscribeOptions = { export type SubscribeOptions = {
keepAlive?: number; keepAlive?: number;
pingsBeforePeerRenewed?: number; pingsBeforePeerRenewed?: number;
maxMissedMessagesThreshold?: number; enableLightPushFilterCheck?: boolean;
}; };
export interface ISubscription { export interface ISubscription {

View File

@ -2,7 +2,7 @@ import { sha256 } from "@noble/hashes/sha256";
import type { IDecodedMessage, IProtoMessage } from "@waku/interfaces"; import type { IDecodedMessage, IProtoMessage } from "@waku/interfaces";
import { isDefined } from "@waku/utils"; import { isDefined } from "@waku/utils";
import { import {
bytesToUtf8, bytesToHex,
concat, concat,
numberToBytes, numberToBytes,
utf8ToBytes utf8ToBytes
@ -56,6 +56,6 @@ export function messageHashStr(
message: IProtoMessage | IDecodedMessage message: IProtoMessage | IDecodedMessage
): string { ): string {
const hash = messageHash(pubsubTopic, message); const hash = messageHash(pubsubTopic, message);
const hashStr = bytesToUtf8(hash); const hashStr = bytesToHex(hash);
return hashStr; return hashStr;
} }

View File

@ -1,5 +1,8 @@
export const DEFAULT_KEEP_ALIVE = 60_000; export const DEFAULT_KEEP_ALIVE = 60_000;
export const DEFAULT_LIGHT_PUSH_FILTER_CHECK = false;
export const DEFAULT_LIGHT_PUSH_FILTER_CHECK_INTERVAL = 10_000;
export const DEFAULT_SUBSCRIBE_OPTIONS = { export const DEFAULT_SUBSCRIBE_OPTIONS = {
keepAlive: DEFAULT_KEEP_ALIVE keepAlive: DEFAULT_KEEP_ALIVE,
enableLightPushFilterCheck: DEFAULT_LIGHT_PUSH_FILTER_CHECK
}; };

View File

@ -6,6 +6,7 @@ import {
type IDecodedMessage, type IDecodedMessage,
type IDecoder, type IDecoder,
type IFilter, type IFilter,
type ILightPush,
type Libp2p, type Libp2p,
NetworkConfig, NetworkConfig,
type ProtocolCreateOptions, type ProtocolCreateOptions,
@ -38,7 +39,8 @@ class Filter extends BaseProtocolSDK implements IFilter {
public constructor( public constructor(
connectionManager: ConnectionManager, connectionManager: ConnectionManager,
libp2p: Libp2p, private libp2p: Libp2p,
private lightPush?: ILightPush,
options?: ProtocolCreateOptions options?: ProtocolCreateOptions
) { ) {
super( super(
@ -195,7 +197,9 @@ class Filter extends BaseProtocolSDK implements IFilter {
this.protocol, this.protocol,
this.connectionManager, this.connectionManager,
() => this.connectedPeers, () => this.connectedPeers,
this.renewPeer.bind(this) this.renewPeer.bind(this),
this.libp2p,
this.lightPush
) )
); );
@ -300,7 +304,9 @@ class Filter extends BaseProtocolSDK implements IFilter {
export function wakuFilter( export function wakuFilter(
connectionManager: ConnectionManager, connectionManager: ConnectionManager,
lightPush?: ILightPush,
init?: ProtocolCreateOptions init?: ProtocolCreateOptions
): (libp2p: Libp2p) => IFilter { ): (libp2p: Libp2p) => IFilter {
return (libp2p: Libp2p) => new Filter(connectionManager, libp2p, init); return (libp2p: Libp2p) =>
new Filter(connectionManager, libp2p, lightPush, init);
} }

View File

@ -1,6 +1,12 @@
import type { Peer } from "@libp2p/interface"; import type { Peer } from "@libp2p/interface";
import type { PeerId } from "@libp2p/interface"; import type { PeerId } from "@libp2p/interface";
import { ConnectionManager, FilterCore } from "@waku/core"; import {
ConnectionManager,
createDecoder,
createEncoder,
FilterCore,
LightPushCore
} from "@waku/core";
import { import {
type Callback, type Callback,
type ContentTopic, type ContentTopic,
@ -8,8 +14,10 @@ import {
EConnectionStateEvents, EConnectionStateEvents,
type IDecodedMessage, type IDecodedMessage,
type IDecoder, type IDecoder,
type ILightPush,
type IProtoMessage, type IProtoMessage,
type ISubscription, type ISubscription,
type Libp2p,
type PeerIdStr, type PeerIdStr,
ProtocolError, ProtocolError,
type PubsubTopic, type PubsubTopic,
@ -23,14 +31,23 @@ import { groupByContentTopic, Logger } from "@waku/utils";
import { ReliabilityMonitorManager } from "../../reliability_monitor/index.js"; import { ReliabilityMonitorManager } from "../../reliability_monitor/index.js";
import { ReceiverReliabilityMonitor } from "../../reliability_monitor/receiver.js"; import { ReceiverReliabilityMonitor } from "../../reliability_monitor/receiver.js";
import { DEFAULT_KEEP_ALIVE, DEFAULT_SUBSCRIBE_OPTIONS } from "./constants.js"; import {
DEFAULT_KEEP_ALIVE,
DEFAULT_LIGHT_PUSH_FILTER_CHECK,
DEFAULT_LIGHT_PUSH_FILTER_CHECK_INTERVAL,
DEFAULT_SUBSCRIBE_OPTIONS
} from "./constants.js";
const log = new Logger("sdk:filter:subscription_manager"); const log = new Logger("sdk:filter:subscription_manager");
export class SubscriptionManager implements ISubscription { export class SubscriptionManager implements ISubscription {
private reliabilityMonitor: ReceiverReliabilityMonitor; private reliabilityMonitor: ReceiverReliabilityMonitor;
private keepAliveTimer: number | null = null; private keepAliveTimeout: number = DEFAULT_KEEP_ALIVE;
private keepAliveInterval: ReturnType<typeof setInterval> | null = null;
private enableLightPushFilterCheck = DEFAULT_LIGHT_PUSH_FILTER_CHECK;
private subscriptionCallbacks: Map< private subscriptionCallbacks: Map<
ContentTopic, ContentTopic,
SubscriptionCallback<IDecodedMessage> SubscriptionCallback<IDecodedMessage>
@ -43,7 +60,9 @@ export class SubscriptionManager implements ISubscription {
private readonly getPeers: () => Peer[], private readonly getPeers: () => Peer[],
private readonly renewPeer: ( private readonly renewPeer: (
peerToDisconnect: PeerId peerToDisconnect: PeerId
) => Promise<Peer | undefined> ) => Promise<Peer | undefined>,
private readonly libp2p: Libp2p,
private readonly lightPush?: ILightPush
) { ) {
this.pubsubTopic = pubsubTopic; this.pubsubTopic = pubsubTopic;
this.subscriptionCallbacks = new Map(); this.subscriptionCallbacks = new Map();
@ -54,7 +73,8 @@ export class SubscriptionManager implements ISubscription {
this.renewPeer.bind(this), this.renewPeer.bind(this),
() => Array.from(this.subscriptionCallbacks.keys()), () => Array.from(this.subscriptionCallbacks.keys()),
this.protocol.subscribe.bind(this.protocol), this.protocol.subscribe.bind(this.protocol),
this.protocol.addLibp2pEventListener.bind(this.protocol) this.protocol.addLibp2pEventListener.bind(this.protocol),
this.sendLightPushCheckMessage.bind(this)
); );
} }
@ -63,11 +83,10 @@ export class SubscriptionManager implements ISubscription {
callback: Callback<T>, callback: Callback<T>,
options: SubscribeOptions = DEFAULT_SUBSCRIBE_OPTIONS options: SubscribeOptions = DEFAULT_SUBSCRIBE_OPTIONS
): Promise<SDKProtocolResult> { ): Promise<SDKProtocolResult> {
this.reliabilityMonitor.setMaxMissedMessagesThreshold(
options.maxMissedMessagesThreshold
);
this.reliabilityMonitor.setMaxPingFailures(options.pingsBeforePeerRenewed); this.reliabilityMonitor.setMaxPingFailures(options.pingsBeforePeerRenewed);
this.keepAliveTimer = options.keepAlive || DEFAULT_KEEP_ALIVE; this.keepAliveTimeout = options.keepAlive || DEFAULT_KEEP_ALIVE;
this.enableLightPushFilterCheck =
options?.enableLightPushFilterCheck || DEFAULT_LIGHT_PUSH_FILTER_CHECK;
const decodersArray = Array.isArray(decoders) ? decoders : [decoders]; const decodersArray = Array.isArray(decoders) ? decoders : [decoders];
@ -85,11 +104,20 @@ export class SubscriptionManager implements ISubscription {
} }
} }
if (this.enableLightPushFilterCheck) {
decodersArray.push(
createDecoder(
this.buildLightPushContentTopic(),
this.pubsubTopic
) as IDecoder<T>
);
}
const decodersGroupedByCT = groupByContentTopic(decodersArray); const decodersGroupedByCT = groupByContentTopic(decodersArray);
const contentTopics = Array.from(decodersGroupedByCT.keys()); const contentTopics = Array.from(decodersGroupedByCT.keys());
const promises = this.getPeers().map(async (peer) => const promises = this.getPeers().map(async (peer) =>
this.protocol.subscribe(this.pubsubTopic, peer, contentTopics) this.subscribeWithPeerVerification(peer, contentTopics)
); );
const results = await Promise.allSettled(promises); const results = await Promise.allSettled(promises);
@ -107,12 +135,17 @@ export class SubscriptionManager implements ISubscription {
callback callback
} as unknown as SubscriptionCallback<IDecodedMessage>; } as unknown as SubscriptionCallback<IDecodedMessage>;
// don't handle case of internal content topic
if (contentTopic === this.buildLightPushContentTopic()) {
return;
}
// The callback and decoder may override previous values, this is on // The callback and decoder may override previous values, this is on
// purpose as the user may call `subscribe` to refresh the subscription // purpose as the user may call `subscribe` to refresh the subscription
this.subscriptionCallbacks.set(contentTopic, subscriptionCallback); this.subscriptionCallbacks.set(contentTopic, subscriptionCallback);
}); });
this.startSubscriptionsMaintenance(this.keepAliveTimer); this.startSubscriptionsMaintenance(this.keepAliveTimeout);
return finalResult; return finalResult;
} }
@ -174,10 +207,9 @@ export class SubscriptionManager implements ISubscription {
message: WakuMessage, message: WakuMessage,
peerIdStr: PeerIdStr peerIdStr: PeerIdStr
): Promise<void> { ): Promise<void> {
const alreadyReceived = this.reliabilityMonitor.processIncomingMessage( const alreadyReceived = this.reliabilityMonitor.notifyMessageReceived(
message, peerIdStr,
this.pubsubTopic, message as IProtoMessage
peerIdStr
); );
if (alreadyReceived) { if (alreadyReceived) {
@ -200,6 +232,19 @@ export class SubscriptionManager implements ISubscription {
await pushMessage(subscriptionCallback, this.pubsubTopic, message); await pushMessage(subscriptionCallback, this.pubsubTopic, message);
} }
private async subscribeWithPeerVerification(
peer: Peer,
contentTopics: string[]
): Promise<CoreProtocolResult> {
const result = await this.protocol.subscribe(
this.pubsubTopic,
peer,
contentTopics
);
await this.sendLightPushCheckMessage(peer);
return result;
}
private handleResult( private handleResult(
results: PromiseSettledResult<CoreProtocolResult>[], results: PromiseSettledResult<CoreProtocolResult>[],
type: "ping" | "subscribe" | "unsubscribe" | "unsubscribeAll" type: "ping" | "subscribe" | "unsubscribe" | "unsubscribeAll"
@ -240,23 +285,26 @@ export class SubscriptionManager implements ISubscription {
let result; let result;
try { try {
result = await this.protocol.ping(peer); result = await this.protocol.ping(peer);
return result;
} catch (error) { } catch (error) {
return { result = {
success: null, success: null,
failure: { failure: {
peerId, peerId,
error: ProtocolError.GENERIC_FAIL error: ProtocolError.GENERIC_FAIL
} }
}; };
} finally {
void this.reliabilityMonitor.handlePingResult(peerId, result);
} }
log.info(
`Received result from filter ping peerId:${peerId.toString()}\tsuccess:${result.success?.toString()}\tfailure:${result.failure?.error}`
);
await this.reliabilityMonitor.handlePingResult(peerId, result);
return result;
} }
private startSubscriptionsMaintenance(interval: number): void { private startSubscriptionsMaintenance(timeout: number): void {
log.info("Starting subscriptions maintenance"); log.info("Starting subscriptions maintenance");
this.startKeepAlivePings(interval); this.startKeepAlivePings(timeout);
this.startConnectionListener(); this.startConnectionListener();
} }
@ -295,31 +343,69 @@ export class SubscriptionManager implements ISubscription {
log.error(`networkStateListener failed to recover: ${err}`); log.error(`networkStateListener failed to recover: ${err}`);
} }
this.startKeepAlivePings(this.keepAliveTimer || DEFAULT_KEEP_ALIVE); this.startKeepAlivePings(this.keepAliveTimeout);
} }
private startKeepAlivePings(interval: number): void { private startKeepAlivePings(timeout: number): void {
if (this.keepAliveTimer) { if (this.keepAliveInterval) {
log.info("Recurring pings already set up."); log.info("Recurring pings already set up.");
return; return;
} }
this.keepAliveTimer = setInterval(() => { this.keepAliveInterval = setInterval(() => {
void this.ping() void this.ping();
.then(() => log.info("Keep-alive ping successful")) }, timeout);
.catch((error) => log.error("Error in keep-alive ping cycle:", error));
}, interval) as unknown as number;
} }
private stopKeepAlivePings(): void { private stopKeepAlivePings(): void {
if (!this.keepAliveTimer) { if (!this.keepAliveInterval) {
log.info("Already stopped recurring pings."); log.info("Already stopped recurring pings.");
return; return;
} }
log.info("Stopping recurring pings."); log.info("Stopping recurring pings.");
clearInterval(this.keepAliveTimer); clearInterval(this.keepAliveInterval);
this.keepAliveTimer = null; this.keepAliveInterval = null;
}
private async sendLightPushCheckMessage(peer: Peer): Promise<void> {
if (
this.lightPush &&
this.libp2p &&
this.reliabilityMonitor.shouldVerifyPeer(peer.id)
) {
const encoder = createEncoder({
contentTopic: this.buildLightPushContentTopic(),
pubsubTopic: this.pubsubTopic,
ephemeral: true
});
const message = { payload: new Uint8Array(1) };
const protoMessage = await encoder.toProtoObj(message);
// make a delay to be sure message is send when subscription is in place
setTimeout(
(async () => {
const result = await (this.lightPush!.protocol as LightPushCore).send(
encoder,
message,
peer
);
this.reliabilityMonitor.notifyMessageSent(peer.id, protoMessage);
if (result.failure) {
log.error(
`failed to send lightPush ping message to peer:${peer.id.toString()}\t${result.failure.error}`
);
return;
}
}) as () => void,
DEFAULT_LIGHT_PUSH_FILTER_CHECK_INTERVAL
);
}
}
private buildLightPushContentTopic(): string {
return `/js-waku-subscription-ping/1/${this.libp2p.peerId.toString()}/utf8`;
} }
} }

View File

@ -24,7 +24,8 @@ export class ReliabilityMonitorManager {
peer: Peer, peer: Peer,
contentTopics: ContentTopic[] contentTopics: ContentTopic[]
) => Promise<CoreProtocolResult>, ) => Promise<CoreProtocolResult>,
addLibp2pEventListener: Libp2p["addEventListener"] addLibp2pEventListener: Libp2p["addEventListener"],
sendLightPushMessage: (peer: Peer) => Promise<void>
): ReceiverReliabilityMonitor { ): ReceiverReliabilityMonitor {
if (ReliabilityMonitorManager.receiverMonitors.has(pubsubTopic)) { if (ReliabilityMonitorManager.receiverMonitors.has(pubsubTopic)) {
return ReliabilityMonitorManager.receiverMonitors.get(pubsubTopic)!; return ReliabilityMonitorManager.receiverMonitors.get(pubsubTopic)!;
@ -36,7 +37,8 @@ export class ReliabilityMonitorManager {
renewPeer, renewPeer,
getContentTopics, getContentTopics,
protocolSubscribe, protocolSubscribe,
addLibp2pEventListener addLibp2pEventListener,
sendLightPushMessage
); );
ReliabilityMonitorManager.receiverMonitors.set(pubsubTopic, monitor); ReliabilityMonitorManager.receiverMonitors.set(pubsubTopic, monitor);
return monitor; return monitor;
@ -50,7 +52,6 @@ export class ReliabilityMonitorManager {
public static stopAll(): void { public static stopAll(): void {
for (const [pubsubTopic, monitor] of this.receiverMonitors) { for (const [pubsubTopic, monitor] of this.receiverMonitors) {
monitor.setMaxMissedMessagesThreshold(undefined);
monitor.setMaxPingFailures(undefined); monitor.setMaxPingFailures(undefined);
this.receiverMonitors.delete(pubsubTopic); this.receiverMonitors.delete(pubsubTopic);
} }

View File

@ -8,24 +8,20 @@ import {
PubsubTopic PubsubTopic
} from "@waku/interfaces"; } from "@waku/interfaces";
import { messageHashStr } from "@waku/message-hash"; import { messageHashStr } from "@waku/message-hash";
import { WakuMessage } from "@waku/proto";
import { Logger } from "@waku/utils"; import { Logger } from "@waku/utils";
import { bytesToUtf8 } from "@waku/utils/bytes";
type ReceivedMessageHashes = {
all: Set<string>;
nodes: Record<PeerIdStr, Set<string>>;
};
const DEFAULT_MAX_MISSED_MESSAGES_THRESHOLD = 3;
const log = new Logger("sdk:receiver:reliability_monitor"); const log = new Logger("sdk:receiver:reliability_monitor");
const DEFAULT_MAX_PINGS = 3; const DEFAULT_MAX_PINGS = 3;
const MESSAGE_VERIFICATION_DELAY = 5_000;
export class ReceiverReliabilityMonitor { export class ReceiverReliabilityMonitor {
private receivedMessagesHashes: ReceivedMessageHashes; private receivedMessagesFormPeer = new Set<string>();
private missedMessagesByPeer: Map<string, number> = new Map(); private receivedMessages = new Set<string>();
private maxMissedMessagesThreshold = DEFAULT_MAX_MISSED_MESSAGES_THRESHOLD; private scheduledVerification = new Map<string, number>();
private verifiedPeers = new Set<string>();
private peerFailures: Map<string, number> = new Map(); private peerFailures: Map<string, number> = new Map();
private maxPingFailures: number = DEFAULT_MAX_PINGS; private maxPingFailures: number = DEFAULT_MAX_PINGS;
private peerRenewalLocks: Set<PeerIdStr> = new Set(); private peerRenewalLocks: Set<PeerIdStr> = new Set();
@ -40,18 +36,9 @@ export class ReceiverReliabilityMonitor {
peer: Peer, peer: Peer,
contentTopics: ContentTopic[] contentTopics: ContentTopic[]
) => Promise<CoreProtocolResult>, ) => Promise<CoreProtocolResult>,
private addLibp2pEventListener: Libp2p["addEventListener"] private addLibp2pEventListener: Libp2p["addEventListener"],
private sendLightPushMessage: (peer: Peer) => Promise<void>
) { ) {
const allPeerIdStr = this.getPeers().map((p) => p.id.toString());
this.receivedMessagesHashes = {
all: new Set(),
nodes: {
...Object.fromEntries(allPeerIdStr.map((peerId) => [peerId, new Set()]))
}
};
allPeerIdStr.forEach((peerId) => this.missedMessagesByPeer.set(peerId, 0));
this.addLibp2pEventListener("peer:disconnect", (evt) => { this.addLibp2pEventListener("peer:disconnect", (evt) => {
const peerId = evt.detail; const peerId = evt.detail;
if (this.getPeers().some((p) => p.id.equals(peerId))) { if (this.getPeers().some((p) => p.id.equals(peerId))) {
@ -60,13 +47,6 @@ export class ReceiverReliabilityMonitor {
}); });
} }
public setMaxMissedMessagesThreshold(value: number | undefined): void {
if (value === undefined) {
return;
}
this.maxMissedMessagesThreshold = value;
}
public setMaxPingFailures(value: number | undefined): void { public setMaxPingFailures(value: number | undefined): void {
if (value === undefined) { if (value === undefined) {
return; return;
@ -88,6 +68,9 @@ export class ReceiverReliabilityMonitor {
if (failures >= this.maxPingFailures) { if (failures >= this.maxPingFailures) {
try { try {
log.info(
`Attempting to renew ${peerId.toString()} due to ping failures.`
);
await this.renewAndSubscribePeer(peerId); await this.renewAndSubscribePeer(peerId);
this.peerFailures.delete(peerId.toString()); this.peerFailures.delete(peerId.toString());
} catch (error) { } catch (error) {
@ -96,77 +79,79 @@ export class ReceiverReliabilityMonitor {
} }
} }
public processIncomingMessage( public notifyMessageReceived(
message: WakuMessage, peerIdStr: string,
pubsubTopic: PubsubTopic, message: IProtoMessage
peerIdStr?: string
): boolean { ): boolean {
const alreadyReceived = this.addMessageToCache( const hash = this.buildMessageHash(message);
message,
pubsubTopic, this.verifiedPeers.add(peerIdStr);
peerIdStr this.receivedMessagesFormPeer.add(`${peerIdStr}-${hash}`);
log.info(
`notifyMessage received debug: ephemeral:${message.ephemeral}\t${bytesToUtf8(message.payload)}`
); );
void this.checkAndRenewPeers(); log.info(`notifyMessage received: peer:${peerIdStr}\tmessage:${hash}`);
return alreadyReceived;
if (this.receivedMessages.has(hash)) {
return true;
}
this.receivedMessages.add(hash);
return false;
} }
private addMessageToCache( public notifyMessageSent(peerId: PeerId, message: IProtoMessage): void {
message: WakuMessage, const peerIdStr = peerId.toString();
pubsubTopic: PubsubTopic, const hash = this.buildMessageHash(message);
peerIdStr?: string
): boolean {
const hashedMessageStr = messageHashStr(
pubsubTopic,
message as IProtoMessage
);
const alreadyReceived = log.info(`notifyMessage sent debug: ${bytesToUtf8(message.payload)}`);
this.receivedMessagesHashes.all.has(hashedMessageStr);
this.receivedMessagesHashes.all.add(hashedMessageStr);
if (peerIdStr) { if (this.scheduledVerification.has(peerIdStr)) {
const hashesForPeer = this.receivedMessagesHashes.nodes[peerIdStr]; log.warn(
if (!hashesForPeer) { `notifyMessage sent: attempting to schedule verification for pending peer:${peerIdStr}\tmessage:${hash}`
log.warn( );
`Peer ${peerIdStr} not initialized in receivedMessagesHashes.nodes, adding it.` return;
}
const timeout = window.setTimeout(
(async () => {
const receivedAnyMessage = this.verifiedPeers.has(peerIdStr);
const receivedTestMessage = this.receivedMessagesFormPeer.has(
`${peerIdStr}-${hash}`
); );
this.receivedMessagesHashes.nodes[peerIdStr] = new Set();
}
this.receivedMessagesHashes.nodes[peerIdStr].add(hashedMessageStr);
}
return alreadyReceived; if (receivedAnyMessage || receivedTestMessage) {
log.info(
`notifyMessage sent setTimeout: verified that peer pushes filter messages, peer:${peerIdStr}\tmessage:${hash}`
);
return;
}
log.warn(
`notifyMessage sent setTimeout: peer didn't return probe message, attempting renewAndSubscribe, peer:${peerIdStr}\tmessage:${hash}`
);
this.scheduledVerification.delete(peerIdStr);
await this.renewAndSubscribePeer(peerId);
}) as () => void,
MESSAGE_VERIFICATION_DELAY
);
this.scheduledVerification.set(peerIdStr, timeout);
} }
private async checkAndRenewPeers(): Promise<void> { public shouldVerifyPeer(peerId: PeerId): boolean {
for (const hash of this.receivedMessagesHashes.all) { const peerIdStr = peerId.toString();
for (const [peerIdStr, hashes] of Object.entries(
this.receivedMessagesHashes.nodes const isPeerVerified = this.verifiedPeers.has(peerIdStr);
)) { const isVerificationPending = this.scheduledVerification.has(peerIdStr);
if (!hashes.has(hash)) {
this.incrementMissedMessageCount(peerIdStr); return !(isPeerVerified || isVerificationPending);
if (this.shouldRenewPeer(peerIdStr)) { }
log.info(
`Peer ${peerIdStr} has missed too many messages, renewing.` private buildMessageHash(message: IProtoMessage): string {
); return messageHashStr(this.pubsubTopic, message);
const peerId = this.getPeers().find(
(p) => p.id.toString() === peerIdStr
)?.id;
if (!peerId) {
log.error(
`Unexpected Error: Peer ${peerIdStr} not found in connected peers.`
);
continue;
}
try {
await this.renewAndSubscribePeer(peerId);
} catch (error) {
log.error(`Failed to renew peer ${peerIdStr}: ${error}`);
}
}
}
}
}
} }
private async renewAndSubscribePeer( private async renewAndSubscribePeer(
@ -193,12 +178,9 @@ export class ReceiverReliabilityMonitor {
this.getContentTopics() this.getContentTopics()
); );
this.receivedMessagesHashes.nodes[newPeer.id.toString()] = new Set(); await this.sendLightPushMessage(newPeer);
this.missedMessagesByPeer.set(newPeer.id.toString(), 0);
this.peerFailures.delete(peerIdStr); this.peerFailures.delete(peerIdStr);
this.missedMessagesByPeer.delete(peerIdStr);
delete this.receivedMessagesHashes.nodes[peerIdStr];
return newPeer; return newPeer;
} catch (error) { } catch (error) {
@ -208,14 +190,4 @@ export class ReceiverReliabilityMonitor {
this.peerRenewalLocks.delete(peerIdStr); this.peerRenewalLocks.delete(peerIdStr);
} }
} }
private incrementMissedMessageCount(peerIdStr: string): void {
const currentCount = this.missedMessagesByPeer.get(peerIdStr) || 0;
this.missedMessagesByPeer.set(peerIdStr, currentCount + 1);
}
private shouldRenewPeer(peerIdStr: string): boolean {
const missedMessages = this.missedMessagesByPeer.get(peerIdStr) || 0;
return missedMessages > this.maxMissedMessagesThreshold;
}
} }

View File

@ -116,7 +116,11 @@ export class WakuNode implements IWaku {
} }
if (protocolsEnabled.filter) { if (protocolsEnabled.filter) {
const filter = wakuFilter(this.connectionManager, options); const filter = wakuFilter(
this.connectionManager,
this.lightPush,
options
);
this.filter = filter(libp2p); this.filter = filter(libp2p);
} }