package com.peachpasswords.sync /** * Who speaks first in a relay room. * * The relay is a two-party splice with no buffering and no "peer joined" * signal, so exactly one side must open with `PEACH_RECONNECT` and the other * must wait for it. Both sides derive the same room id from the shared pair * secret, so the role has to be derivable from state both already hold — * there is nothing on the wire to negotiate it with. * * Two rules, in order: * * 1. A browser extension can never hold a room open, so it always dials. * Toward an extension peer we therefore always wait. * 2. Otherwise — phone to phone, phone to tablet — the lower long-term public * key dials. Comparing the keys is symmetric by construction: each side * compares the same two values and reaches the opposite conclusion, so the * roles can never collide (both dialling, or both waiting, which is what * left phone-to-phone with nobody dialling). * * The keys are hex of the same length, so an ordinary lexicographic compare is * a total order over them. Ties are impossible: two devices sharing a static * key are the same device. */ internal fun weDialPeer(ourLongTermPublicKeyHex: String, peer: PairedDevice): Boolean { if (peer.deviceType == "extension") return false val theirs = peer.longTermPublicKeyHex ?: return false return ourLongTermPublicKeyHex.compareTo(theirs) < 0 }