From 1a23a3eaab36cdb11d14fada47547021e808e551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Pi=C4=85tkowski?= Date: Sun, 30 Nov 2025 22:56:58 +0100 Subject: [PATCH 1/2] fix: small improvements --- be/config.yaml | 2 +- be/src/start-libp2p-relay.ts | 2 +- fe/index.html | 54 ++++++++++++++++++- fe/src/App.tsx | 45 ++++++++-------- fe/src/components/Box/sub-pages/Welcome.tsx | 53 ++++++++++-------- .../src/peers-message-exchange-protocol.ts | 2 +- 6 files changed, 111 insertions(+), 47 deletions(-) diff --git a/be/config.yaml b/be/config.yaml index 1e2b515..9418879 100644 --- a/be/config.yaml +++ b/be/config.yaml @@ -2,7 +2,7 @@ libp2p: listenMultiaddrs: - - /ip4/127.0.0.1/tcp/8080/ws + - /ip4/0.0.0.0/tcp/8080/ws announceMultiaddrs: - /ip4/127.0.0.1/tcp/8080/ws # - /dns4/example.com/tcp/443/wss Example of multiaddr with Secure WebSockets diff --git a/be/src/start-libp2p-relay.ts b/be/src/start-libp2p-relay.ts index 7a71a34..2d453c2 100644 --- a/be/src/start-libp2p-relay.ts +++ b/be/src/start-libp2p-relay.ts @@ -83,7 +83,7 @@ export async function startLibp2pRelay({ const peerMessageExchange = new PeerMessageExchangeProtocol({ protocolId: "/icod2/relay-peer-message-exchange/1.0.0", }); - peerMessageExchange.initialize(libp2p); + peerMessageExchange.initialize(libp2p, { skipHandleInitialization: false }); logger.info( { peerId, shortPeerId: shortenPeerId(peerId) }, diff --git a/fe/index.html b/fe/index.html index da9e8e7..fec9267 100644 --- a/fe/index.html +++ b/fe/index.html @@ -26,11 +26,63 @@ body { margin: 0; } + + #root { + min-height: 100vh; + } + + .preload { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 100vh; + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", sans-serif; + color: #c089ff; + background: transparent; + opacity: 0; + animation: preload-fade 1500ms ease forwards; + animation-delay: 750ms; + } + + .preload__spinner { + width: 48px; + height: 48px; + border: 4px solid rgba(192, 137, 255, 0.25); + border-top-color: #c089ff; + border-radius: 50%; + animation: preload-spin 1s linear infinite; + margin-bottom: 12px; + } + + @keyframes preload-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } + + @keyframes preload-fade { + from { + opacity: 0; + } + to { + opacity: 1; + } + } Stashcrate -
+
+
+
+

Loading Stashcrate...

