Skip to content

feat: support compressed headers#1463

Open
HashEngineering wants to merge 18 commits intomasterfrom
feat/compressed-headers
Open

feat: support compressed headers#1463
HashEngineering wants to merge 18 commits intomasterfrom
feat/compressed-headers

Conversation

@HashEngineering
Copy link
Collaborator

@HashEngineering HashEngineering commented Feb 12, 2026

Issue being fixed or feature implemented

Related PR's and Dependencies

dashpay/dashj#291

Screenshots / Videos

How Has This Been Tested?

  • QA (Mobile Team)

Checklist:

  • I have performed a self-review of my own code and added comments where necessary
  • I have added or updated relevant unit/integration/functional/e2e tests

Summary by CodeRabbit

  • New Features

    • Added PIN upgrade workflow and blockchain rescan functionality with optional wallet creation date selection for faster synchronization.
    • Enhanced invitation and instant username creation flows with improved voting and fee management.
    • Extended gift card provider support and error messaging across multiple languages.
  • Bug Fixes

    • Fixed Send Flows to prevent unwanted error dialog displays.
  • Documentation

    • Added comprehensive localization support for wallet restoration, blockchain rescan, and PIN upgrade features across 25+ languages.
  • Chores

    • Updated dependencies and bumped wallet version to 11.6.1.

@HashEngineering HashEngineering self-assigned this Feb 12, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

This PR introduces v11.6.1, adding support for instant usernames, non-contested username voting, invitation fees, blockchain rescan with optional wallet creation date selection, and PIN upgrade flows. It includes extensive localization updates across 30+ languages, minor test signature updates for suspend functions, build dependency updates (dashj-scrypt-android 1.0.0→1.0.1, dashjVersion and dppVersion snapshot removal), and targeted logic adjustments for network availability tracking and username voting recovery.

Changes

Cohort / File(s) Summary
Build Configuration
Gemfile, build.gradle, wallet/build.gradle
Added nkf gem dependency; updated dashjVersion (21.1.14-SNAPSHOT→21.1.14) and dppVersion (2.0.3-SNAPSHOT→2.0.3); bumped dashj-scrypt-android to 1.0.1; updated version to 11.6.1 (from 11.5.0).
Version Documentation
wallet/CHANGES
Added v11.6.1 release notes documenting Send Flows error dialog fix.
Test Infrastructure
wallet/androidTest/de/schildbach/wallet/security/SecurityGuardMultiThreadingTest.kt, wallet/androidTest/de/schildbach/wallet/transactions/TransactionExportTest.kt
Updated suspend function signatures for wallet wiped listeners (non-suspend→suspend); disabled IV backup recovery test; refactored test to use TransactionMetadataProvider mock instead of Map-based storage; wrapped exporter calls in runBlocking.
DashPay Service Logic
wallet/src/de/schildbach/wallet/service/BlockchainServiceImpl.kt
Added availableNetworks tracking for multi-network resilience; introduced pendingContactPaymentTxs batching; improved network loss handling and cleanup logging.
Platform Repository
wallet/src/de/schildbach/wallet/ui/dashpay/PlatformRepo.kt
Added Stopwatch timing/logging for getVoteContenders; updated hasIdentity() to hasBlockchainIdentity() check in newTx flow.
Username Recovery & Voting
wallet/src/de/schildbach/wallet/service/platform/work/RestoreIdentityWorker.kt
Replaced credit-funding branching with unified identity recovery path; added contested username voting tracking (foundContestedNameInVotingPeriod, getCurrentlyContestedNames); implemented voting status assignment (VOTING, LOCKED, APPROVED, LOST_VOTE); scheduled GetUsernameVotingResultOperation; simplified identity state transitions.
Send & Payment Flows
wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt, wallet/src/de/schildbach/wallet/ui/send/SendCoinsViewModel.kt, wallet/src/de/schildbach/wallet/ui/send/SendCoinsFragment.kt
Added Context.propagate(wallet.context) calls in estimateNetworkFee, directPay, createSendRequest, signAndSendPayment, and getPendingBalance; added exception logging in updateView and handleGo.
Transaction UI
wallet/src/de/schildbach/wallet/ui/dashpay/notification/TransactionViewHolder.kt, wallet/src/de/schildbach/wallet/ui/main/WalletTransactionsFragment.kt
Removed chainHeight parameter from bind signature; broadened error condition check to include "invalid peer certificate: certificate expired".
Localization: Explore Dash (Gift Cards)
features/exploredash/src/main/res/values-el/strings-explore-dash.xml, features/exploredash/src/main/res/values-pt/strings-explore-dash.xml, features/exploredash/src/main/res/values-sk/strings-explore-dash.xml
Added Greek and Portuguese gift card UI strings (providers, balance checks, error messages, barcode, contact); updated Slovak terminology from "kartu" to "poukážku" (voucher).
Localization: Core UI (Multiple Languages)
common/src/main/res/values-sk/strings.xml, wallet/res/values-*/strings-extra.xml
Updated onboarding recover wallet text across 20+ languages to shorter labels; added forgot_pin_setup_new_pin_title; changed menu_invite_title (e.g., "Invite"→"Invitations"); replaced multi-variant welcome screen strings with consolidated welcome_screen_title_1/2/3.
Localization: DashPay (Invitations & Voting)
wallet/res/values-*/strings-dashpay.xml
Renamed invitation_init_title→invitation_title across all 20+ languages; added 30+ new strings per language supporting instant usernames, invitation fees (noncontested/contested variants), voting outcomes, error states, and create instant username dialogs.
Localization: Blockchain & Restore
wallet/res/values-*/strings.xml
Added restore_wallet_creation_date_* and rescan_blockchain_* UI strings (~7 entries per language) enabling optional wallet creation date selection during restore/rescan; added upgrade_pin_* strings for PIN upgrade flow across 20+ languages.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • Release v11.6 #1458: Shares identical build configuration updates (Gemfile, build.gradle versions), string resource modifications across multiple locales, and test file changes, indicating parallel or preparatory localization/versioning work.

