mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-26 06:45:20 +00:00
Add source-based routing support (#862)
* Add source-based routing support * include neighbors in ANNOUNCE --------- Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
This commit is contained in:
@@ -54,6 +54,86 @@ struct BinaryProtocolTests {
|
||||
#expect(decodedPacket.signature != nil)
|
||||
#expect(decodedPacket.signature == TestConstants.testSignature)
|
||||
}
|
||||
|
||||
@Test func packetWithRouteRoundTrip() throws {
|
||||
let route: [Data] = [
|
||||
try #require(Data(hexString: "0102030405060708")),
|
||||
try #require(Data(hexString: "1112131415161718")),
|
||||
try #require(Data(hexString: "2122232425262728"))
|
||||
]
|
||||
|
||||
var packet = BitchatPacket(
|
||||
type: 0x01,
|
||||
senderID: route[0],
|
||||
recipientID: route.last,
|
||||
timestamp: 1_720_000_000_000,
|
||||
payload: Data("route-test".utf8),
|
||||
signature: nil,
|
||||
ttl: 6
|
||||
)
|
||||
packet.route = route
|
||||
|
||||
let encoded = try #require(BinaryProtocol.encode(packet), "Failed to encode packet with route")
|
||||
let flagsByte = encoded[BinaryProtocol.Offsets.flags]
|
||||
#expect((flagsByte & BinaryProtocol.Flags.hasRoute) != 0)
|
||||
|
||||
let decoded = try #require(BinaryProtocol.decode(encoded), "Failed to decode packet with route")
|
||||
let decodedRoute = try #require(decoded.route)
|
||||
#expect(decodedRoute.count == route.count)
|
||||
for (expected, actual) in zip(route, decodedRoute) {
|
||||
#expect(actual == expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Test func packetWithRoutePadsShortHop() throws {
|
||||
let sender = try #require(Data(hexString: "0011223344556677"))
|
||||
let destination = try #require(Data(hexString: "8899aabbccddeeff"))
|
||||
let shortHop = Data([0xAA, 0xBB, 0xCC])
|
||||
|
||||
var packet = BitchatPacket(
|
||||
type: 0x02,
|
||||
senderID: sender,
|
||||
recipientID: destination,
|
||||
timestamp: 1_730_000_000_000,
|
||||
payload: Data("pad-test".utf8),
|
||||
signature: nil,
|
||||
ttl: 5
|
||||
)
|
||||
packet.route = [shortHop, destination]
|
||||
|
||||
let encoded = try #require(BinaryProtocol.encode(packet), "Failed to encode packet with short hop route")
|
||||
let decoded = try #require(BinaryProtocol.decode(encoded), "Failed to decode packet with short hop route")
|
||||
let decodedRoute = try #require(decoded.route)
|
||||
let firstHop = try #require(decodedRoute.first)
|
||||
#expect(firstHop.count == BinaryProtocol.senderIDSize)
|
||||
#expect(firstHop.prefix(shortHop.count) == shortHop)
|
||||
let paddingBytes = firstHop.suffix(firstHop.count - shortHop.count)
|
||||
#expect(paddingBytes.allSatisfy { $0 == 0 })
|
||||
}
|
||||
|
||||
@Test func packetWithRouteAndCompressedPayload() throws {
|
||||
let route: [Data] = [
|
||||
try #require(Data(hexString: "0101010101010101")),
|
||||
try #require(Data(hexString: "0202020202020202"))
|
||||
]
|
||||
let repeatedString = String(repeating: "compress-me", count: 150)
|
||||
var packet = BitchatPacket(
|
||||
type: 0x03,
|
||||
senderID: route[0],
|
||||
recipientID: route.last,
|
||||
timestamp: 1_740_000_000_000,
|
||||
payload: Data(repeatedString.utf8),
|
||||
signature: nil,
|
||||
ttl: 7
|
||||
)
|
||||
packet.route = route
|
||||
|
||||
let encoded = try #require(BinaryProtocol.encode(packet), "Failed to encode packet with route and compression")
|
||||
let decoded = try #require(BinaryProtocol.decode(encoded), "Failed to decode packet with route and compression")
|
||||
#expect(decoded.payload == Data(repeatedString.utf8))
|
||||
let decodedRoute = try #require(decoded.route)
|
||||
#expect(decodedRoute == route)
|
||||
}
|
||||
|
||||
// MARK: - Compression Tests
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// MeshTopologyTrackerTests.swift
|
||||
// bitchatTests
|
||||
//
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// For more information, see <https://unlicense.org>
|
||||
//
|
||||
|
||||
import Testing
|
||||
import Foundation
|
||||
@testable import bitchat
|
||||
|
||||
struct MeshTopologyTrackerTests {
|
||||
private func hex(_ value: String) throws -> Data {
|
||||
try #require(Data(hexString: value))
|
||||
}
|
||||
|
||||
@Test func directLinkProducesRoute() throws {
|
||||
let tracker = MeshTopologyTracker()
|
||||
let a = try hex("0102030405060708")
|
||||
let b = try hex("1112131415161718")
|
||||
|
||||
tracker.recordDirectLink(between: a, and: b)
|
||||
let route = try #require(tracker.computeRoute(from: a, to: b))
|
||||
#expect(route == [a, b])
|
||||
}
|
||||
|
||||
@Test func multiHopRouteComputation() throws {
|
||||
let tracker = MeshTopologyTracker()
|
||||
let a = try hex("0001020304050607")
|
||||
let b = try hex("1011121314151617")
|
||||
let c = try hex("2021222324252627")
|
||||
let d = try hex("3031323334353637")
|
||||
|
||||
tracker.recordDirectLink(between: a, and: b)
|
||||
tracker.recordDirectLink(between: b, and: c)
|
||||
tracker.recordDirectLink(between: c, and: d)
|
||||
|
||||
let route = try #require(tracker.computeRoute(from: a, to: d))
|
||||
#expect(route == [a, b, c, d])
|
||||
}
|
||||
|
||||
@Test func recordRouteAddsEdges() throws {
|
||||
let tracker = MeshTopologyTracker()
|
||||
var a = Data([0xAA, 0xBB, 0xCC])
|
||||
let b = try hex("4445464748494A4B")
|
||||
let c = try hex("5455565758595A5B")
|
||||
|
||||
tracker.recordRoute([a, b, c])
|
||||
|
||||
a.append(Data(repeating: 0, count: BinaryProtocol.senderIDSize - a.count))
|
||||
let route = try #require(tracker.computeRoute(from: a, to: c))
|
||||
#expect(route.first == a)
|
||||
#expect(route.last == c)
|
||||
}
|
||||
|
||||
@Test func removingDirectLinkBreaksRoute() throws {
|
||||
let tracker = MeshTopologyTracker()
|
||||
let a = try hex("0101010101010101")
|
||||
let b = try hex("0202020202020202")
|
||||
let c = try hex("0303030303030303")
|
||||
|
||||
tracker.recordDirectLink(between: a, and: b)
|
||||
tracker.recordDirectLink(between: b, and: c)
|
||||
let initialRoute = try #require(tracker.computeRoute(from: a, to: c))
|
||||
#expect(initialRoute == [a, b, c])
|
||||
|
||||
tracker.removeDirectLink(between: b, and: c)
|
||||
#expect(tracker.computeRoute(from: a, to: c) == nil)
|
||||
}
|
||||
|
||||
@Test func removingPeerClearsEdges() throws {
|
||||
let tracker = MeshTopologyTracker()
|
||||
let a = try hex("0F0E0D0C0B0A0908")
|
||||
let b = try hex("0A0B0C0D0E0F0001")
|
||||
let c = try hex("0011223344556677")
|
||||
|
||||
tracker.recordRoute([a, b, c])
|
||||
let initialRoute = try #require(tracker.computeRoute(from: a, to: c))
|
||||
#expect(initialRoute == [a, b, c])
|
||||
|
||||
tracker.removePeer(b)
|
||||
#expect(tracker.computeRoute(from: a, to: c) == nil)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -217,7 +217,27 @@ struct PeerIDTests {
|
||||
let short = peerID.toShort()
|
||||
#expect(short == peerID)
|
||||
}
|
||||
|
||||
|
||||
@Test func routingData_fromShortID() throws {
|
||||
let peerID = PeerID(str: hex16)
|
||||
let routing = try #require(peerID.routingData)
|
||||
#expect(routing.count == 8)
|
||||
#expect(routing == Data(hexString: hex16))
|
||||
}
|
||||
|
||||
@Test func routingData_fromNoiseKey() throws {
|
||||
let peerID = PeerID(str: hex64)
|
||||
let routing = try #require(peerID.routingData)
|
||||
let expectedShort = peerID.toShort()
|
||||
#expect(routing == Data(hexString: expectedShort.id))
|
||||
}
|
||||
|
||||
@Test func routingPeerRoundTrip() throws {
|
||||
let raw = try #require(Data(hexString: hex16))
|
||||
let peerID = try #require(PeerID(routingData: raw))
|
||||
#expect(peerID.routingData == raw)
|
||||
}
|
||||
|
||||
// MARK: - Codable
|
||||
|
||||
@Test func codable_emptyPrefix() throws {
|
||||
|
||||
Reference in New Issue
Block a user