-
Notifications
You must be signed in to change notification settings - Fork 2
fix: mnemonic not found crash #678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
| import to.bitkit.data.keychain.Keychain | ||
| import to.bitkit.test.BaseUnitTest | ||
| import kotlin.test.assertFalse | ||
| import kotlin.test.assertTrue |
Check warning
Code scanning / detekt
Detects unused imports Warning test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@piotr-iohk
run in terminal ./gradlew detekt to self-check until no more issues.
If you can't fix an issue let us know, you can also just @Suppress("DetektCheckId") when applicable
ovitrif
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utAck
PS. Still need to resolve the nit review comments to be able to merge AFAIU, so I let myself choose Approve even though I want the remarks addressed 🙏🏻
| /** | ||
| * Sets up the VSS client. Returns true if setup succeeded, false if mnemonic not available. | ||
| * Throws on other errors. | ||
| */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pls remove comments, Claude should do it based on CLAUDE.md but sometimes it forgets or you're not using claude 🙃
| * Throws on other errors. | ||
| */ | ||
| suspend fun setup(walletIndex: Int = 0): Boolean = withContext(ioDispatcher) { | ||
| // If already set up successfully, return immediately |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: also inline comments, the less the better.
| return@withContext false | ||
| } | ||
|
|
||
| runCatching { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: why not keeping everything into the initial runCatching and relying on result type Result<Boolean>?!
ps. would help make nicer code here.
| private suspend fun setupVssClientWithRetry() { | ||
| var attempt = 0 | ||
| val maxAttempts = 10 | ||
| val baseDelayMs = 1000L | ||
|
|
||
| while (attempt < maxAttempts && isObserving) { | ||
| val success = runCatching { vssBackupClient.setup() }.getOrDefault(false) | ||
| if (success) { | ||
| Logger.debug("VSS client setup succeeded on attempt ${attempt + 1}", context = TAG) | ||
| return | ||
| } | ||
| attempt++ | ||
| if (attempt < maxAttempts) { | ||
| val delayMs = baseDelayMs * attempt // Linear backoff: 1s, 2s, 3s, ... | ||
| Logger.debug( | ||
| "VSS client setup deferred, retrying in ${delayMs}ms (attempt $attempt/$maxAttempts)", | ||
| context = TAG, | ||
| ) | ||
| delay(delayMs) | ||
| } | ||
| } | ||
|
|
||
| if (isObserving) { | ||
| Logger.warn("VSS client setup failed after $maxAttempts attempts", context = TAG) | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@piotr-iohk any reason this isn't in VssBackupClient?!
nit: Optimised setup for it seems more like its own concern than the responsibility of BackupRepo; it even has a "dumber" setup already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extra nit: if this is a logic that we have in multiple places (which I reckon we might) it might make sense to foresee an utility higher-order-function (accepting labmdas) for retryWithExponentialBackoff
Code ReviewI found 2 issues that need attention: Issue 1: Logic Error in VssBackupClient.ktFile: The Problem:
Code reference:
Suggested fix: }.onFailure {
isSetup.completeExceptionally(it)
Logger.error("VSS client setup error", e = it, context = TAG)
return@withContext false // Return false on failure
}
trueOr alternatively, re-throw the exception to match the documented behavior. Issue 2: CLAUDE.md Violation in VssBackupClientTest.ktFile: The
The rule states to use
Code reference: bitkit-android/app/src/test/java/to/bitkit/data/backup/VssBackupClientTest.kt Lines 23 to 29 in 1950109
Suggested fix: @Before
fun setUp() {
sut = VssBackupClient(
ioDispatcher = testDispatcher,
vssStoreIdProvider = vssStoreIdProvider,
keychain = keychain,
)
} |
|
@piotr-iohk to streamline this |
Fixes: #553
OBS: #657 (comment)
Description
This PR fixes a crash that occurs when
VssBackupClient.setup()is called before the mnemonic is available in the keychain. This race condition can happen during wallet restoration whenBackupRepo.startObservingBackups()is triggered (onNodeLifecycleState.Running) before the mnemonic has been saved.Root cause:
Changes:
VssBackupClient.setup()now returnsBoolean(true = success, false = mnemonic not available yet)BackupReponow retriessetup()with linear backoff (1s, 2s, 3s... up to 10 attempts) when mnemonic is not yet availablePreview
N/A - crash fix, no UI changes
QA Notes
To reproduce the original crash: note: it is intermittent.
Unit tests added:
setup returns false when mnemonic is not availablesetup does not call vssStoreIdProvider when mnemonic is not availablesetup checks mnemonic before proceeding with vss initializationsetup can be called multiple times when mnemonic not availableRun with:
./gradlew testDevDebugUnitTest --tests "to.bitkit.data.backup.VssBackupClientTest"