Suggested reviewers

  • abaranouski

Poem

🐰 A whisker of voting, usernames now shine,
Invitations and fees in a pattern so fine,
From instant to contested, the features align,
With languages blooming in thirty-line design,
The wallets restored with dates so divine! 🌙

🚥 Pre-merge checks | ✅ 2 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'feat: support compressed headers' is specific and describes a real feature addition present in the changeset, but does not capture the main focus reflected by the actual changes, which are predominantly bug fixes, translations, dependency updates, and test modifications rather than compressed headers support. Clarify whether 'support compressed headers' is the primary objective; if not, update the title to reflect the actual main changes (e.g., bug fixes, translations, and dependency updates for v11.6.1 release).
✅ Passed checks (2 passed)
Check name Status Explanation
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into master
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/compressed-headers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
wallet/src/de/schildbach/wallet/ui/send/SendCoinsViewModel.kt (1)

270-272: ⚠️ Potential issue | 🟡 Minor

Pre-existing bug: error message is incorrect.

The guard checks !isAssetLock (i.e., isAssetLock is false), but the message says "isAssetLock must be true, but is true". It should say "but is false".

Proposed fix
-            error("isAssetLock must be true, but is true")
+            error("isAssetLock must be true, but is false")
wallet/src/de/schildbach/wallet/service/BlockchainServiceImpl.kt (1)

934-955: ⚠️ Potential issue | 🟠 Major

onCapabilitiesChanged is inconsistent with availableNetworks tracking, causing incorrect impediment state.

When capabilities change, this handler adds/removes the NETWORK impediment based solely on the single network's capabilities, ignoring whether other validated networks exist in availableNetworks. Scenario:

  1. WiFi (A) and Cellular (B) both available → both in availableNetworks, no impediment.
  2. WiFi (A) loses validation → onCapabilitiesChanged adds Impediment.NETWORK.
  3. Cellular (B) is still valid, but the impediment is now set, blocking the peer group.

Additionally, availableNetworks is never updated here, so a network that loses validation remains in the set, making the onLost emptiness check unreliable later.

Proposed fix
         override fun onCapabilitiesChanged(network: Network, capabilities: NetworkCapabilities) {
             serviceScope.launch {
                 val hasInternet = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                 val hasValidated = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
 
                 if (log.isInfoEnabled) {
                     val s = StringBuilder("network capabilities changed: ")
                         .append(network)
                         .append(", hasInternet: ").append(hasInternet)
                         .append(", validated: ").append(hasValidated)
                     log.info(s.toString())
                 }
 
                 if (hasInternet && hasValidated) {
+                    availableNetworks.add(network)
                     impediments.remove(Impediment.NETWORK)
                 } else {
-                    impediments.add(Impediment.NETWORK)
+                    availableNetworks.remove(network)
+                    if (availableNetworks.isEmpty()) {
+                        impediments.add(Impediment.NETWORK)
+                    }
                 }
                 updateBlockchainStateImpediments()
                 check()
             }
         }
wallet/src/de/schildbach/wallet/service/platform/work/RestoreIdentityWorker.kt (1)

270-273: ⚠️ Potential issue | 🔴 Critical

Bug: minOf lambda always references the same outer documentWithVotes instead of each contender's document.

The lambda iterates over voteContenders.map.values but uses the captured documentWithVotes from the enclosing forEach, so every iteration deserializes the same document. The lambda parameter (it) — which is the actual per-contender value — is shadowed by the inner let. This yields an incorrect earliestCreatedAt.

The same bug exists in the duplicated block at lines 387–392.

🐛 Proposed fix
-                            val earliestCreatedAt = voteContenders.map.values.minOf {
-                                val document = documentWithVotes.serializedDocument?.let { platformRepo.platform.names.deserialize(it) }
-                                document?.createdAt ?: 0
-                            }
+                            val earliestCreatedAt = voteContenders.map.values.minOf { contenderDoc ->
+                                val document = contenderDoc.serializedDocument?.let { platformRepo.platform.names.deserialize(it) }
+                                document?.createdAt ?: 0
+                            }

Apply the same fix to the second occurrence (lines 387–392).

