Checkpoint
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { bytesToHex } from '@noble/hashes/utils.js';
|
||||
|
||||
import { Badge, Card, Stack } from 'react-bootstrap';
|
||||
import { Card, Stack } from 'react-bootstrap';
|
||||
|
||||
import PacketDissectionViewer from '../../components/PacketDissectionViewer';
|
||||
import type { Segment } from '../../protocols/dissection.types';
|
||||
import type { Segment } from '../../types/protocol/dissection.types';
|
||||
|
||||
import type { MeshCorePacketRecord } from './MeshCoreContext';
|
||||
import {
|
||||
payloadNameByValue,
|
||||
routeDisplayByValue,
|
||||
} from './MeshCoreData';
|
||||
import { PayloadType, type AdvertPayload, type AnonReqPayload, type DecryptedGroupMessage, type GroupTextPayload, type Payload } from '../../types/protocol/meshcore.types';
|
||||
import type { Packet } from '../../protocols/meshcore';
|
||||
|
||||
const HeaderFact: React.FC<{ label: string; value: React.ReactNode }> = ({ label, value }) => (
|
||||
<div className="meshcore-fact-row">
|
||||
@@ -19,19 +20,9 @@ const HeaderFact: React.FC<{ label: string; value: React.ReactNode }> = ({ label
|
||||
</div>
|
||||
);
|
||||
|
||||
const bitSlice = (value: number, msb: number, lsb: number): number => {
|
||||
const width = msb - lsb + 1;
|
||||
const mask = (1 << width) - 1;
|
||||
return (value >> lsb) & mask;
|
||||
};
|
||||
|
||||
const buildMeshCoreSegments = (packet: MeshCorePacketRecord): Segment[] => {
|
||||
const pathField = packet.raw.length > 1 ? packet.raw[1] : 0;
|
||||
const pathHashSize = bitSlice(pathField, 7, 6) + 1;
|
||||
const pathHashCount = bitSlice(pathField, 5, 0);
|
||||
const pathBytesExpected = pathHashCount === 0 || pathHashSize === 4 ? 0 : pathHashCount * pathHashSize;
|
||||
const pathBytesAvailable = Math.min(pathBytesExpected, Math.max(packet.raw.length - 2, 0));
|
||||
const payloadOffset = 2 + pathBytesAvailable;
|
||||
const buildMeshCoreSegments = (packet: Packet): Segment[] => {
|
||||
const { pathHashSize, pathHashCount } = packet;
|
||||
const pathLength = pathHashSize * pathHashCount;
|
||||
|
||||
const segments: Segment[] = [
|
||||
{
|
||||
@@ -75,14 +66,14 @@ const buildMeshCoreSegments = (packet: MeshCorePacketRecord): Segment[] => {
|
||||
},
|
||||
];
|
||||
|
||||
if (pathBytesAvailable > 0) {
|
||||
if (pathLength > 0) {
|
||||
segments.push({
|
||||
name: 'Path Data',
|
||||
offset: 2,
|
||||
byteCount: pathBytesAvailable,
|
||||
byteCount: pathLength,
|
||||
attributes: [
|
||||
{
|
||||
byteWidth: pathBytesAvailable,
|
||||
byteWidth: pathLength,
|
||||
type: 'bytes',
|
||||
name: `Path Hashes (${pathHashCount} × ${pathHashSize} bytes)`,
|
||||
},
|
||||
@@ -90,22 +81,26 @@ const buildMeshCoreSegments = (packet: MeshCorePacketRecord): Segment[] => {
|
||||
});
|
||||
}
|
||||
|
||||
const payloadLength = packet.raw.length - payloadOffset;
|
||||
if (payloadLength > 0) {
|
||||
const payloadOffset = 2 + (packet.transportCodes ? packet.transportCodes.length * 2 : 0) + pathLength;
|
||||
if (packet.payload.length > 0) {
|
||||
segments.push({
|
||||
name: 'Payload',
|
||||
offset: payloadOffset,
|
||||
byteCount: payloadLength,
|
||||
byteCount: packet.payload.length,
|
||||
attributes: [
|
||||
{
|
||||
byteWidth: payloadLength,
|
||||
byteWidth: packet.payload.length,
|
||||
type: 'bytes',
|
||||
name: `Payload Data (${payloadLength} bytes)`,
|
||||
name: `Payload Data (${packet.payload.length} bytes)`,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (packet.segments) {
|
||||
segments.push(...packet.segments);
|
||||
}
|
||||
|
||||
return segments;
|
||||
};
|
||||
|
||||
@@ -116,24 +111,24 @@ const asRecord = (value: unknown): Record<string, unknown> | null => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const PayloadDetails: React.FC<{ packet: MeshCorePacketRecord }> = ({ packet }) => {
|
||||
const payload = packet.decodedPayload;
|
||||
const PayloadDetails: React.FC<{ packet: Packet }> = ({ packet }) => {
|
||||
const payload = packet.decodedPayload as Payload | undefined;
|
||||
const payloadObj = asRecord(payload);
|
||||
|
||||
if (!payloadObj) {
|
||||
if (typeof payload === 'undefined' || !payload || !payloadObj) {
|
||||
return (
|
||||
<Card body className="data-table-card">
|
||||
<h6 className="mb-2">Payload</h6>
|
||||
<div>Unable to decode payload; showing raw bytes only.</div>
|
||||
<code>{bytesToHex(packet.raw)}</code>
|
||||
<code>{bytesToHex(packet.payload)}</code>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof payloadObj.flags === 'number' && payloadObj.data instanceof Uint8Array) {
|
||||
if (typeof payloadObj?.flags === 'number' && payloadObj?.data instanceof Uint8Array) {
|
||||
return (
|
||||
<Card body className="data-table-card">
|
||||
<h6 className="mb-2">CONTROL Payload</h6>
|
||||
<h6 className="mb-2">Control</h6>
|
||||
<HeaderFact label="Flags" value={`0x${payloadObj.flags.toString(16)}`} />
|
||||
<HeaderFact label="Data Length" value={payloadObj.data.length} />
|
||||
<HeaderFact label="Data" value={<code>{bytesToHex(payloadObj.data)}</code>} />
|
||||
@@ -141,6 +136,18 @@ const PayloadDetails: React.FC<{ packet: MeshCorePacketRecord }> = ({ packet })
|
||||
);
|
||||
}
|
||||
|
||||
if (packet.payloadType === PayloadType.GROUP_TEXT && typeof packet.decrypted !== 'undefined') {
|
||||
const payload = packet.decodedPayload as GroupTextPayload;
|
||||
const decrypted = packet.decrypted as DecryptedGroupMessage;
|
||||
return (
|
||||
<Card body className="data-table-card">
|
||||
<h6 className="mb-2">{payloadNameByValue[packet.payloadType] ?? packet.payloadType} (Encrypted)</h6>
|
||||
<HeaderFact label="Channel Hash" value={<><span className="meshcore-hash">{payload.channelHash}</span>{decrypted.group ? ` (${decrypted.group})` : ''}</>} />
|
||||
<HeaderFact label="Timestamp" value={decrypted.timestamp.toLocaleString()} />
|
||||
<HeaderFact label="Message" value={decrypted.message} />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
if (
|
||||
typeof payloadObj.channelHash === 'string'
|
||||
&& payloadObj.cipherText instanceof Uint8Array
|
||||
@@ -148,9 +155,9 @@ const PayloadDetails: React.FC<{ packet: MeshCorePacketRecord }> = ({ packet })
|
||||
) {
|
||||
return (
|
||||
<Card body className="data-table-card">
|
||||
<h6 className="mb-2">GROUP Payload</h6>
|
||||
<h6 className="mb-2">{payloadNameByValue[packet.payloadType] ?? packet.payloadType} (Encrypted)</h6>
|
||||
<HeaderFact label="Channel Hash" value={payloadObj.channelHash} />
|
||||
<HeaderFact label="Cipher Text Length" value={payloadObj.cipherText.length} />
|
||||
<HeaderFact label="Cipher Text" value={payloadObj.cipherText.length + ' bytes'} />
|
||||
<HeaderFact label="Cipher MAC" value={<code>{bytesToHex(payloadObj.cipherMAC)}</code>} />
|
||||
</Card>
|
||||
);
|
||||
@@ -164,9 +171,9 @@ const PayloadDetails: React.FC<{ packet: MeshCorePacketRecord }> = ({ packet })
|
||||
) {
|
||||
return (
|
||||
<Card body className="data-table-card">
|
||||
<h6 className="mb-2">Encrypted Payload</h6>
|
||||
<HeaderFact label="Destination" value={payloadObj.dstHash} />
|
||||
<HeaderFact label="Source" value={payloadObj.srcHash} />
|
||||
<h6 className="mb-2">{payloadNameByValue[packet.payloadType] ?? packet.payloadType} (Encrypted)</h6>
|
||||
<HeaderFact label="Destination" value={<span className="meshcore-hash">{payloadObj.dstHash}</span>} />
|
||||
<HeaderFact label="Source" value={<span className="meshcore-hash">{payloadObj.srcHash}</span>} />
|
||||
<HeaderFact label="Cipher Text Length" value={payloadObj.cipherText.length} />
|
||||
<HeaderFact label="Cipher MAC" value={<code>{bytesToHex(payloadObj.cipherMAC)}</code>} />
|
||||
</Card>
|
||||
@@ -176,13 +183,47 @@ const PayloadDetails: React.FC<{ packet: MeshCorePacketRecord }> = ({ packet })
|
||||
if (payloadObj.data instanceof Uint8Array) {
|
||||
return (
|
||||
<Card body className="data-table-card">
|
||||
<h6 className="mb-2">Raw Payload</h6>
|
||||
<h6 className="mb-2">{payloadNameByValue[packet.payloadType] ?? packet.payloadType} (Raw)</h6>
|
||||
<HeaderFact label="Data Length" value={payloadObj.data.length} />
|
||||
<HeaderFact label="Data" value={<code>{bytesToHex(payloadObj.data)}</code>} />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (packet.payloadType === PayloadType.ADVERT) {
|
||||
const advert = payload as AdvertPayload;
|
||||
return (
|
||||
<Card body className="data-table-card">
|
||||
<h6 className="mb-2">Advert Payload</h6>
|
||||
<HeaderFact label="Public Key" value={<code>{bytesToHex(advert.publicKey)}</code>} />
|
||||
<HeaderFact label="Signature" value={advert.signature.length + ' Bytes'} />
|
||||
{advert.timestamp && (
|
||||
<HeaderFact label="Time" value={<code>{advert.timestamp.toISOString()}</code>} />
|
||||
)}
|
||||
{advert.appdata?.name && (
|
||||
<HeaderFact label="Name" value={advert.appdata.name} />
|
||||
)}
|
||||
{advert.appdata?.latitude && (
|
||||
<HeaderFact label="Latitude" value={advert.appdata.latitude} />
|
||||
)}
|
||||
{advert.appdata?.longitude && (
|
||||
<HeaderFact label="Longitude" value={advert.appdata.longitude} />
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (packet.payloadType === PayloadType.ANON_REQ) {
|
||||
const anonReq = payload as AnonReqPayload;
|
||||
return (
|
||||
<Card body className="data-table-card">
|
||||
<h6 className="mb-2">Anonymous Request</h6>
|
||||
<HeaderFact label="Destination" value={anonReq.dstHash} />
|
||||
<HeaderFact label="Public Key" value={<code>{bytesToHex(anonReq.publicKey)}</code>} />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card body className="data-table-card">
|
||||
<h6 className="mb-2">Payload</h6>
|
||||
@@ -192,7 +233,7 @@ const PayloadDetails: React.FC<{ packet: MeshCorePacketRecord }> = ({ packet })
|
||||
};
|
||||
|
||||
interface MeshCorePacketDetailsPaneProps {
|
||||
packet: MeshCorePacketRecord | null;
|
||||
packet: Packet | null;
|
||||
streamReady: boolean;
|
||||
}
|
||||
|
||||
@@ -209,6 +250,7 @@ const MeshCorePacketDetailsPane: React.FC<MeshCorePacketDetailsPaneProps> = ({ p
|
||||
|
||||
return (
|
||||
<Stack gap={2} className="h-100 meshcore-detail-stack">
|
||||
{/*
|
||||
<Card body className="data-table-card">
|
||||
<Stack direction="horizontal" gap={2} className="mb-2">
|
||||
<h6 className="mb-0">{payloadNameByValue[packet.payloadType] ?? packet.payloadType}</h6>
|
||||
@@ -225,14 +267,15 @@ const MeshCorePacketDetailsPane: React.FC<MeshCorePacketDetailsPaneProps> = ({ p
|
||||
<HeaderFact label="Path" value={<code>{bytesToHex(packet.path)}</code>} />
|
||||
<HeaderFact label="Raw Packet" value={<code>{bytesToHex(packet.raw)}</code>} />
|
||||
</Card>
|
||||
*/}
|
||||
<PayloadDetails packet={packet} />
|
||||
<Card body className="data-table-card">
|
||||
<PacketDissectionViewer
|
||||
rawPacket={packet.raw}
|
||||
segments={buildMeshCoreSegments(packet)}
|
||||
title="Packet Bytes (Wire View)"
|
||||
rawPacket={packet.toBytes()}
|
||||
segments={packet.segments || buildMeshCoreSegments(packet)}
|
||||
title={`${payloadNameByValue[packet.payloadType] ?? packet.payloadType} Packet Dissection`}
|
||||
/>
|
||||
</Card>
|
||||
<PayloadDetails packet={packet} />
|
||||
<Card body className="data-table-card">
|
||||
<h6 className="mb-2">Stream Preparation</h6>
|
||||
<div>MeshCore stream service is initialized and ready for topic subscriptions.</div>
|
||||
|
||||
Reference in New Issue
Block a user