@@ -10,6 +10,7 @@ import com.synonym.vssclient.vssNewClientWithLnurlAuth
1010import com.synonym.vssclient.vssStore
1111import kotlinx.coroutines.CompletableDeferred
1212import kotlinx.coroutines.CoroutineDispatcher
13+ import kotlinx.coroutines.delay
1314import kotlinx.coroutines.withContext
1415import kotlinx.coroutines.withTimeout
1516import to.bitkit.data.keychain.Keychain
@@ -28,24 +29,15 @@ class VssBackupClient @Inject constructor(
2829) {
2930 private var isSetup = CompletableDeferred <Unit >()
3031
31- /* *
32- * Sets up the VSS client. Returns true if setup succeeded, false if mnemonic not available.
33- * Throws on other errors.
34- */
35- suspend fun setup (walletIndex : Int = 0): Boolean = withContext(ioDispatcher) {
36- // If already set up successfully, return immediately
37- if (isSetup.isCompleted && ! isSetup.isCancelled) {
38- runCatching { isSetup.await() }.onSuccess { return @withContext true }
39- }
32+ suspend fun setup (walletIndex : Int = 0): Result <Boolean > = withContext(ioDispatcher) {
33+ runCatching {
34+ if (isSetup.isCompleted && ! isSetup.isCancelled) {
35+ runCatching { isSetup.await() }.onSuccess { return @runCatching true }
36+ }
4037
41- // Check if mnemonic is available before proceeding
42- val mnemonic = keychain.loadString(Keychain .Key .BIP39_MNEMONIC .name)
43- if (mnemonic == null ) {
44- Logger .warn(" VSS client setup deferred: mnemonic not available yet" , context = TAG )
45- return @withContext false
46- }
38+ val mnemonic = keychain.loadString(Keychain .Key .BIP39_MNEMONIC .name)
39+ ? : return @runCatching false
4740
48- runCatching {
4941 withTimeout(30 .seconds) {
5042 Logger .debug(" VSS client setting up…" , context = TAG )
5143 val vssUrl = Env .vssServerUrl
@@ -72,11 +64,44 @@ class VssBackupClient @Inject constructor(
7264 isSetup.complete(Unit )
7365 Logger .info(" VSS client setup with server: '$vssUrl '" , context = TAG )
7466 }
67+ true
7568 }.onFailure {
7669 isSetup.completeExceptionally(it)
77- Logger .error(" VSS client setup error" , e = it, context = TAG )
70+ Logger .error(" VSS client setup error" , it, TAG )
71+ }
72+ }
73+
74+ class SetupRetryLogger {
75+ var onSuccess: (attempt: Int ) -> Unit = {}
76+ var onRetry: (attempt: Int , maxAttempts: Int , delayMs: Long ) -> Unit = { _, _, _ -> }
77+ var onExhausted: (maxAttempts: Int ) -> Unit = {}
78+ }
79+
80+ suspend fun setupWithRetry (
81+ maxAttempts : Int = 10,
82+ baseDelayMs : Long = 1000L,
83+ logger : SetupRetryLogger .() -> Unit ,
84+ ): Result <Boolean > = withContext(ioDispatcher) {
85+ val log = SetupRetryLogger ().apply (logger)
86+ var attempt = 0
87+ while (attempt < maxAttempts) {
88+ val result = setup()
89+ if (result.getOrDefault(false )) {
90+ log.onSuccess(attempt + 1 )
91+ return @withContext Result .success(true )
92+ }
93+ if (result.isFailure) {
94+ return @withContext result
95+ }
96+ attempt++
97+ if (attempt < maxAttempts) {
98+ val delayMs = baseDelayMs * attempt
99+ log.onRetry(attempt, maxAttempts, delayMs)
100+ delay(delayMs)
101+ }
78102 }
79- true
103+ log.onExhausted(maxAttempts)
104+ Result .success(false )
80105 }
81106
82107 fun reset () {
0 commit comments