🤖 Fix all issues with AI agents
In
`@wallet/src/de/schildbach/wallet/service/platform/work/RestoreIdentityWorker.kt`:
- Around line 293-411: The review points out duplicated contest-processing logic
between the getCurrentlyContestedNames and getAllContestedNames paths; extract
the shared logic into a private method (suggested name processContestedNames)
that accepts parameters (names: List<String>, blockchainIdentity:
BlockchainIdentity, blockchainIdentityData: BlockchainIdentityData,
maybeDualUsernames: Boolean, instantUsername: String?) and returns a Boolean
indicating foundContestedNameInVotingPeriod; move the loop body that iterates
voteContenders, sets
blockchainIdentity.currentUsername/secondaryUsername/primaryUsername, computes
usernameRequestStatus, inserts UsernameRequest, builds/updates UsernameInfo (fix
the minOf computation to deserialize each contender’s serializedDocument rather
than reusing documentWithVotes), set votingStartedAt/requestStatus, call
platformRepo.updateBlockchainIdentityData(blockchainIdentityData,
blockchainIdentity), and enqueue GetUsernameVotingResultOperation when
appropriate; then replace both original code blocks to call
foundContestedNameInVotingPeriod =
processContestedNames(currentlyContestedNames, ...) and, if false,
foundContestedNameInVotingPeriod = processContestedNames(contestedNames, ...),
removing the duplicated code and the inconsistent null-safe checks.
🟡 Minor comments (21)
wallet/res/values-th/strings-extra.xml-4-4 (1)

4-4: ⚠️ Potential issue | 🟡 Minor

Inconsistent use of English "wallet" vs. Thai "กระเป๋าสตางค์".

Most other strings in this file translate "wallet" as "กระเป๋าสตางค์" (e.g., onboarding_load_wallet_error on Line 5, set_pin_create_new_wallet on Line 7, recover_wallet_title on Line 70). This string uses the English word "wallet" instead, which creates an inconsistency in the Thai localization.

wallet/res/values-ro/strings.xml-237-237 (1)

237-237: ⚠️ Potential issue | 🟡 Minor

Typo: duplicated word "din" → "Scanează din din nou" should be "Scanează din nou".

The immediately preceding string on line 236 (preferences_initiate_reset_dialog_positive) has the correct form.

Proposed fix
-    <string name="rescan_blockchain_confirm_button">Scanează din din nou</string>
+    <string name="rescan_blockchain_confirm_button">Scanează din nou</string>
wallet/res/values-el/strings-extra.xml-338-338 (1)

338-338: ⚠️ Potential issue | 🟡 Minor

Likely grammatical error: "χωρίς μέσω" doesn't make sense in Greek.

"χωρίς" means "without" and "μέσω" means "through/via" — having both together is contradictory. The intended meaning is probably either:

  • "Στείλτε και λάβετε Dash μέσω ονομάτων χρήσης" (Send and receive Dash through usernames), or
  • "Στείλτε και λάβετε Dash χωρίς διευθύνσεις, μέσω ονομάτων χρήσης" (without addresses, through usernames)

Please verify with a native speaker or the source string.

wallet/res/values-zh-rTW/strings-extra.xml-335-337 (1)

335-337: ⚠️ Potential issue | 🟡 Minor

Extraneous spaces around "達世幣" in Chinese text.

Lines 336 and 337 have spaces before/around "達世幣" (e.g., 花費 達世幣, 存儲 達世幣 最安全的方式). In Chinese typography, spaces between Chinese characters and Chinese-context words are not standard and will look odd in the UI. Consider removing them:

Proposed fix
     <string name="welcome_screen_title_1">通過用戶名發送和接收達世幣</string>
-    <string name="welcome_screen_title_2">在數千家商店花費 達世幣</string>
-    <string name="welcome_screen_title_3">存儲 達世幣 最安全的方式</string>
+    <string name="welcome_screen_title_2">在數千家商店花費達世幣</string>
+    <string name="welcome_screen_title_3">存儲達世幣最安全的方式</string>
wallet/res/values-es/strings-extra.xml-268-268 (1)

268-268: ⚠️ Potential issue | 🟡 Minor

Fix accent typo in the invite menu label.

“Invitaciónes” is misspelled in Spanish; it should be “Invitaciones”.

✏️ Suggested fix
-    <string name="menu_invite_title">Invitaciónes</string>
+    <string name="menu_invite_title">Invitaciones</string>
wallet/res/values-es/strings.xml-206-212 (1)

206-212: ⚠️ Potential issue | 🟡 Minor

Fix typo/placeholder in the selected date label.

The current value includes an extra “1” and omits the expected “%s” format alignment for the selected date.

✏️ Suggested fix
-    <string name="restore_wallet_selected_date">Selecciono: 1%s</string>
+    <string name="restore_wallet_selected_date">Seleccionado: %s</string>
wallet/res/values-pl/strings-dashpay.xml-531-531 (1)

531-531: ⚠️ Potential issue | 🟡 Minor

Fix typo in the invitation fee CTA.

Line 531 has “Potwiedź”, which should be “Potwierdź”.

✏️ Suggested fix
-    <string name="invitation_fee_confirm_and_pay_button">Potwiedź i zapłać</string>
+    <string name="invitation_fee_confirm_and_pay_button">Potwierdź i zapłać</string>
wallet/res/values-id/strings-extra.xml-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor

Clarify onboarding recovery wording (wallet vs private key).

Line 5 now says “Pulihkan kunci pribadi”, but the key is onboarding_recover_wallet. This could mislead users about the recovery flow. Consider aligning the text with wallet recovery (or explicitly mention the recovery phrase if that’s the intended UI).

✏️ Suggested wording update
-    <string name="onboarding_recover_wallet">Pulihkan kunci pribadi</string>
+    <string name="onboarding_recover_wallet">Pulihkan dompet</string>
wallet/res/values-de/strings.xml-207-213 (1)

