mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 16:05:19 +00:00
- Add version negotiation messages (0x20 versionHello, 0x21 versionAck) - Implement VersionHello and VersionAck message types with platform info - Add ProtocolVersion struct for version management and negotiation - Update BinaryProtocol to check supported versions - Add version negotiation to connection flow before Noise handshake - Maintain backward compatibility with legacy peers (assume v1) - Add comprehensive test suite with 40+ test cases - Update documentation with version negotiation details This ensures BitChat clients can negotiate protocol versions for smooth upgrades while maintaining full backward compatibility with existing clients.
230 lines
7.3 KiB
Swift
230 lines
7.3 KiB
Swift
//
|
|
// BinaryProtocolVersionTests.swift
|
|
// bitchatTests
|
|
//
|
|
// This is free and unencumbered software released into the public domain.
|
|
// For more information, see <https://unlicense.org>
|
|
//
|
|
|
|
import XCTest
|
|
@testable import bitchat
|
|
|
|
class BinaryProtocolVersionTests: XCTestCase {
|
|
|
|
// MARK: - Version Support Tests
|
|
|
|
func testCurrentVersionIsSupported() {
|
|
// Current version should always be supported
|
|
XCTAssertTrue(ProtocolVersion.isSupported(ProtocolVersion.current))
|
|
}
|
|
|
|
func testVersion1IsSupported() {
|
|
// Version 1 must be supported for backward compatibility
|
|
XCTAssertTrue(ProtocolVersion.isSupported(1))
|
|
}
|
|
|
|
func testUnsupportedVersionsRejected() {
|
|
// Test various unsupported versions
|
|
XCTAssertFalse(ProtocolVersion.isSupported(0))
|
|
XCTAssertFalse(ProtocolVersion.isSupported(2))
|
|
XCTAssertFalse(ProtocolVersion.isSupported(99))
|
|
XCTAssertFalse(ProtocolVersion.isSupported(255))
|
|
}
|
|
|
|
// MARK: - Binary Protocol Version Handling
|
|
|
|
func testBinaryProtocolRejectsUnsupportedVersion() {
|
|
// Create a packet with unsupported version
|
|
var data = Data()
|
|
|
|
// Header
|
|
data.append(99) // Unsupported version
|
|
data.append(MessageType.message.rawValue)
|
|
data.append(5) // TTL
|
|
|
|
// Timestamp (8 bytes)
|
|
let timestamp = UInt64(Date().timeIntervalSince1970 * 1000)
|
|
for i in (0..<8).reversed() {
|
|
data.append(UInt8((timestamp >> (i * 8)) & 0xFF))
|
|
}
|
|
|
|
// Flags (no recipient, no signature)
|
|
data.append(0)
|
|
|
|
// Payload length (2 bytes)
|
|
let payload = Data("test".utf8)
|
|
let payloadLength = UInt16(payload.count)
|
|
data.append(UInt8((payloadLength >> 8) & 0xFF))
|
|
data.append(UInt8(payloadLength & 0xFF))
|
|
|
|
// SenderID (8 bytes)
|
|
data.append(Data(repeating: 0x01, count: 8))
|
|
|
|
// Payload
|
|
data.append(payload)
|
|
|
|
// Try to decode - should fail due to unsupported version
|
|
let decoded = BinaryProtocol.decode(data)
|
|
XCTAssertNil(decoded, "Should reject packet with unsupported version")
|
|
}
|
|
|
|
func testBinaryProtocolAcceptsVersion1() {
|
|
// Create a valid version 1 packet
|
|
let packet = BitchatPacket(
|
|
type: MessageType.message.rawValue,
|
|
senderID: Data("sender12".utf8),
|
|
recipientID: nil,
|
|
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
|
payload: Data("Hello".utf8),
|
|
signature: nil,
|
|
ttl: 3
|
|
)
|
|
|
|
// Encode
|
|
guard let encoded = packet.toBinaryData() else {
|
|
XCTFail("Failed to encode version 1 packet")
|
|
return
|
|
}
|
|
|
|
// Decode
|
|
guard let decoded = BitchatPacket.from(encoded) else {
|
|
XCTFail("Failed to decode version 1 packet")
|
|
return
|
|
}
|
|
|
|
XCTAssertEqual(decoded.version, 1)
|
|
XCTAssertEqual(decoded.payload, Data("Hello".utf8))
|
|
}
|
|
|
|
// MARK: - Version Message Type Tests
|
|
|
|
func testVersionHelloMessageType() {
|
|
let hello = VersionHello(
|
|
clientVersion: "1.0.0",
|
|
platform: "iOS"
|
|
)
|
|
|
|
guard let helloData = hello.encode() else {
|
|
XCTFail("Failed to encode VersionHello")
|
|
return
|
|
}
|
|
|
|
let packet = BitchatPacket(
|
|
type: MessageType.versionHello.rawValue,
|
|
ttl: 1,
|
|
senderID: "testpeer",
|
|
payload: helloData
|
|
)
|
|
|
|
XCTAssertEqual(packet.type, MessageType.versionHello.rawValue)
|
|
XCTAssertEqual(MessageType.versionHello.description, "versionHello")
|
|
}
|
|
|
|
func testVersionAckMessageType() {
|
|
let ack = VersionAck(
|
|
agreedVersion: 1,
|
|
serverVersion: "1.0.0",
|
|
platform: "macOS"
|
|
)
|
|
|
|
guard let ackData = ack.encode() else {
|
|
XCTFail("Failed to encode VersionAck")
|
|
return
|
|
}
|
|
|
|
let packet = BitchatPacket(
|
|
type: MessageType.versionAck.rawValue,
|
|
ttl: 1,
|
|
senderID: "testpeer",
|
|
payload: ackData
|
|
)
|
|
|
|
XCTAssertEqual(packet.type, MessageType.versionAck.rawValue)
|
|
XCTAssertEqual(MessageType.versionAck.description, "versionAck")
|
|
}
|
|
|
|
// MARK: - Compression Compatibility Tests
|
|
|
|
func testCompressedPacketWithVersion() {
|
|
// Create a large payload that will trigger compression
|
|
let largeContent = String(repeating: "Hello World! ", count: 100)
|
|
let packet = BitchatPacket(
|
|
type: MessageType.message.rawValue,
|
|
senderID: Data("sender12".utf8),
|
|
recipientID: nil,
|
|
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
|
payload: Data(largeContent.utf8),
|
|
signature: nil,
|
|
ttl: 3
|
|
)
|
|
|
|
// Encode (should compress)
|
|
guard let encoded = packet.toBinaryData() else {
|
|
XCTFail("Failed to encode packet with compression")
|
|
return
|
|
}
|
|
|
|
// Decode
|
|
guard let decoded = BitchatPacket.from(encoded) else {
|
|
XCTFail("Failed to decode compressed packet")
|
|
return
|
|
}
|
|
|
|
// Verify version is preserved
|
|
XCTAssertEqual(decoded.version, 1)
|
|
XCTAssertEqual(decoded.payload, Data(largeContent.utf8))
|
|
}
|
|
|
|
// MARK: - Future Version Migration Tests
|
|
|
|
func testVersionSetConsistency() {
|
|
// Ensure version constants are consistent
|
|
XCTAssertTrue(ProtocolVersion.supportedVersions.contains(ProtocolVersion.current))
|
|
XCTAssertTrue(ProtocolVersion.supportedVersions.contains(ProtocolVersion.minimum))
|
|
XCTAssertGreaterThanOrEqual(ProtocolVersion.current, ProtocolVersion.minimum)
|
|
XCTAssertLessThanOrEqual(ProtocolVersion.current, ProtocolVersion.maximum)
|
|
}
|
|
|
|
func testVersionNegotiationAlwaysPicksHighest() {
|
|
// When multiple versions are supported, should pick highest
|
|
let clientVersions: [UInt8] = [1, 2, 3, 4, 5]
|
|
let serverVersions: [UInt8] = [3, 4, 5, 6, 7]
|
|
|
|
let agreed = ProtocolVersion.negotiateVersion(
|
|
clientVersions: clientVersions,
|
|
serverVersions: serverVersions
|
|
)
|
|
|
|
XCTAssertEqual(agreed, 5) // Highest common version
|
|
}
|
|
|
|
// MARK: - Packet Size Tests with Version Negotiation
|
|
|
|
func testVersionNegotiationPacketsAreSmall() {
|
|
// Version negotiation should use minimal bandwidth
|
|
let hello = VersionHello(
|
|
clientVersion: "1.0.0",
|
|
platform: "iOS"
|
|
)
|
|
|
|
guard let helloData = hello.encode() else {
|
|
XCTFail("Failed to encode hello")
|
|
return
|
|
}
|
|
|
|
let packet = BitchatPacket(
|
|
type: MessageType.versionHello.rawValue,
|
|
ttl: 1,
|
|
senderID: "12345678",
|
|
payload: helloData
|
|
)
|
|
|
|
guard let encoded = packet.toBinaryData() else {
|
|
XCTFail("Failed to encode packet")
|
|
return
|
|
}
|
|
|
|
// Version negotiation packets should be reasonably small
|
|
XCTAssertLessThan(encoded.count, 512, "Version negotiation packet too large")
|
|
}
|
|
} |