mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 18:25:21 +00:00
gossip works
This commit is contained in:
@@ -707,6 +707,11 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
val gossip = com.bitchat.android.services.meshgraph.GossipTLV.encodeNeighbors(directPeers)
|
val gossip = com.bitchat.android.services.meshgraph.GossipTLV.encodeNeighbors(directPeers)
|
||||||
tlvPayload = tlvPayload + gossip
|
tlvPayload = tlvPayload + gossip
|
||||||
}
|
}
|
||||||
|
// Always update our own node in the mesh graph with the neighbor list we used
|
||||||
|
try {
|
||||||
|
com.bitchat.android.services.meshgraph.MeshGraphService.getInstance()
|
||||||
|
.updateFromAnnouncement(myPeerID, nickname, directPeers, System.currentTimeMillis().toULong())
|
||||||
|
} catch (_: Exception) { }
|
||||||
} catch (_: Exception) { }
|
} catch (_: Exception) { }
|
||||||
|
|
||||||
val announcePacket = BitchatPacket(
|
val announcePacket = BitchatPacket(
|
||||||
@@ -763,6 +768,11 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
val gossip = com.bitchat.android.services.meshgraph.GossipTLV.encodeNeighbors(directPeers)
|
val gossip = com.bitchat.android.services.meshgraph.GossipTLV.encodeNeighbors(directPeers)
|
||||||
tlvPayload = tlvPayload + gossip
|
tlvPayload = tlvPayload + gossip
|
||||||
}
|
}
|
||||||
|
// Always update our own node in the mesh graph with the neighbor list we used
|
||||||
|
try {
|
||||||
|
com.bitchat.android.services.meshgraph.MeshGraphService.getInstance()
|
||||||
|
.updateFromAnnouncement(myPeerID, nickname, directPeers, System.currentTimeMillis().toULong())
|
||||||
|
} catch (_: Exception) { }
|
||||||
} catch (_: Exception) { }
|
} catch (_: Exception) { }
|
||||||
|
|
||||||
val packet = BitchatPacket(
|
val packet = BitchatPacket(
|
||||||
|
|||||||
@@ -240,11 +240,11 @@ class MessageHandler(private val myPeerID: String) {
|
|||||||
previousPeerID = null
|
previousPeerID = null
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update mesh graph from gossip neighbors (if present)
|
// Update mesh graph from gossip neighbors (only if TLV present)
|
||||||
try {
|
try {
|
||||||
val neighbors = com.bitchat.android.services.meshgraph.GossipTLV.decodeNeighborsFromAnnouncementPayload(packet.payload)
|
val neighborsOrNull = com.bitchat.android.services.meshgraph.GossipTLV.decodeNeighborsFromAnnouncementPayload(packet.payload)
|
||||||
com.bitchat.android.services.meshgraph.MeshGraphService.getInstance()
|
com.bitchat.android.services.meshgraph.MeshGraphService.getInstance()
|
||||||
.updateFromAnnouncement(peerID, nickname, neighbors, packet.timestamp)
|
.updateFromAnnouncement(peerID, nickname, neighborsOrNull, packet.timestamp)
|
||||||
} catch (_: Exception) { }
|
} catch (_: Exception) { }
|
||||||
|
|
||||||
Log.d(TAG, "✅ Processed verified TLV announce: stored identity for $peerID")
|
Log.d(TAG, "✅ Processed verified TLV announce: stored identity for $peerID")
|
||||||
|
|||||||
@@ -24,9 +24,10 @@ object GossipTLV {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scan a TLV-encoded announce payload and extract neighbor peerIDs if present.
|
* Scan a TLV-encoded announce payload and extract neighbor peerIDs.
|
||||||
|
* Returns null if the TLV is not present at all; returns an empty list if present with length 0.
|
||||||
*/
|
*/
|
||||||
fun decodeNeighborsFromAnnouncementPayload(payload: ByteArray): List<String> {
|
fun decodeNeighborsFromAnnouncementPayload(payload: ByteArray): List<String>? {
|
||||||
val result = mutableListOf<String>()
|
val result = mutableListOf<String>()
|
||||||
var offset = 0
|
var offset = 0
|
||||||
while (offset + 2 <= payload.size) {
|
while (offset + 2 <= payload.size) {
|
||||||
@@ -45,10 +46,11 @@ object GossipTLV {
|
|||||||
result.add(bytesToPeerIdHex(idBytes))
|
result.add(bytesToPeerIdHex(idBytes))
|
||||||
pos += 8
|
pos += 8
|
||||||
}
|
}
|
||||||
break // only one neighbors TLV expected
|
return result // present (possibly empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
// Not present
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hexStringPeerIdTo8Bytes(hexString: String): ByteArray {
|
private fun hexStringPeerIdTo8Bytes(hexString: String): ByteArray {
|
||||||
@@ -73,4 +75,3 @@ object GossipTLV {
|
|||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,33 +28,40 @@ class MeshGraphService private constructor() {
|
|||||||
* Update graph from a verified announcement.
|
* Update graph from a verified announcement.
|
||||||
* Replaces previous neighbors for origin if this is newer (by timestamp).
|
* Replaces previous neighbors for origin if this is newer (by timestamp).
|
||||||
*/
|
*/
|
||||||
fun updateFromAnnouncement(originPeerID: String, originNickname: String?, neighbors: List<String>, timestamp: ULong) {
|
fun updateFromAnnouncement(originPeerID: String, originNickname: String?, neighborsOrNull: List<String>?, timestamp: ULong) {
|
||||||
// Newer-only replacement per origin
|
|
||||||
val prevTs = lastUpdate[originPeerID]
|
|
||||||
if (prevTs != null && prevTs >= timestamp) return
|
|
||||||
lastUpdate[originPeerID] = timestamp
|
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
// Update nickname
|
// Always update nickname if provided
|
||||||
if (originNickname != null) nicknames[originPeerID] = originNickname
|
if (originNickname != null) nicknames[originPeerID] = originNickname
|
||||||
|
|
||||||
|
// If no neighbors TLV present, do not modify edges or timestamps
|
||||||
|
if (neighborsOrNull == null) {
|
||||||
|
publishSnapshot()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Newer-only replacement per origin (based on TLV-bearing announcements only)
|
||||||
|
val prevTs = lastUpdate[originPeerID]
|
||||||
|
if (prevTs != null && prevTs >= timestamp) {
|
||||||
|
// Older or equal TLV-bearing update: ignore
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastUpdate[originPeerID] = timestamp
|
||||||
|
|
||||||
// Remove old symmetric edges contributed by this origin
|
// Remove old symmetric edges contributed by this origin
|
||||||
val prevNeighbors = adjacency[originPeerID]?.toSet().orEmpty()
|
val prevNeighbors = adjacency[originPeerID]?.toSet().orEmpty()
|
||||||
prevNeighbors.forEach { n ->
|
prevNeighbors.forEach { n ->
|
||||||
adjacency[n]?.remove(originPeerID)
|
adjacency[n]?.remove(originPeerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace origin's adjacency with new set
|
// Replace origin's adjacency with new set (may be empty)
|
||||||
val newSet = neighbors.distinct().take(10).filter { it != originPeerID }.toMutableSet()
|
val newSet = neighborsOrNull.distinct().take(10).filter { it != originPeerID }.toMutableSet()
|
||||||
adjacency[originPeerID] = newSet
|
adjacency[originPeerID] = newSet
|
||||||
// Ensure nodes exist for neighbors
|
// Ensure undirected edges
|
||||||
newSet.forEach { n ->
|
newSet.forEach { n ->
|
||||||
adjacency.putIfAbsent(n, mutableSetOf())
|
adjacency.putIfAbsent(n, mutableSetOf())
|
||||||
// Add symmetric edge for undirected graph visualization
|
|
||||||
adjacency[n]?.add(originPeerID)
|
adjacency[n]?.add(originPeerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute snapshot
|
|
||||||
publishSnapshot()
|
publishSnapshot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,4 +100,3 @@ class MeshGraphService private constructor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ import androidx.compose.ui.draw.rotate
|
|||||||
import com.bitchat.android.mesh.BluetoothMeshService
|
import com.bitchat.android.mesh.BluetoothMeshService
|
||||||
import com.bitchat.android.services.meshgraph.MeshGraphService
|
import com.bitchat.android.services.meshgraph.MeshGraphService
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||||
|
import androidx.compose.ui.graphics.nativeCanvas
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MeshTopologySection() {
|
fun MeshTopologySection() {
|
||||||
@@ -78,6 +81,22 @@ fun MeshTopologySection() {
|
|||||||
val pos = positions[node.peerID] ?: androidx.compose.ui.geometry.Offset(cx, cy)
|
val pos = positions[node.peerID] ?: androidx.compose.ui.geometry.Offset(cx, cy)
|
||||||
drawCircle(color = Color(0xFF00C851), radius = 10f, center = pos)
|
drawCircle(color = Color(0xFF00C851), radius = 10f, center = pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw labels near nodes (nickname or short ID)
|
||||||
|
val labelColor = colorScheme.onSurface.toArgb()
|
||||||
|
val textSizePx = 10.sp.toPx()
|
||||||
|
drawIntoCanvas { canvas ->
|
||||||
|
val paint = android.graphics.Paint().apply {
|
||||||
|
isAntiAlias = true
|
||||||
|
color = labelColor
|
||||||
|
textSize = textSizePx
|
||||||
|
}
|
||||||
|
nodes.forEach { node ->
|
||||||
|
val pos = positions[node.peerID] ?: androidx.compose.ui.geometry.Offset(cx, cy)
|
||||||
|
val label = (node.nickname?.takeIf { it.isNotBlank() } ?: node.peerID.take(8))
|
||||||
|
canvas.nativeCanvas.drawText(label, pos.x + 12f, pos.y - 12f, paint)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Label list for clarity under the canvas
|
// Label list for clarity under the canvas
|
||||||
@@ -157,11 +176,6 @@ fun DebugSettingsSheet(
|
|||||||
.padding(bottom = 24.dp),
|
.padding(bottom = 24.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
item {
|
|
||||||
// Mesh topology visualization
|
|
||||||
MeshTopologySection()
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
item {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
Icon(Icons.Filled.BugReport, contentDescription = null, tint = Color(0xFFFF9500))
|
Icon(Icons.Filled.BugReport, contentDescription = null, tint = Color(0xFFFF9500))
|
||||||
@@ -195,6 +209,11 @@ fun DebugSettingsSheet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mesh topology visualization (moved below verbose logging)
|
||||||
|
item {
|
||||||
|
MeshTopologySection()
|
||||||
|
}
|
||||||
|
|
||||||
// GATT controls
|
// GATT controls
|
||||||
item {
|
item {
|
||||||
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
|
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user