207-213: ⚠️ Potential issue | 🟡 Minor

Keep pronoun usage consistent in restore-date copy.

Line 208 uses the formal “Ihrer,” while surrounding strings use the informal “deiner/du.” Please align to one form for UX consistency.

Suggested tweak
-    <string name="restore_wallet_creation_date_info">Durch die Auswahl des Erstellungsdatums Ihrer Wallet wird die Synchronisierung beschleunigt, da nur Daten ab diesem Datum gescannt werden.</string>
+    <string name="restore_wallet_creation_date_info">Durch die Auswahl des Erstellungsdatums deiner Wallet wird die Synchronisierung beschleunigt, da nur Daten ab diesem Datum gescannt werden.</string>
wallet/res/values-es/strings-dashpay.xml-20-20 (1)

20-20: ⚠️ Potential issue | 🟡 Minor

Fix the placeholder typo (“1%s”).

There’s an extra “1” before the %s placeholder.

✏️ Suggested fix
-    <string name="new_account_secondary_confirm_message">Elegiste 1%s como nombre de usuario instantáneo. Ten en cuenta que el nombre de usuario no se puede cambiar una vez registrado.</string>
+    <string name="new_account_secondary_confirm_message">Elegiste %s como nombre de usuario instantáneo. Ten en cuenta que el nombre de usuario no se puede cambiar una vez registrado.</string>
wallet/res/values-es/strings-dashpay.xml-219-220 (1)

219-220: ⚠️ Potential issue | 🟡 Minor

Use imperative tone for the wait instruction.

“Esperé” is past tense; UI copy should use imperative/neutral.

✏️ Suggested fix
-    <string name="join_dashpay_wait">Esperé a que la sincronización termine para crear un nombre de usuario</string>
+    <string name="join_dashpay_wait">Espera a que la sincronización termine para crear un nombre de usuario</string>
features/exploredash/src/main/res/values-el/strings-explore-dash.xml-194-194 (1)

194-194: ⚠️ Potential issue | 🟡 Minor

Make this label reflect “check current balance.”

The key suggests a balance check, but the value reads “view card details.” Consider aligning to avoid confusing UX.

✏️ Suggested fix
-    <string name="purchase_check_current_balance">Δείτε τις λεπτομέρειες της κάρτας</string>
+    <string name="purchase_check_current_balance">Ελέγξτε το τρέχον υπόλοιπο της κάρτας</string>
wallet/res/values-it/strings-dashpay.xml-524-529 (1)

524-529: ⚠️ Potential issue | 🟡 Minor

Align the non‑contested length rule with the 20+ wording used elsewhere.

This string says “più di 20 caratteri,” but earlier copy in this file states “20 o più caratteri,” which implies ≥20. Consider aligning for consistency and to avoid off‑by‑one confusion.

✏️ Suggested fix
-    <string name="invitation_fee_noncontested_message">La persona a cui invii questo invito può richiedere uno username che contenga un numero compreso tra 2 e 9 e che sia lungo più di 20 caratteri.</string>
+    <string name="invitation_fee_noncontested_message">La persona a cui invii questo invito può richiedere uno username che contenga un numero compreso tra 2 e 9 e che sia lungo 20 o più caratteri.</string>
wallet/res/values-zh-rTW/strings-dashpay.xml-525-543 (1)

525-543: ⚠️ Potential issue | 🟡 Minor

Fix the Dash network wording and extra punctuation.

  • “儀表板網絡” reads as “dashboard network,” which is incorrect here.
  • The “。.” sequence introduces an extra period.
✏️ Suggested fix
-    <string name="invitation_fee_message">每個邀請將需要得到資助,以便接收者可以在儀表板網絡上快速創建其用戶名稱。</string>
+    <string name="invitation_fee_message">每個邀請將需要得到資助,以便接收者可以在達世幣網路上快速創建其用戶名稱。</string>
@@
-    <string name="create_instant_username_description">您請求的用戶名將在 2 週內進行投票。為了立即聯繫和付款,您可以創建類似的即時用戶名。.\n\n如果您的原始請求獲得批准,它將取代即時用戶名,並保留聯繫人和歷史記錄。如果沒有獲批,請繼續使用即時用戶名。</string>
+    <string name="create_instant_username_description">您請求的用戶名將在 2 週內進行投票。為了立即聯繫和付款,您可以創建類似的即時用戶名。\n\n如果您的原始請求獲得批准,它將取代即時用戶名,並保留聯繫人和歷史記錄。如果沒有獲批,請繼續使用即時用戶名。</string>
wallet/res/values-nl/strings-dashpay.xml-454-454 (1)

454-454: ⚠️ Potential issue | 🟡 Minor

Trim unintended leading spaces in Dutch strings.

The current values render with a visible leading space.

🛠️ Suggested fix
-    <string name="request_username_length_requirement_noncontested"> Tussen 20 en 23 tekens</string>
+    <string name="request_username_length_requirement_noncontested">Tussen 20 en 23 tekens</string>
@@
-    <string name="invitation_fee_noncontested"> Niet betwist</string>
+    <string name="invitation_fee_noncontested">Niet betwist</string>

Also applies to: 527-527

wallet/res/values-fa/strings-dashpay.xml-523-537 (1)

