fix: clear stale Arti Tor cache on each fresh init, retry on failure
The Arti data directory (<filesDir>/arti/) accumulates stale consensus and relay descriptors over time. When this cached data becomes invalid, TorClient::create_bootstrapped() either bootstraps with stale guards (leading to circuit failures) or fails outright. Since the native TorClient persists for the process lifetime, force-closing and reopening the app re-reads the same corrupted data. Only clearing all app data (which deletes the arti/ dir) would recover — matching the user report. - Clear arti/cache/ on each fresh initialization (new process) so Arti always downloads fresh consensus and relay descriptors - On initialize() failure, clear ALL arti data (state + cache) and retry once, recovering from corrupted state without requiring the user to manually clear app data https://claude.ai/code/session_01VFAypytKGzdmuoJAXrb72G
This commit is contained in:
@@ -54,6 +54,34 @@ class TorService(
|
||||
private val _status = MutableStateFlow<TorServiceStatus>(TorServiceStatus.Off)
|
||||
val status: StateFlow<TorServiceStatus> = _status.asStateFlow()
|
||||
|
||||
private fun artiDataDir() = File(context.filesDir, "arti")
|
||||
|
||||
/**
|
||||
* Clears the Arti cache directory (consensus, relay descriptors) while
|
||||
* preserving the state directory (guard selection). This forces a fresh
|
||||
* consensus download on the next bootstrap, preventing stale cached data
|
||||
* from causing circuit failures.
|
||||
*/
|
||||
private fun clearArtiCache() {
|
||||
val cacheDir = File(artiDataDir(), "cache")
|
||||
if (cacheDir.exists()) {
|
||||
cacheDir.deleteRecursively()
|
||||
Log.d("TorService") { "Cleared Arti cache directory" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all Arti persistent data (state + cache). Used as a last resort
|
||||
* when initialization fails, to recover from corrupted state.
|
||||
*/
|
||||
private fun clearAllArtiData() {
|
||||
val dataDir = artiDataDir()
|
||||
if (dataDir.exists()) {
|
||||
dataDir.deleteRecursively()
|
||||
Log.d("TorService") { "Cleared all Arti data" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the TorClient (once) and start the SOCKS proxy.
|
||||
* Must be called from a coroutine on [Dispatchers.IO].
|
||||
@@ -90,12 +118,21 @@ class TorService(
|
||||
}
|
||||
}
|
||||
|
||||
val dataDir = File(context.filesDir, "arti").absolutePath
|
||||
// Clear cached consensus/descriptors so Arti bootstraps with
|
||||
// fresh network data, preventing stale guards/circuits.
|
||||
clearArtiCache()
|
||||
|
||||
val dataDir = artiDataDir().absolutePath
|
||||
Log.d("TorService") { "Initializing Arti with data dir: $dataDir" }
|
||||
|
||||
val initResult = ArtiNative.initialize(dataDir)
|
||||
var initResult = ArtiNative.initialize(dataDir)
|
||||
if (initResult != 0) {
|
||||
Log.e("TorService") { "Failed to initialize Arti: error $initResult" }
|
||||
Log.e("TorService") { "Failed to initialize Arti: error $initResult, clearing data and retrying" }
|
||||
clearAllArtiData()
|
||||
initResult = ArtiNative.initialize(dataDir)
|
||||
}
|
||||
if (initResult != 0) {
|
||||
Log.e("TorService") { "Failed to initialize Arti on retry: error $initResult" }
|
||||
initialized.set(false)
|
||||
_status.value = TorServiceStatus.Off
|
||||
return@withContext
|
||||
|
||||
Reference in New Issue
Block a user