implement peer-store re-bootstrapping

This commit is contained in:
Sasha 2025-09-11 22:23:02 +02:00
parent 8f09f5fa5a
commit 8d773c4620
No known key found for this signature in database
2 changed files with 108 additions and 0 deletions

View File

@ -0,0 +1,100 @@
import { PeerId } from "@libp2p/interface";
import { Libp2p } from "@waku/interfaces";
import { Logger } from "@waku/utils";
type BootstrapTriggerConstructorOptions = {
libp2p: Libp2p;
};
interface IBootstrapTrigger {
start(): void;
stop(): void;
}
const log = new Logger("bootstrap-trigger");
const DEFAULT_BOOTSTRAP_TIMEOUT_MS = 1000;
export class BootstrapTrigger implements IBootstrapTrigger {
private readonly libp2p: Libp2p;
private bootstrapTimeout: NodeJS.Timeout | null = null;
public constructor(options: BootstrapTriggerConstructorOptions) {
this.libp2p = options.libp2p;
}
public start(): void {
log.info("Starting bootstrap trigger");
this.libp2p.addEventListener("peer:disconnect", this.onPeerDisconnectEvent);
}
public stop(): void {
log.info("Stopping bootstrap trigger");
this.libp2p.removeEventListener(
"peer:disconnect",
this.onPeerDisconnectEvent
);
if (this.bootstrapTimeout) {
clearTimeout(this.bootstrapTimeout);
this.bootstrapTimeout = null;
log.info("Cleared pending bootstrap timeout");
}
}
private onPeerDisconnectEvent = (event: CustomEvent<PeerId>): void => {
const peerId = event.detail;
const connections = this.libp2p.getConnections();
log.info(
`Peer disconnected: ${peerId.toString()}, remaining connections: ${connections.length}`
);
if (connections.length !== 0) {
return;
}
log.info(
`Last peer disconnected, scheduling bootstrap in ${DEFAULT_BOOTSTRAP_TIMEOUT_MS} milliseconds`
);
if (this.bootstrapTimeout) {
clearTimeout(this.bootstrapTimeout);
}
this.bootstrapTimeout = setTimeout(() => {
log.info("Triggering bootstrap after timeout");
this.triggerBootstrap();
this.bootstrapTimeout = null;
}, DEFAULT_BOOTSTRAP_TIMEOUT_MS);
};
private triggerBootstrap(): void {
log.info("Triggering bootstrap discovery");
const bootstrapComponents = Object.values(this.libp2p.components.components)
.filter((c) => !!c)
.filter(
(c: unknown) =>
(c as { [Symbol.toStringTag]: string })[Symbol.toStringTag] ===
"@waku/bootstrap"
);
if (bootstrapComponents.length === 0) {
log.warn("No bootstrap components found to trigger");
return;
}
log.info(
`Found ${bootstrapComponents.length} bootstrap components, starting them`
);
bootstrapComponents.forEach((component) => {
try {
(component as { start: () => void }).start();
log.info("Successfully started bootstrap component");
} catch (error) {
log.error("Failed to start bootstrap component", error);
}
});
}
}

View File

@ -11,6 +11,7 @@ import {
import { Libp2p } from "@waku/interfaces";
import { Logger } from "@waku/utils";
import { BootstrapTrigger } from "./bootstrap_trigger.js";
import { ConnectionLimiter } from "./connection_limiter.js";
import { Dialer } from "./dialer.js";
import { DiscoveryDialer } from "./discovery_dialer.js";
@ -45,6 +46,7 @@ export class ConnectionManager implements IConnectionManager {
private readonly shardReader: ShardReader;
private readonly networkMonitor: NetworkMonitor;
private readonly connectionLimiter: ConnectionLimiter;
private readonly bootstrapTrigger: BootstrapTrigger;
private readonly options: ConnectionManagerOptions;
private libp2p: Libp2p;
@ -64,6 +66,10 @@ export class ConnectionManager implements IConnectionManager {
...options.config
};
this.bootstrapTrigger = new BootstrapTrigger({
libp2p: options.libp2p
});
this.keepAliveManager = new KeepAliveManager({
relay: options.relay,
libp2p: options.libp2p,
@ -110,6 +116,7 @@ export class ConnectionManager implements IConnectionManager {
this.discoveryDialer.start();
this.keepAliveManager.start();
this.connectionLimiter.start();
this.bootstrapTrigger.start();
}
public stop(): void {
@ -118,6 +125,7 @@ export class ConnectionManager implements IConnectionManager {
this.discoveryDialer.stop();
this.keepAliveManager.stop();
this.connectionLimiter.stop();
this.bootstrapTrigger.stop();
}
public isConnected(): boolean {