523-537: ⚠️ Potential issue | 🟡 Minor

Correct Persian wording for contested/non‑contested labels and instant‑username text.

The current labels read as approved/unapproved, the “two types” sentence is negated, and there’s a typo in “در عیر”. Please verify the wording with your translator.

🛠️ Suggested fix (please confirm with translator)
-    <string name="invitation_fee_noncontested">مورد موافقت قرار نگرفت</string>
-    <string name="invitation_fee_contested">مورد موافقت قرار گرفت</string>
+    <string name="invitation_fee_noncontested">غیررقابتی</string>
+    <string name="invitation_fee_contested">رقابتی</string>
@@
-    <string name="username_types_message">دو نوع نام کاربری نداریم: توافق‌شده و توافق‌نشده.\nنام‌های کاربری توافق‌نشده شامل حداقل یک عدد (۲ تا ۹) هستید و یا بیش از ۲۰ کاراکتر دارند و به صورت خودکار مورد تائید قرار می‌گیرند.\nاگر می‌خواهید نام‌ کابری توافق‌شده ایجاد کنید که کوتاه‌تر و بدون عدد است، باید بدون دعوتنامه در دش‌پی ثبت‌نام کنید، کارمزد مربوطه را بپردازید و منتظر تائید پس از تکمیل دوره رأی‌گیری بمانید.</string>
+    <string name="username_types_message">دو نوع نام کاربری داریم: توافق‌شده و توافق‌نشده.\nنام‌های کاربری توافق‌نشده شامل حداقل یک عدد (۲ تا ۹) هستید و یا بیش از ۲۰ کاراکتر دارند و به صورت خودکار مورد تائید قرار می‌گیرند.\nاگر می‌خواهید نام‌ کابری توافق‌شده ایجاد کنید که کوتاه‌تر و بدون عدد است، باید بدون دعوتنامه در دش‌پی ثبت‌نام کنید، کارمزد مربوطه را بپردازید و منتظر تائید پس از تکمیل دوره رأی‌گیری بمانید.</string>
@@
-    <string name="create_instant_username_description">نام کاربری درخواستی شما طی دو هفته آینده به رای گذاشته می‌شود. برای ارتباط‌گیری و پرداخت‌های فوری، می‌توانید یک نام کاربری مشابه فوری بسازید.\nاگر درخواست اصلی‌تان مورد پذیرش قرار گرفت، آن نام کاربری جایگزین این نام کاربری فوری می‌شود و همه مخاطبان و سابقه آن نیز حفظ خواهد شد. در عیر این صورت، می‌توانید همچنان به استفاده از این نام کاربری فوری ادامه دهید. </string>
+    <string name="create_instant_username_description">نام کاربری درخواستی شما طی دو هفته آینده به رای گذاشته می‌شود. برای ارتباط‌گیری و پرداخت‌های فوری، می‌توانید یک نام کاربری مشابه فوری بسازید.\nاگر درخواست اصلی‌تان مورد پذیرش قرار گرفت، آن نام کاربری جایگزین این نام کاربری فوری می‌شود و همه مخاطبان و سابقه آن نیز حفظ خواهد شد. در غیر این صورت، می‌توانید همچنان به استفاده از این نام کاربری فوری ادامه دهید. </string>

Also applies to: 542-542

wallet/res/values-fa/strings-dashpay.xml-19-19 (1)

19-19: ⚠️ Potential issue | 🟡 Minor

Fix missing space around the %s placeholder.

Without the space, the username will render glued to the following word.

🛠️ Suggested fix
-    <string name="new_account_secondary_confirm_message">می‌توانید %sرا به عنوان نام کاربری فوری انتخاب کنید. دقت داشته باشید که پس از ثبت نام کاربری، دیگر امکان تغییر آن وجود ندارد.  </string>
+    <string name="new_account_secondary_confirm_message">می‌توانید %s را به عنوان نام کاربری فوری انتخاب کنید. دقت داشته باشید که پس از ثبت نام کاربری، دیگر امکان تغییر آن وجود ندارد.  </string>
wallet/res/values-de/strings-dashpay.xml-450-450 (1)

450-450: ⚠️ Potential issue | 🟡 Minor

German grammar: "eine" should be "einem" (dative with "entsprechen").

The verb "entsprechen" requires the dative case: "einem dieser Kriterien" instead of "eine dieser Kriterien".

Proposed fix
-    <string name="request_username_requirements_message_invite_noncontested">Der Nutzername muss eine dieser Kriterien entsprechen</string>
+    <string name="request_username_requirements_message_invite_noncontested">Der Nutzername muss einem dieser Kriterien entsprechen</string>
wallet/res/values-fr/strings-dashpay.xml-533-533 (1)

533-533: ⚠️ Potential issue | 🟡 Minor

French grammatical error: "créé" should be "créée" (feminine past participle).

"L'invitation" is feminine in French, so the past participle should agree: "créée" instead of "créé".

Proposed fix
-    <string name="request_username_invitation_not_mixed_message">L\'invitation a été créé avec des fonds non mélangés</string>
+    <string name="request_username_invitation_not_mixed_message">L\'invitation a été créée avec des fonds non mélangés</string>
wallet/res/values-zh/strings-dashpay.xml-529-529 (1)

529-529: ⚠️ Potential issue | 🟡 Minor

