send path uses the device directory, not the prekey route — every send burned bundle quota and popped OPKs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
cf3f43fdbf
commit
7727cfa432
@@ -378,7 +378,33 @@ class Conversations(
|
||||
}
|
||||
|
||||
private fun sendPlaintext(toUserId: Long, text: String): Sent {
|
||||
val bundles = relay.fetchKeys(toUserId)
|
||||
// 🔴 **The DIRECTORY, not the key route.** `/e2e/keys` pops a one-time
|
||||
// prekey per device and is metered per contact pair — calling it on
|
||||
// EVERY send drained both, and an hour of ordinary chatting ended in
|
||||
// "Too many attempts". The directory costs nothing; full bundles are
|
||||
// fetched only when some device has no session yet, which is the one
|
||||
// moment X3DH actually needs them.
|
||||
val directory = relay.listDevices(toUserId)
|
||||
val stubs = (0 until directory.length()).map { i ->
|
||||
val o = directory.getJSONObject(i)
|
||||
PeerBundle(
|
||||
deviceId = o.optString("device_id"),
|
||||
identityDH = runCatching { Wire.unb64(o.optString("ik_dh")) }
|
||||
.getOrDefault(ByteArray(0)),
|
||||
identitySigning = runCatching { Wire.unb64(o.optString("ik_sig")) }
|
||||
.getOrDefault(ByteArray(0)),
|
||||
signedPreKeyId = 0,
|
||||
signedPreKey = ByteArray(0),
|
||||
signedPreKeySignature = ByteArray(0),
|
||||
oneTimePreKeyId = 0,
|
||||
oneTimePreKey = null,
|
||||
proto = o.optInt("proto", 1),
|
||||
)
|
||||
}
|
||||
val needsEstablish = stubs.any {
|
||||
it.deviceId != identity.deviceId && existingSession(it.deviceId) == null
|
||||
}
|
||||
val bundles = if (needsEstablish) relay.fetchKeys(toUserId) else stubs
|
||||
guardIdentity(toUserId, bundles)
|
||||
val delivered = mutableListOf<String>()
|
||||
val failed = LinkedHashMap<String, String>()
|
||||
|
||||
Reference in New Issue
Block a user