replaceForLastDeviceRecovery
Replaces the target's key by a last-device recovery (docs/last-device-recovery.md) in one atomic step, together with the consumption of its challenge (LastDeviceRecoveryRepository). No nonce is claimed: the server-issued challenge is the single-use value. In this order:
the target has no registration: LastDeviceRecoveryReplacementResult.NOT_REGISTERED;
the target's current key was installed by the last-device recovery with LastDeviceRecoveryReplacement.recoveryId: LastDeviceRecoveryReplacementResult.ALREADY_APPLIED, nothing written;
the target's user has no active recovery key (none registered, or revoked), or another one than LastDeviceRecoveryReplacement.expectedRecoveryPublicKey: LastDeviceRecoveryReplacementResult.NOT_CONFIGURED, nothing written;
the target has no stored challenge with this ID and nonce, or it was issued under another recovery key epoch than the current one (docs/recovery-key-lifecycle.md): LastDeviceRecoveryReplacementResult.CHALLENGE_INVALID; it has, but it expired before LastDeviceRecoveryReplacement.now: LastDeviceRecoveryReplacementResult.EXPIRED;
prunes every challenge that expired before
now;the target's key or epoch differ from the expected state or from the challenge's, or the replacement key is the current key: LastDeviceRecoveryReplacementResult.CONFLICT;
the epoch is Long.MAX_VALUE: LastDeviceRecoveryReplacementResult.EPOCH_EXHAUSTED;
deletes the challenge, stores the replacement key, epoch + 1, the last-device recovery ID and
nowas installation time, and clears the recovery and rotation IDs: LastDeviceRecoveryReplacementResult.REPLACED.
Every result except LastDeviceRecoveryReplacementResult.REPLACED leaves the registration and the challenge unchanged (pruning expired challenges may commit). The same compare-and-set as replaceForRecovery and replaceForRotation: of transitions from the same state, exactly one replaces the key. Nothing else changes: prekeys, mailboxes and nonces stay.