Mistranslation: "且" (AND) should be "或" (OR) — user must meet either condition, not both.

The noncontested username requirement is that the name contains a digit 2–9 or is 20+ characters long. This string uses "且" (and), which would incorrectly tell users they need both. This is inconsistent with username_types_message on Line 537 of this same file, which correctly uses "或" (or).

Proposed fix
-    <string name="invitation_fee_noncontested_message">收到您发送邀请的人可以申请包含数字2-9且长度超过20个字符的用户名.</string>
+    <string name="invitation_fee_noncontested_message">收到您发送邀请的人可以申请包含数字2-9或长度超过20个字符的用户名.</string>
wallet/res/values-de/strings-dashpay.xml-529-529 (1)

529-529: ⚠️ Potential issue | 🟡 Minor

Mistranslation: "und" (AND) should be "oder" (OR) — same issue as the Chinese locale.

The noncontested requirement is that the username contains a digit 2–9 or is 20+ characters. This string uses "und" (and), telling users both conditions must be met. Compare with username_types_message (Line 537), which correctly describes them as separate, independently sufficient conditions.

Proposed fix
-    <string name="invitation_fee_noncontested_message">Der Person, die du diese Einladung sendest, kann einen Nutzernamen beanspruchen der die Nummer 2-9 beinhaltet und mehr als 20 Zeichen hat.</string>
+    <string name="invitation_fee_noncontested_message">Der Person, der du diese Einladung sendest, kann einen Nutzernamen beanspruchen, der die Nummer 2-9 beinhaltet oder mehr als 20 Zeichen hat.</string>
🧹 Nitpick comments (10)
wallet/res/values-nl/strings-extra.xml (1)

93-93: Pre-existing typo: "jpuw" should be "jouw".

Not introduced by this PR, but since you're already touching this file: line 93 contains jpuw portemonnee which should be jouw portemonnee.

Suggested fix
-    <string name="forgot_pin_instruction_3">Log in met je nieuwe PIN om toegang te krijgen tot jpuw portemonnee</string>
+    <string name="forgot_pin_instruction_3">Log in met je nieuwe PIN om toegang te krijgen tot jouw portemonnee</string>
wallet/androidTest/de/schildbach/wallet/security/SecurityGuardMultiThreadingTest.kt (1)

910-1043: Prefer @Ignore over commenting out the entire test.

The @Ignore annotation is already imported (line 39) and is the JUnit-idiomatic way to disable tests. It keeps the test visible in reports as "skipped" and avoids ~130 lines of commented-out code that won't be caught by refactoring tools or compilation checks.

Suggested approach
-//    `@Test`
-//    fun testIvBackupAndRecovery() {
-//        ...
-//    }
+    `@Ignore`("IV backup/recovery test temporarily disabled")
+    `@Test`
+    fun testIvBackupAndRecovery() {
+        // ... (uncomment the body)
+    }
wallet/res/values-tr/strings-dashpay.xml (1)

391-393: Note: invitation_fee_title has the same Turkish value as invitation_confirm_title (line 19) and dashpay_invite_fee (line 85).

All three resolve to "Davet Ücreti". If these are intentionally separate keys for different UI contexts, this is fine. Just flagging in case any of them are redundant or should have distinct wording.

wallet/androidTest/de/schildbach/wallet/transactions/TransactionExportTest.kt (1)

71-79: System.currentTimeMillis() fallback could make the test non-deterministic.

Line 73: if tx.updateTime were ever null, the fallback to System.currentTimeMillis() would produce a wall-clock timestamp, causing the assertion on line 92 (expecting "1970-01-01T00:00:00Z") to fail unpredictably. Consider using a fixed fallback (e.g., 0L) to keep the test deterministic.

Suggested fix
-            tx.updateTime?.time ?: System.currentTimeMillis(),
+            tx.updateTime?.time ?: 0L,
wallet/src/de/schildbach/wallet/ui/main/WalletTransactionsFragment.kt (1)

420-431: Error-message matching via raw strings is fragile but the new logic is correct.

The short-circuit evaluation of || protects against NPE on line 431 (when errorMessage is null, line 430 already yields true). The boolean semantics are sound: "certificate expired" errors will now also route to the CreateUsernameActivity reuse flow, which appears intentional.

However, all the error-message checks across lines 420–431 rely on substring matching against server-produced text. If the upstream error wording changes even slightly, these conditions silently break. Consider extracting these strings into named constants (or matching on error codes/types if available) to make them easier to maintain and grep for.

wallet/src/de/schildbach/wallet/ui/send/SendCoinsViewModel.kt (1)

235-235: Redundant second Context.propagate at Line 251.

The context is already propagated at Line 235 at the top of the same withContext(Dispatchers.IO) block. Between these two calls, there are no suspension points (only regular function calls), so the thread won't change and the context remains propagated. The second call is harmless but unnecessary.

Also applies to: 251-252

Gemfile (1)

11-11: Consider pinning the nkf gem version for reproducible builds.

The other gems in this file specify version constraints (~> 2.227.1, ~> 0.1.2), but nkf is unpinned. This is likely added to resolve a Ruby 3.1+ stdlib extraction warning, which is fine, but pinning the version would be consistent with the rest of the file.

wallet/src/de/schildbach/wallet/service/BlockchainServiceImpl.kt (1)

1937-1942: TOCTOU on peerGroup!!.lock.isLocked — listeners may be leaked.

