mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 12:05:20 +00:00
Centralized relay manager (#182)
* noise: send nonce * broadcast manager * add missing file
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
package com.bitchat.android.mesh
|
||||
|
||||
import android.util.Log
|
||||
import com.bitchat.android.model.RoutedPacket
|
||||
import com.bitchat.android.protocol.BitchatPacket
|
||||
import com.bitchat.android.util.toHexString
|
||||
import kotlinx.coroutines.*
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Centralized packet relay management
|
||||
*
|
||||
* This class handles all relay decisions and logic for bitchat packets.
|
||||
* All packets that aren't specifically addressed to us get processed here.
|
||||
*/
|
||||
class PacketRelayManager(private val myPeerID: String) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PacketRelayManager"
|
||||
}
|
||||
|
||||
// Delegate for callbacks
|
||||
var delegate: PacketRelayManagerDelegate? = null
|
||||
|
||||
// Coroutines
|
||||
private val relayScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
/**
|
||||
* Main entry point for relay decisions
|
||||
* Only packets that aren't specifically addressed to us should be passed here
|
||||
*/
|
||||
suspend fun handlePacketRelay(routed: RoutedPacket) {
|
||||
val packet = routed.packet
|
||||
val peerID = routed.peerID ?: "unknown"
|
||||
|
||||
Log.d(TAG, "Evaluating relay for packet type ${packet.type} from $peerID (TTL: ${packet.ttl})")
|
||||
|
||||
// Double-check this packet isn't addressed to us
|
||||
if (isPacketAddressedToMe(packet)) {
|
||||
Log.d(TAG, "Packet addressed to us, skipping relay")
|
||||
return
|
||||
}
|
||||
|
||||
// Skip our own packets
|
||||
if (peerID == myPeerID) {
|
||||
Log.d(TAG, "Packet from ourselves, skipping relay")
|
||||
return
|
||||
}
|
||||
|
||||
// Check TTL and decrement
|
||||
if (packet.ttl == 0u.toUByte()) {
|
||||
Log.d(TAG, "TTL expired, not relaying packet")
|
||||
return
|
||||
}
|
||||
|
||||
// Decrement TTL by 1
|
||||
val relayPacket = packet.copy(ttl = (packet.ttl - 1u).toUByte())
|
||||
Log.d(TAG, "Decremented TTL from ${packet.ttl} to ${relayPacket.ttl}")
|
||||
|
||||
// Apply relay logic based on packet type
|
||||
val shouldRelay = shouldRelayPacket(relayPacket, peerID)
|
||||
|
||||
if (shouldRelay) {
|
||||
relayPacket(RoutedPacket(relayPacket, peerID, routed.relayAddress))
|
||||
} else {
|
||||
Log.d(TAG, "Relay decision: NOT relaying packet type ${packet.type}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a packet is specifically addressed to us
|
||||
*/
|
||||
private fun isPacketAddressedToMe(packet: BitchatPacket): Boolean {
|
||||
val recipientID = packet.recipientID
|
||||
|
||||
// No recipient means broadcast (not addressed to us specifically)
|
||||
if (recipientID == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if it's a broadcast recipient
|
||||
val broadcastRecipient = delegate?.getBroadcastRecipient()
|
||||
if (broadcastRecipient != null && recipientID.contentEquals(broadcastRecipient)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if recipient matches our peer ID
|
||||
val recipientIDString = recipientID.toHexString()
|
||||
return recipientIDString == myPeerID
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we should relay this packet based on type and network conditions
|
||||
*/
|
||||
private fun shouldRelayPacket(packet: BitchatPacket, fromPeerID: String): Boolean {
|
||||
// Always relay if TTL is high enough (indicates important message)
|
||||
if (packet.ttl >= 4u) {
|
||||
Log.d(TAG, "High TTL (${packet.ttl}), relaying")
|
||||
return true
|
||||
}
|
||||
|
||||
// Get network size for adaptive relay probability
|
||||
val networkSize = delegate?.getNetworkSize() ?: 1
|
||||
|
||||
// Small networks always relay to ensure connectivity
|
||||
if (networkSize <= 3) {
|
||||
Log.d(TAG, "Small network ($networkSize peers), relaying")
|
||||
return true
|
||||
}
|
||||
|
||||
// Apply adaptive relay probability based on network size
|
||||
val relayProb = when {
|
||||
networkSize <= 10 -> 1.0 // Always relay in small networks
|
||||
networkSize <= 30 -> 0.85 // High probability for medium networks
|
||||
networkSize <= 50 -> 0.7 // Moderate probability
|
||||
networkSize <= 100 -> 0.55 // Lower probability for large networks
|
||||
else -> 0.4 // Lowest probability for very large networks
|
||||
}
|
||||
|
||||
val shouldRelay = Random.nextDouble() < relayProb
|
||||
Log.d(TAG, "Network size: $networkSize, Relay probability: $relayProb, Decision: $shouldRelay")
|
||||
|
||||
return shouldRelay
|
||||
}
|
||||
|
||||
/**
|
||||
* Relay message with adaptive probability and timing (same as iOS)
|
||||
* Moved from MessageHandler.kt
|
||||
*/
|
||||
suspend fun relayMessage(routed: RoutedPacket) {
|
||||
val packet = routed.packet
|
||||
|
||||
if (packet.ttl == 0u.toUByte()) {
|
||||
Log.d(TAG, "TTL expired, not relaying message")
|
||||
return
|
||||
}
|
||||
|
||||
val relayPacket = packet.copy(ttl = (packet.ttl - 1u).toUByte())
|
||||
|
||||
// Check network size and apply adaptive relay probability
|
||||
val networkSize = delegate?.getNetworkSize() ?: 1
|
||||
val relayProb = when {
|
||||
networkSize <= 10 -> 1.0
|
||||
networkSize <= 30 -> 0.85
|
||||
networkSize <= 50 -> 0.7
|
||||
networkSize <= 100 -> 0.55
|
||||
else -> 0.4
|
||||
}
|
||||
|
||||
val shouldRelay = relayPacket.ttl >= 4u || networkSize <= 3 || Random.nextDouble() < relayProb
|
||||
|
||||
if (shouldRelay) {
|
||||
val delay = Random.nextLong(50, 500) // Random delay like iOS
|
||||
Log.d(TAG, "Relaying message after ${delay}ms delay")
|
||||
delay(delay)
|
||||
relayPacket(routed.copy(packet = relayPacket))
|
||||
} else {
|
||||
Log.d(TAG, "Relay decision: NOT relaying message (network size: $networkSize, prob: $relayProb)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually broadcast the packet for relay
|
||||
*/
|
||||
private fun relayPacket(routed: RoutedPacket) {
|
||||
Log.d(TAG, "🔄 Relaying packet type ${routed.packet.type} with TTL ${routed.packet.ttl}")
|
||||
delegate?.broadcastPacket(routed)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get debug information
|
||||
*/
|
||||
fun getDebugInfo(): String {
|
||||
return buildString {
|
||||
appendLine("=== Packet Relay Manager Debug Info ===")
|
||||
appendLine("Relay Scope Active: ${relayScope.isActive}")
|
||||
appendLine("My Peer ID: $myPeerID")
|
||||
appendLine("Network Size: ${delegate?.getNetworkSize() ?: "unknown"}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the relay manager
|
||||
*/
|
||||
fun shutdown() {
|
||||
Log.d(TAG, "Shutting down PacketRelayManager")
|
||||
relayScope.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate interface for packet relay manager callbacks
|
||||
*/
|
||||
interface PacketRelayManagerDelegate {
|
||||
// Network information
|
||||
fun getNetworkSize(): Int
|
||||
fun getBroadcastRecipient(): ByteArray
|
||||
|
||||
// Packet operations
|
||||
fun broadcastPacket(routed: RoutedPacket)
|
||||
}
|
||||
Reference in New Issue
Block a user