diff --git a/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager+AccountRestoreExtension.swift b/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager+AccountRestoreExtension.swift index 8b189534..557b2b38 100644 --- a/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager+AccountRestoreExtension.swift +++ b/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager+AccountRestoreExtension.swift @@ -983,6 +983,13 @@ extension SphinxOnionManager { restoredContactInfoTracker = [] } + // NOTE: This timer is independent of the Rust CHUNK_TIMEOUT_SECS buffer timeout. + // Extending CHUNK_TIMEOUT_SECS in sphinx-ffi/src/chunk.rs alone does not extend how + // long this client waits — the timer below still fires at kMessageFetchTimeout (30s) + // and clears all fetch callbacks regardless of the server-side buffer tolerance. + // To fully benefit from a longer Rust chunk timeout, kMessageFetchTimeout must also + // be scaled accordingly — that is a separate client-side change and an explicit + // fast-follow candidate. func startMessageFetchTimeoutTimer() { DispatchQueue.main.async { [weak self] in guard let self = self else { return } diff --git a/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager+HandleStateExtension.swift b/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager+HandleStateExtension.swift index 00a2dcdb..77103cac 100644 --- a/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager+HandleStateExtension.swift +++ b/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager+HandleStateExtension.swift @@ -168,6 +168,25 @@ extension SphinxOnionManager { return getMessageTag(messages: rr.msgs, isSendingMessage: isSendingMessage) } + // MARK: - state_mp Persistence Requirement + // + // Any call that returns a RunReturn — including all fetch/chunk-bearing calls — MUST + // flow through handleRunReturn() (or at minimum updateStateMap()) before discarding + // the result. If it does not, the state_mp delta is silently dropped: any in-flight + // chunk buffer associated with that call will restart from scratch on the next + // fragment, with no error surfaced to the user. + // + // Fetch functions and their persistence status (as of 2026-07): + // • fetch_msgs_batch → exercised end-to-end via fetchMessageBlock + // (SphinxOnionManager+AccountRestoreExtension.swift) + // • fetch_msgs_batch_per_contact → exercised end-to-end via fetchMessagePerContactBlock + // (SphinxOnionManager+AccountRestoreExtension.swift) + // • fetchMsgs → NO live call site in this app's business logic; + // persistence is only theoretically covered until a + // real caller is added and wired through handleRunReturn. + // • fetchMsgsBatchOkkey → NO live call site in this app's business logic; + // persistence is only theoretically covered until a + // real caller is added and wired through handleRunReturn. func updateStateMap(stateMap: Data?) { if let stateMap = stateMap { let _ = storeOnionState(inc: [UInt8](stateMap)) diff --git a/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager.swift b/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager.swift index 7979e573..d3216ada 100644 --- a/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager.swift +++ b/com.stakwork.sphinx.desktop/Crypter/SphinxOnionManager/SphinxOnionManager.swift @@ -38,6 +38,15 @@ class SphinxOnionManager : NSObject, @unchecked Sendable { static let kMqttKeepAlive: UInt16 = 15 static let kConnectionTimeoutInterval: TimeInterval = 15.0 + // Client-side fetch watchdog — independent of Rust CHUNK_TIMEOUT_SECS + // + // This timeout drives startMessageFetchTimeoutTimer(), which calls clearFetchCallbacks() + // and abandons an in-flight fetch/restore after 30 seconds. It is entirely independent + // of the Rust-side CHUNK_TIMEOUT_SECS buffer timeout in sphinx-ffi/src/chunk.rs. + // Increasing CHUNK_TIMEOUT_SECS on the server/FFI side (e.g. to 300s) does NOT make + // this client wait longer — this watchdog will still fire at 30s and clear callbacks, + // preventing the app from benefiting from the extended Rust buffer tolerance. + // Scaling kMessageFetchTimeout to match is a separate, out-of-scope follow-up change. static let kMessageFetchTimeout: TimeInterval = 30.0 var reconnectionTimer: Timer? = nil