+
+
diff --git a/fe/src/App.tsx b/fe/src/App.tsx index b264f16..955e6aa 100644 --- a/fe/src/App.tsx +++ b/fe/src/App.tsx @@ -1,5 +1,5 @@ import { Theme } from "@radix-ui/themes"; -import { type FC, lazy, useEffect, useState } from "react"; +import { type FC, useEffect, useState } from "react"; import { createBrowserRouter, Link, @@ -9,22 +9,6 @@ import { import Welcome from "./components/Box/sub-pages/Welcome"; import { MainLayout } from "./components/layout/MainLayout"; -const LazyUnlockBox = lazy( - () => import("./components/Box/sub-pages/RestoreBoxes/LockedBox"), -); -const LazyLockBox = lazy(() => - import("./components/Box/sub-pages/CreationBoxes").then((mod) => ({ - default: mod.RootLockBox, - })), -); -const LazyComponentsDemo = lazy(() => import("./components/ComponentsDemo")); -const LazyCryptoPlayground = lazy( - () => import("./components/CryptoPlayground"), -); -const LazyDecodePlayground = lazy( - () => import("./components/DecodePlayground"), -); - function useSystemTheme() { const [theme, setTheme] = useState<"light" | "dark">("light"); @@ -83,23 +67,40 @@ const router = createBrowserRouter([ children: [ { path: "/crypto-poc", - Component: LazyCryptoPlayground, + async lazy() { + const mod = await import("./components/CryptoPlayground"); + return { Component: mod.default }; + }, }, { path: "/components-demo", - Component: LazyComponentsDemo, + async lazy() { + const mod = await import("./components/ComponentsDemo"); + return { Component: mod.default }; + }, }, { path: "/decode-poc", - Component: LazyDecodePlayground, + async lazy() { + const mod = await import("./components/DecodePlayground"); + return { Component: mod.default }; + }, }, { path: "/unlock-box/:roomToken?", - Component: LazyUnlockBox, + async lazy() { + const mod = await import( + "./components/Box/sub-pages/RestoreBoxes/LockedBox" + ); + return { Component: mod.default }; + }, }, { path: "/lock-box/:roomToken?", - Component: LazyLockBox, + async lazy() { + const mod = await import("./components/Box/sub-pages/CreationBoxes"); + return { Component: mod.RootLockBox }; + }, }, { path: "/", diff --git a/fe/src/components/Box/sub-pages/Welcome.tsx b/fe/src/components/Box/sub-pages/Welcome.tsx index 01f8a98..59b6036 100644 --- a/fe/src/components/Box/sub-pages/Welcome.tsx +++ b/fe/src/components/Box/sub-pages/Welcome.tsx @@ -1,6 +1,6 @@ import type React from "react"; import type { ReactNode } from "react"; -import { Link } from "react-router-dom"; +import { NavLink } from "react-router-dom"; import { Button } from "@/ui/Button"; import { Typography } from "@/ui/Typography"; @@ -16,31 +16,37 @@ const Welcome: React.FC = () => { <>

Start by creating a box. You’ll:

} buttonSlot={ - - - + + {({ isPending, isTransitioning }) => ( + + )} + } /> {

} buttonSlot={ - - - + + {({ isPending, isTransitioning }) => ( + + )} + } /> diff --git a/libs/protocols/src/peers-message-exchange-protocol.ts b/libs/protocols/src/peers-message-exchange-protocol.ts index 2b62432..6c44a65 100644 --- a/libs/protocols/src/peers-message-exchange-protocol.ts +++ b/libs/protocols/src/peers-message-exchange-protocol.ts @@ -60,7 +60,7 @@ export class PeerMessageExchangeProtocol< this.libp2p = libp2p; - if (skipHandleInitialization) { + if (skipHandleInitialization === false) { registerProtoHandle(this.protocolId, libp2p, (message, peerId) => { const jsonMessage = parseJsonSafely(message); if (!jsonMessage) return; From 0092694dcafc29a2f5bd99fd174c8191b5823dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Pi=C4=85tkowski?= Date: Tue, 2 Dec 2025 11:44:46 +0100 Subject: [PATCH 2/2] fix: stop gossip score to disable peer discovery --- be/src/debug-utils/debugPrintGossipScore.ts | 22 +++ be/src/debug-utils/debugPubSubMessages.ts | 22 +++ be/src/start-libp2p-relay.ts | 22 +++ .../libp2p/core/peer-connection-handler.ts | 18 +++ .../libp2p/core/webrtc-libp2p-service.ts | 15 +++ fe/src/services/libp2p/useLibp2p/useLibp2p.ts | 61 +++------ .../libp2p/useLibp2p/utils/debug-utils.ts | 127 ++++++++++++++++++ fe/vite.config.ts | 2 +- 8 files changed, 244 insertions(+), 45 deletions(-) create mode 100644 be/src/debug-utils/debugPrintGossipScore.ts create mode 100644 be/src/debug-utils/debugPubSubMessages.ts create mode 100644 fe/src/services/libp2p/useLibp2p/utils/debug-utils.ts diff --git a/be/src/debug-utils/debugPrintGossipScore.ts b/be/src/debug-utils/debugPrintGossipScore.ts new file mode 100644 index 0000000..902033e --- /dev/null +++ b/be/src/debug-utils/debugPrintGossipScore.ts @@ -0,0 +1,22 @@ +import type { GossipSub } from "@chainsafe/libp2p-gossipsub"; +import { shortenPeerId } from "@icod2/protocols"; +import type { Libp2p } from "@libp2p/interface"; +import type { Logger } from "src/logger.js"; + +export const debugPrintGossipScore = (libp2p: Libp2p, logger: Logger) => { + const gs = libp2p.services.pubsub as GossipSub; + + const scores = Array.from(gs.peers.keys()).map((id: string) => ({ + id: shortenPeerId(id), + score: gs.score.score(id), + })); + + const mesh = Array.from(gs.mesh.entries()).map( + ([topic, peers]: [string, Set]) => ({ + topic, + peers: Array.from(peers).map((p) => shortenPeerId(p)), + }), + ); + + logger.info({ scores, mesh }, "Gossipsub scores/mesh snapshot"); +}; diff --git a/be/src/debug-utils/debugPubSubMessages.ts b/be/src/debug-utils/debugPubSubMessages.ts new file mode 100644 index 0000000..e4e3f42 --- /dev/null +++ b/be/src/debug-utils/debugPubSubMessages.ts @@ -0,0 +1,22 @@ +import { shortenPeerId } from "@icod2/protocols"; +import type { PubSub } from "@libp2p/interface"; +import type { Libp2p } from "libp2p"; +import type { Logger } from "src/logger.js"; + +export const debugPubSubMessages = ( + libp2p: Libp2p<{ pubsub: PubSub }>, + logger: Logger, +) => { + libp2p.services.pubsub.addEventListener("message", (e) => { + const msg = e.detail; + logger.info( + { + // @ts-expect-error + from: shortenPeerId(msg.from?.toString()), + topic: msg.topic, + dataLength: msg.data?.length ?? 0, + }, + "Pubsub message on roomToken (might be discovery or app msg)", + ); + }); +}; diff --git a/be/src/start-libp2p-relay.ts b/be/src/start-libp2p-relay.ts index 2d453c2..5fd3feb 100644 --- a/be/src/start-libp2p-relay.ts +++ b/be/src/start-libp2p-relay.ts @@ -18,6 +18,8 @@ import type { Libp2p, PeerId, SubscriptionChangeData } from "@libp2p/interface"; import { tcp } from "@libp2p/tcp"; import { webSockets } from "@libp2p/websockets"; import { createLibp2p } from "libp2p"; +import { debugPrintGossipScore } from "./debug-utils/debugPrintGossipScore.js"; +import { debugPubSubMessages } from "./debug-utils/debugPubSubMessages.js"; import { getLogger } from "./logger.js"; import { starRoomRegistrationServiceStart } from "./services/room-registration.js"; import { debounce } from "./utils/debounce.js"; @@ -71,6 +73,22 @@ export async function startLibp2pRelay({ fallbackToFloodsub: true, floodPublish: true, doPX: true, + // Relax scoring so peers are not greylisted/pruned in small rooms + scoreParams: { + topics: {}, + topicScoreCap: 0, + appSpecificScore: () => 0, + appSpecificWeight: 0, + IPColocationFactorWeight: 0, + behaviourPenaltyWeight: 0, + }, + scoreThresholds: { + gossipThreshold: -1, + publishThreshold: -1, + graylistThreshold: -1, + acceptPXThreshold: 0, + opportunisticGraftThreshold: 0, + }, }) as (arg: GossipSubComponents) => GossipSub, }, }); @@ -233,6 +251,7 @@ export async function startLibp2pRelay({ const roomStats = getRoomStats(); logger.info({ roomStats }, "Subscription changed"); debouncedPrintInfoAboutPubSubTopics(); + debugPrintGossipScore(libp2p, logger); } const debouncedLogPeerUpdated = debounce(peerUpdated, 2000); @@ -250,6 +269,7 @@ export async function startLibp2pRelay({ function printInfoAboutPubSubTopics() { const topics = getTopics(); logger.info({ topics }, "All Topics"); + debugPrintGossipScore(libp2p, logger); } function peerUpdated(peerIdStr: string) { @@ -278,6 +298,8 @@ export async function startLibp2pRelay({ return acc; }, {}); } + + debugPubSubMessages(libp2p, logger); } function formatConnectedPeers(peers: Set): string[] { diff --git a/fe/src/services/libp2p/core/peer-connection-handler.ts b/fe/src/services/libp2p/core/peer-connection-handler.ts index 8fdabad..1941804 100644 --- a/fe/src/services/libp2p/core/peer-connection-handler.ts +++ b/fe/src/services/libp2p/core/peer-connection-handler.ts @@ -59,8 +59,26 @@ export const createPeerConnectionHandler = ({ } }); + libp2p.addEventListener("peer:update", (evt) => { + const { peer, previous } = evt.detail; + loggerGate.canLog && + console.log( + "[peer:update]", + shortenPeerId(peer.id.toString()), + "hadPrevious?", + previous != null, + "addresses:", + peer.addresses.map((a) => a.multiaddr.toString()), + ); + }); + // 👇 Dial peers discovered via pubsub libp2p.addEventListener("peer:discovery", async (evt) => { + loggerGate.canLog && + console.log( + "[peer:discovery] Peer discovered:", + shortenPeerId(evt.detail.id.toString()), + ); // Encapsulate the multiaddrs with the peer ID to ensure correct dialing // Should be fixed when https://github.com/libp2p/js-libp2p/issues/3239 is resolved. const discoveredPeerIdStr = evt.detail.id.toString(); diff --git a/fe/src/services/libp2p/core/webrtc-libp2p-service.ts b/fe/src/services/libp2p/core/webrtc-libp2p-service.ts index 57bbad7..2a5e291 100644 --- a/fe/src/services/libp2p/core/webrtc-libp2p-service.ts +++ b/fe/src/services/libp2p/core/webrtc-libp2p-service.ts @@ -86,6 +86,21 @@ async function createLibp2pService({ fallbackToFloodsub: true, floodPublish: true, doPX: true, // Enable peer exchange + scoreParams: { + topics: {}, + topicScoreCap: 0, + appSpecificScore: () => 0, + appSpecificWeight: 0, + IPColocationFactorWeight: 0, + behaviourPenaltyWeight: 0, + }, + scoreThresholds: { + gossipThreshold: -1, + publishThreshold: -1, + graylistThreshold: -1, + acceptPXThreshold: 0, + opportunisticGraftThreshold: 0, + }, }), identify: identify(), ping: ping({ diff --git a/fe/src/services/libp2p/useLibp2p/useLibp2p.ts b/fe/src/services/libp2p/useLibp2p/useLibp2p.ts index 5bfa325..cbbbbf7 100644 --- a/fe/src/services/libp2p/useLibp2p/useLibp2p.ts +++ b/fe/src/services/libp2p/useLibp2p/useLibp2p.ts @@ -15,6 +15,14 @@ import { RelayReconnectDialer } from "../core/relay-reconnect-dialer"; import { startLibp2pService } from "../core/webrtc-libp2p-service"; import type { RoomTokenProvider } from "..//types"; import { useConnectedRelayState } from "./useConnectedRelayState"; +import { + addDebugDiscoveryState, + addDebugLogDiscoveryMessages, + addDebugPrintConnections, + addDebugTriggerRediscovery, + addManuallyBroadcastMessage, + cleanDebugUtils, +} from "./utils/debug-utils"; export type Libp2pServiceErrors = | "RoomTokenProviderError" @@ -115,48 +123,6 @@ export const useLibp2p = ({ onLibp2pStartedRef.current?.(libp2pService); - // @ts-expect-error - window.debugPrintConnections = () => { - const peers = libp2pService.getPeers(); - loggerGate.canLog && console.log("Peers:", peers); - const connections = libp2pService.getConnections(); - const connectionAddrsStats = connections.reduce( - (acc, connection) => { - const { remotePeer } = connection; - const peerIdStr = remotePeer.toString(); - const peerObj = acc[peerIdStr] || []; - acc[peerIdStr] = peerObj; - - peerObj.push({ - multiPlexer: connection.multiplexer ?? "unknown", - multiaddr: connection.remoteAddr.toString(), - status: connection.status, - streamsCount: connection.streams.length, - }); - - return acc; - }, - {} as Record< - string, - { - multiPlexer: string; - multiaddr: string; - status: string; - streamsCount: number; - }[] - >, - ); - loggerGate.canLog && console.log("Connections:", connectionAddrsStats); - }; - - // @ts-expect-error - window.debugTriggerRediscovery = async () => { - // Temporarily unsubscribe and resubscribe - libp2pService.services.pubsub.unsubscribe(roomToken); - await new Promise((resolve) => setTimeout(resolve, 1000)); - libp2pService.services.pubsub.subscribe(roomToken); - }; - libp2pServiceRef.current = libp2pService; const persistingDialer = new PersistingDialer(libp2pService); const relayReconnectDialer = new RelayReconnectDialer(libp2pService, { @@ -195,13 +161,20 @@ export const useLibp2p = ({ } peerConnectionHandler(libp2pService); + + addDebugDiscoveryState(libp2pService, roomToken); + addDebugLogDiscoveryMessages(libp2pService, roomToken); + addDebugPrintConnections(libp2pService); + addDebugTriggerRediscovery(libp2pService, roomToken); + addManuallyBroadcastMessage(libp2pService, roomToken); + // @ts-expect-error + window.debugLogDiscoveryMessages(); })(); return () => { unmounted = true; - // @ts-expect-error - window.debugPrintConnections = undefined; + cleanDebugUtils(); libp2pService?.removeEventListener("start", handleLibp2pStarted); diff --git a/fe/src/services/libp2p/useLibp2p/utils/debug-utils.ts b/fe/src/services/libp2p/useLibp2p/utils/debug-utils.ts new file mode 100644 index 0000000..938b6fd --- /dev/null +++ b/fe/src/services/libp2p/useLibp2p/utils/debug-utils.ts @@ -0,0 +1,127 @@ +import { loggerGate, shortenPeerId } from "@icod2/protocols"; +import type { Libp2p, PubSub } from "@libp2p/interface"; + +export const addDebugDiscoveryState = ( + libp2p: Libp2p<{ pubsub: PubSub }>, + roomToken: string, +) => { + // @ts-expect-error + window.debugDiscoveryState = () => { + const topics = libp2p.services.pubsub.getTopics(); + const subs = libp2p.services.pubsub + .getSubscribers(roomToken) + .map((p) => p.toString()); + + console.log("pubsub topics:", topics); + console.log("discovery subscribers for room:", subs); + }; +}; + +export const addDebugLogDiscoveryMessages = ( + libp2p: Libp2p<{ pubsub: PubSub }>, + roomToken: string, +) => { + console.log("addDebugLogDiscoveryMessages()"); + // @ts-expect-error + window.debugLogDiscoveryMessages = () => { + libp2p.services.pubsub.addEventListener("message", (evt) => { + const msg = evt.detail; + if (msg.topic !== roomToken) return; + console.log( + "[DISCOVERY?] pubsub message on roomToken", + msg.topic, + "from", + //@ts-expect-error + shortenPeerId(msg.from?.toString()), + "bytes", + msg.data?.length, + ); + }); + }; +}; + +export const addDebugPrintConnections = (libp2p: Libp2p) => { + // @ts-expect-error + window.debugPrintConnections = () => { + const peers = libp2p.getPeers(); + loggerGate.canLog && console.log("Peers:", peers); + const connections = libp2p.getConnections(); + const connectionAddrsStats = connections.reduce( + (acc, connection) => { + const { remotePeer } = connection; + const peerIdStr = remotePeer.toString(); + const peerObj = acc[peerIdStr] || []; + acc[peerIdStr] = peerObj; + + peerObj.push({ + multiPlexer: connection.multiplexer ?? "unknown", + multiaddr: connection.remoteAddr.toString(), + status: connection.status, + streamsCount: connection.streams.length, + }); + + return acc; + }, + {} as Record< + string, + { + multiPlexer: string; + multiaddr: string; + status: string; + streamsCount: number; + }[] + >, + ); + loggerGate.canLog && console.log("Connections:", connectionAddrsStats); + }; +}; + +export const addDebugTriggerRediscovery = ( + libp2p: Libp2p<{ pubsub: PubSub }>, + roomToken: string, +) => { + // @ts-expect-error + window.debugTriggerRediscovery = async () => { + // Temporarily unsubscribe and resubscribe + libp2p.services.pubsub.unsubscribe(roomToken); + await new Promise((resolve) => setTimeout(resolve, 1000)); + libp2p.services.pubsub.subscribe(roomToken); + }; +}; + +export const addDebugPeerStore = (libp2p: Libp2p) => { + // @ts-expect-error + window.debugPrintPeerStore = async () => { + const peers = await libp2p.peerStore.all(); + loggerGate.canLog && console.log("Peers:", peers); + }; +}; + +export const addManuallyBroadcastMessage = ( + libp2p: Libp2p<{ pubsub: PubSub }>, + roomToken: string, +) => { + // @ts-expect-error + window.manuallyBroadcastMessage = async (message?: string) => { + const enc = new TextEncoder(); + await libp2p.services.pubsub.publish( + roomToken, + enc.encode(message ?? "test pubsub"), + ); + }; +}; + +export const cleanDebugUtils = () => { + /* @ts-expect-error */ + delete window.addDebugDiscoveryState; + /* @ts-expect-error */ + delete window.addDebugLogDiscoveryMessages; + /* @ts-expect-error */ + delete window.debugPrintConnections; + /* @ts-expect-error */ + delete window.debugTriggerRediscovery; + /* @ts-expect-error */ + delete window.debugPrintPeerStore; + /* @ts-expect-error */ + delete window.addManuallyBroadcastMessage; +}; diff --git a/fe/vite.config.ts b/fe/vite.config.ts index 79887ed..a82a81f 100644 --- a/fe/vite.config.ts +++ b/fe/vite.config.ts @@ -16,7 +16,7 @@ const __dirname = fileURLToPath(new URL(".", import.meta.url)); export default defineConfig(() => { return { server: { - allowedHosts: ["marcus-publication-declared-guardian.trycloudflare.com"], + allowedHosts: true, }, plugins: [ react(),