The lock state can change between the check and the removeXxxEventListener calls. If the lock is held at check time, listeners are silently skipped and never removed. Since this is in onDestroy and forceStop follows shortly after, the practical risk is low, but a leaked listener could fire during shutdown.

Consider wrapping in a tryLock with a short timeout instead, or documenting that forceStop will clean up remaining listeners.

wallet/src/de/schildbach/wallet/service/platform/work/RestoreIdentityWorker.kt (2)

461-467: Redundant sub-condition can be simplified.

creationState != VOTING || (creationState == VOTING && lostVote && showSecondaryUsername) is equivalent to creationState != VOTING || (lostVote && showSecondaryUsername) since the second branch already implies creationState == VOTING.


39-39: Remove unused imports AssetLockTransaction and Identity.

Lines 39 and 48 import AssetLockTransaction and Identity respectively, but neither is referenced in the code. The file uses BlockchainIdentity instead. These unused imports can be removed.

Comment on lines +293 to +411
// check all contests
if (!foundContestedNameInVotingPeriod) {

// check if the network has this name in the queue for voting
val watch2 = Stopwatch.createStarted()
val contestedNames = platformRepo.platform.names.getAllContestedNames()
log.info("getAllContestedNames returns {} names and took {}", contestedNames.size, watch2)

// now much of this can be put in BlockchainIdentity
contestedNames.forEach { name ->
if (maybeDualUsernames && instantUsername?.contains(name) == false) {
// skip this name if it doesn't appear to be the contested name for the found username
log.info("getVoteContenders: skipping {} because its not related to {}", name, instantUsername)
return@forEach
}
val voteContenders = platformRepo.getVoteContenders(name)
val winner = voteContenders.winner
voteContenders.map.forEach { (identifier, documentWithVotes) ->
if (blockchainIdentity.uniqueIdentifier == identifier) {
foundContestedNameInVotingPeriod = true
if (blockchainIdentity.currentUsername != null) {
blockchainIdentity.secondaryUsername = blockchainIdentity.currentUsername
}
blockchainIdentity.currentUsername = name
blockchainIdentity.primaryUsername = name
// load the serialized doc to get voting period and status...
val usernameRequestStatus = if (winner.isEmpty) {
UsernameRequestStatus.VOTING
} else {
val winnerInfo = winner.get().first
when {
winnerInfo.isLocked -> UsernameRequestStatus.LOCKED
winnerInfo.isWinner(blockchainIdentity.uniqueIdentifier) -> UsernameRequestStatus.APPROVED
else -> UsernameRequestStatus.LOST_VOTE
}
}

blockchainIdentity.usernameStatuses.apply {
// clear()
val usernameInfo = UsernameInfo(
null,
UsernameStatus.CONFIRMED,
blockchainIdentity.currentUsername!!,
usernameRequestStatus,
0
)
put(blockchainIdentity.currentUsername!!, usernameInfo)
}
var votingStartedAt = -1L
var label = name
if (winner.isEmpty) {
val contestedDocument = DomainDocument(
platformRepo.platform.names.deserialize(documentWithVotes.serializedDocument!!)
)
blockchainIdentity.currentUsername = contestedDocument.label
votingStartedAt = contestedDocument.createdAt!!
label = contestedDocument.label
}
val verifyDocument = IdentityVerify(platformRepo.platform.platform).get(
blockchainIdentity.uniqueIdentifier,
name
)

usernameRequestDao.insert(
UsernameRequest(
UsernameRequest.getRequestId(
blockchainIdentity.uniqueIdString,
blockchainIdentity.currentUsername!!
),
label,
name,
votingStartedAt,
blockchainIdentity.uniqueIdString,
verifyDocument?.url, // get it from the document
documentWithVotes.votes,
voteContenders.lockVoteTally,
false
)
)
// what if usernameInfo would have been null, we should create it.
var usernameInfo =
blockchainIdentity.usernameStatuses[blockchainIdentity.currentUsername!!]
if (usernameInfo == null) {
usernameInfo = UsernameInfo(
null,
UsernameStatus.CONFIRMED,
blockchainIdentity.currentUsername!!,
UsernameRequestStatus.VOTING
)
blockchainIdentity.usernameStatuses[blockchainIdentity.currentUsername!!] =
usernameInfo
}

// determine when voting started by finding the minimum timestamp
val earliestCreatedAt = voteContenders.map.values.minOf {
val document = documentWithVotes.serializedDocument?.let {
platformRepo.platform.names.deserialize(it)
}
document?.createdAt ?: 0
}

usernameInfo?.votingStartedAt = earliestCreatedAt
usernameInfo?.requestStatus = usernameRequestStatus
platformRepo.updateBlockchainIdentityData(blockchainIdentityData, blockchainIdentity)

// schedule work to check the status after voting has ended
if (usernameInfo?.votingStartedAt != 0L) {
GetUsernameVotingResultOperation(walletApplication)
.create(
usernameInfo?.username!!,
blockchainIdentity.uniqueIdentifier.toString(),
earliestCreatedAt
)
.enqueue()
}
}
}
}
}
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

~110 lines of duplicated contest-processing logic.

The block at lines 302–410 (getAllContestedNames path) is nearly identical to lines 187–291 (getCurrentlyContestedNames path). This makes both paths drift-prone — the minOf bug already demonstrates this, and the second copy introduces unnecessary null-safe calls (usernameInfo?.) that differ from the first copy.

