From 07934093e649a8fabed131403eb4bcfbc0c3b944 Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Thu, 12 Mar 2026 14:30:26 -0700 Subject: [PATCH] Don't force-insert a node with unknown relationships just because they are the marked recipient of a DM. Closes #44. --- .../src/networkGraph/packetNetworkGraph.ts | 16 +------ frontend/src/test/packetNetworkGraph.test.ts | 47 +++++++++++++++++++ frontend/src/test/useVisualizerData3D.test.ts | 43 +++++++++++++++-- 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/frontend/src/networkGraph/packetNetworkGraph.ts b/frontend/src/networkGraph/packetNetworkGraph.ts index caa99fe..2a96264 100644 --- a/frontend/src/networkGraph/packetNetworkGraph.ts +++ b/frontend/src/networkGraph/packetNetworkGraph.ts @@ -593,20 +593,8 @@ export function buildCanonicalPathForPacket( if (parsed.payloadType === PayloadType.TextMessage && parsed.dstHash) { if (context.myPrefix && parsed.dstHash.toLowerCase() === context.myPrefix) { path.push('self'); - } else { - const nodeId = resolveNode( - state, - context, - { type: 'prefix', value: parsed.dstHash }, - false, - true, - activityAtMs - ); - if (nodeId) { - path.push(nodeId); - } else if (!isOutgoingDm) { - path.push('self'); - } + } else if (!isOutgoingDm && path.length > 0) { + path.push('self'); } } else if (path.length > 0) { path.push('self'); diff --git a/frontend/src/test/packetNetworkGraph.test.ts b/frontend/src/test/packetNetworkGraph.test.ts index 068a0e2..fd61827 100644 --- a/frontend/src/test/packetNetworkGraph.test.ts +++ b/frontend/src/test/packetNetworkGraph.test.ts @@ -181,6 +181,53 @@ describe('packetNetworkGraph', () => { expect(projection.links.get('565656565656->self')?.hasDirectObservation).toBe(true); }); + it('does not add a DM recipient node from destination metadata alone', () => { + const selfKey = 'ffffffffffff0000000000000000000000000000000000000000000000000000'; + const aliceKey = 'aaaaaaaaaaaa0000000000000000000000000000000000000000000000000000'; + const bobKey = 'bbbbbbbbbbbb0000000000000000000000000000000000000000000000000000'; + const repeaterKey = '5656565656560000000000000000000000000000000000000000000000000000'; + + packetFixtures.set('dm-third-party-no-dst-node', { + payloadType: PayloadType.TextMessage, + messageHash: 'dm-third-party-no-dst-node', + pathBytes: ['565656565656'], + srcHash: 'aaaaaaaaaaaa', + dstHash: 'bbbbbbbbbbbb', + advertPubkey: null, + groupTextSender: null, + anonRequestPubkey: null, + }); + + const state = createPacketNetworkState('Me'); + const context = buildPacketNetworkContext({ + contacts: [ + createContact(aliceKey, 'Alice'), + createContact(bobKey, 'Bob'), + createContact(repeaterKey, 'Relay', CONTACT_TYPE_REPEATER), + ], + config: createConfig(selfKey), + repeaterAdvertPaths: [], + splitAmbiguousByTraffic: false, + useAdvertPathHints: false, + }); + + const ingested = ingestPacketIntoPacketNetwork( + state, + context, + createPacket('dm-third-party-no-dst-node') + ); + + expect(ingested?.canonicalPath).toEqual(['aaaaaaaaaaaa', '565656565656', 'self']); + expect(state.nodes.has('bbbbbbbbbbbb')).toBe(false); + expect(snapshotNeighborIds(state)).toEqual( + new Map([ + ['565656565656', ['aaaaaaaaaaaa', 'self']], + ['aaaaaaaaaaaa', ['565656565656']], + ['self', ['565656565656']], + ]) + ); + }); + it('replays real advert packets through the semantic layer', () => { const state = createPacketNetworkState('Me'); const context = buildPacketNetworkContext({ diff --git a/frontend/src/test/useVisualizerData3D.test.ts b/frontend/src/test/useVisualizerData3D.test.ts index 5ac14cf..c27946d 100644 --- a/frontend/src/test/useVisualizerData3D.test.ts +++ b/frontend/src/test/useVisualizerData3D.test.ts @@ -200,7 +200,7 @@ describe('useVisualizerData3D', () => { expect(result.current.renderedNodeIds.has('?32')).toBe(false); }); - it('does not append self after a resolved outgoing DM destination', async () => { + it('does not place a resolved outgoing DM destination into topology', async () => { const selfKey = 'ffffffffffff0000000000000000000000000000000000000000000000000000'; const bobKey = 'bbbbbbbbbbbb0000000000000000000000000000000000000000000000000000'; const repeaterKey = '3232323232320000000000000000000000000000000000000000000000000000'; @@ -226,11 +226,48 @@ describe('useVisualizerData3D', () => { showAmbiguousPaths: true, }); - await waitFor(() => expect(result.current.links.size).toBe(2)); + await waitFor(() => expect(result.current.links.size).toBe(1)); expect(result.current.links.has(buildLinkKey('self', '323232323232'))).toBe(true); - expect(result.current.links.has(buildLinkKey('323232323232', 'bbbbbbbbbbbb'))).toBe(true); + expect(result.current.links.has(buildLinkKey('323232323232', 'bbbbbbbbbbbb'))).toBe(false); expect(result.current.links.has(buildLinkKey('self', 'bbbbbbbbbbbb'))).toBe(false); + expect(result.current.renderedNodeIds.has('bbbbbbbbbbbb')).toBe(false); + }); + + it('does not place a third-party DM destination into topology', async () => { + const selfKey = 'ffffffffffff0000000000000000000000000000000000000000000000000000'; + const aliceKey = 'aaaaaaaaaaaa0000000000000000000000000000000000000000000000000000'; + const bobKey = 'bbbbbbbbbbbb0000000000000000000000000000000000000000000000000000'; + const repeaterKey = '3232323232320000000000000000000000000000000000000000000000000000'; + + packetFixtures.set('dm-third-party-known-dst', { + payloadType: PayloadType.TextMessage, + messageHash: 'dm-third-party-known-dst', + pathBytes: ['323232323232'], + srcHash: 'aaaaaaaaaaaa', + dstHash: 'bbbbbbbbbbbb', + advertPubkey: null, + groupTextSender: null, + anonRequestPubkey: null, + }); + + const { result } = renderVisualizerData({ + packets: [createPacket('dm-third-party-known-dst')], + contacts: [ + createContact(aliceKey, 'Alice'), + createContact(bobKey, 'Bob'), + createContact(repeaterKey, 'Relay', CONTACT_TYPE_REPEATER), + ], + config: createConfig(selfKey), + showAmbiguousPaths: true, + }); + + await waitFor(() => expect(result.current.links.size).toBe(2)); + + expect(result.current.links.has(buildLinkKey('aaaaaaaaaaaa', '323232323232'))).toBe(true); + expect(result.current.links.has(buildLinkKey('323232323232', 'self'))).toBe(true); + expect(result.current.links.has(buildLinkKey('323232323232', 'bbbbbbbbbbbb'))).toBe(false); + expect(result.current.renderedNodeIds.has('bbbbbbbbbbbb')).toBe(false); }); it('collapses a high-confidence ambiguous repeater into its known sibling when both share the same next hop', async () => {