SyncPeerRole.kt
1package com.peachpasswords.sync23/**4 * Who speaks first in a relay room.5 *6 * The relay is a two-party splice with no buffering and no "peer joined"7 * signal, so exactly one side must open with `PEACH_RECONNECT` and the other8 * must wait for it. Both sides derive the same room id from the shared pair9 * secret, so the role has to be derivable from state both already hold —10 * there is nothing on the wire to negotiate it with.11 *12 * Two rules, in order:13 *14 * 1. A browser extension can never hold a room open, so it always dials.15 * Toward an extension peer we therefore always wait.16 * 2. Otherwise — phone to phone, phone to tablet — the lower long-term public17 * key dials. Comparing the keys is symmetric by construction: each side18 * compares the same two values and reaches the opposite conclusion, so the19 * roles can never collide (both dialling, or both waiting, which is what20 * left phone-to-phone with nobody dialling).21 *22 * The keys are hex of the same length, so an ordinary lexicographic compare is23 * a total order over them. Ties are impossible: two devices sharing a static24 * key are the same device.25 */26internal fun weDialPeer(ourLongTermPublicKeyHex: String, peer: PairedDevice): Boolean {27 if (peer.deviceType == "extension") return false28 val theirs = peer.longTermPublicKeyHex ?: return false29 return ourLongTermPublicKeyHex.compareTo(theirs) < 030}