Consider extracting a shared method, e.g.:

private fun processContestedNames(
    names: List<String>,
    blockchainIdentity: BlockchainIdentity,
    blockchainIdentityData: BlockchainIdentityData,
    maybeDualUsernames: Boolean,
    instantUsername: String?
): Boolean { /* shared logic */ }

Then the two call sites reduce to:

foundContestedNameInVotingPeriod = processContestedNames(currentlyContestedNames, ...)
if (!foundContestedNameInVotingPeriod) {
    val contestedNames = platformRepo.platform.names.getAllContestedNames()
    foundContestedNameInVotingPeriod = processContestedNames(contestedNames, ...)
}
🤖 Prompt for AI Agents
In
`@wallet/src/de/schildbach/wallet/service/platform/work/RestoreIdentityWorker.kt`
around lines 293 - 411, The review points out duplicated contest-processing
logic between the getCurrentlyContestedNames and getAllContestedNames paths;
extract the shared logic into a private method (suggested name
processContestedNames) that accepts parameters (names: List<String>,
blockchainIdentity: BlockchainIdentity, blockchainIdentityData:
BlockchainIdentityData, maybeDualUsernames: Boolean, instantUsername: String?)
and returns a Boolean indicating foundContestedNameInVotingPeriod; move the loop
body that iterates voteContenders, sets
blockchainIdentity.currentUsername/secondaryUsername/primaryUsername, computes
usernameRequestStatus, inserts UsernameRequest, builds/updates UsernameInfo (fix
the minOf computation to deserialize each contender’s serializedDocument rather
than reusing documentWithVotes), set votingStartedAt/requestStatus, call
platformRepo.updateBlockchainIdentityData(blockchainIdentityData,
blockchainIdentity), and enqueue GetUsernameVotingResultOperation when
appropriate; then replace both original code blocks to call
foundContestedNameInVotingPeriod =
processContestedNames(currentlyContestedNames, ...) and, if false,
foundContestedNameInVotingPeriod = processContestedNames(contestedNames, ...),
removing the duplicated code and the inconsistent null-safe checks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we will do this another time

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HashEngineering, understood! I've noted this for future consideration. Feel free to revisit the refactoring when it fits better into your development schedule.


🧠 Learnings used
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1423
File: wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt:418-446
Timestamp: 2025-08-25T14:48:39.247Z
Learning: HashEngineering prefers to refactor and reuse topup code for balance validation logic improvements in DashPay activities like EditProfileActivity, rather than implementing individual fixes in the current PR.

Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1422
File: common/src/main/java/org/dash/wallet/common/util/Constants.kt:68-69
Timestamp: 2025-08-25T15:00:20.777Z
Learning: HashEngineering prefers to keep HTTP logging enabled in release mode (using log.info instead of gating with BuildConfig.DEBUG) to debug production errors, even though this may leak URLs in production logs. This is a deliberate trade-off for debugging purposes in the Dash wallet project.

Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1410
File: wallet/src/de/schildbach/wallet/data/InvitationLinkData.kt:79-84
Timestamp: 2025-07-12T07:12:04.769Z
Learning: HashEngineering prefers to handle defensive validation improvements for URI parsing in follow-up PRs rather than including them in the current PR when the main focus is on replacing Firebase with AppsFlyer.

Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1450
File: docs/security/FALLBACK_TESTING_GUIDE.md:408-428
Timestamp: 2026-01-12T20:56:41.213Z
Learning: HashEngineering prefers to place testing utilities like FallbackTestingUtils in debug-only source sets rather than using ProGuard rules to strip them from release builds. This ensures test code is never compiled into production builds by the build system itself.

Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1425
File: wallet/src/de/schildbach/wallet/ui/more/SettingsScreen.kt:122-151
Timestamp: 2025-09-05T06:47:44.508Z
Learning: HashEngineering prefers to defer UI edge case fixes like the CoinJoin finishing state display issue (showing "Finishing ...%" when mode is OFF but status is FINISHING) to later PRs when the main focus is on redesigning the settings screen.

Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1422
File: wallet/src/de/schildbach/wallet/data/PaymentIntent.java:476-481
Timestamp: 2025-08-25T15:04:18.461Z
Learning: HashEngineering prefers to keep payeeUsername and payeeUserId in PaymentIntent.toString() logs unredacted for debugging purposes, prioritizing troubleshooting capability over PII protection concerns in the Dash wallet project.

Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1425
File: common/src/main/java/org/dash/wallet/common/ui/components/DashButton.kt:94-101
Timestamp: 2025-09-05T06:00:25.088Z
Learning: HashEngineering prefers to fix the DashButton alpha dimming issue (using explicit disabled colors instead of overall alpha) in a follow-up PR rather than including it in the current settings screen redesign PR.

Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1417
File: features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/MerchantDao.kt:0-0
Timestamp: 2025-08-08T16:48:49.964Z
Learning: In PR dashpay/dash-wallet#1417, HashEngineering chose to defer adding Room indexes for gift_card_providers (provider, denominationsType, merchantId) to a follow-up PR; do not block the current PR on this optimization. Files: features/exploredash/.../data/explore/MerchantDao.kt and features/exploredash/.../data/dashspend/GiftCardProvider.kt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant