Block Filters Are Now the Default Sync Mode — The wallet now syncs the chain using BIP 157/158 compact block filters by default. Unlike bloom filters (which the wallet historically used, and most SPV wallets still do), compact filters never tell peers what addresses your wallet is watching. The peer sends you a small encoded filter for each block, the wallet checks every filter locally, and only requests the full block when there’s a hit. Your address set stays on your phone. A pool of opt-in DigiByte full nodes serves these filters — we crawl for filter-capable peers, deliver them through our seeder API, and route through them automatically; bloom mode remains available in Settings → Sync Mode for users who prefer maximum peer compatibility.
Graceful Bloom Fallback — Filter-serving peers are a smaller pool than bloom-serving peers right now. If the wallet can’t make filter progress within 120 seconds (Tor circuit failures, all filter peers down, transient outage), it automatically falls back to bloom for the rest of the session and surfaces an amber “Privacy degraded for this session” banner on the wallet home so you know what happened. Next app launch tries filters first again, so a brief outage doesn’t permanently downgrade your privacy. No silent privacy loss, no permanent hang on connection problems.
Block-Filter Decoder + Wire Driver Fixes — Three bugs in the C core’s BIP 158 implementation prevented end-to-end filter sync from working against real peers. The GCS decoder rejected every honest filter due to a strict end-of-stream check that didn’t accept BIP 158’s mandatory byte-alignment padding (bytePos < len flagged trailing padding bits as excess data). The cfheaders driver only fired at peer-connect time, so on a fresh boot where the checkpoint height was below the auto-fetch start, it bailed once and never re-tried after headers caught up. And lazy chain initialisation hardcoded startHeight=0, anchor=ZERO instead of anchoring at the configured wallet birth height with the peer’s claimed previous filter header. Verified end-to-end against a live filter peer: cfheaders requested, chain extended, cfilter MATCH on the exact block containing a real test payment, full block downloaded.
DigiAsset Send Is Live — The Send button on the asset detail screen now actually sends. Enter a recipient DigiByte address and a quantity (respecting the asset’s divisibility — too-precise input is rejected rather than silently truncated), tap Review & Send, confirm. The UI shows a spinner during broadcast, auto-dismisses the dialog on result, then a banner at the top of the screen displays either the transaction id on success or a specific error on failure. End-to-end wiring across the encoder, coin selector, native build & sign JNI, and publishTransaction that have been landing in previous releases.
Doze-Frozen Keepalive Fix — The peer-keepalive respawn we shipped in v3.5.20 only fired when the Job was cancelled. In practice an overnight Android Doze pause can freeze a coroutine inside delay() without cancelling it — Job reports active, respawn never fires, peers stay at 0. Fix: the keepalive stamps a tick watermark every 10 seconds; if onStartCommand observes a stale watermark (>60s since last tick) it force-cancels and relaunches. Observed live on the emulator: 7-hour Doze pause would previously have required the user to force-stop the app.
Assets Empty State Points to digiscope.me Issuance — When a user opens the Assets tab with no DigiAssets, there’s now a prominent callout linking to digiscope.me/assets/create. With the two public DigiAsset issuance tools (digiassetX.com, diginexum.trade) currently offline, asset creation is being brought online on our own infrastructure so users aren’t blocked. The wallet stays intentionally out of issuance — that’s a web concern with account auth, IPFS pinning, and user-deposit handling that belongs server-side.
DigiAsset Send Engine Under the Hood — The next layer of DigiAsset send lands: a new native bridge buildAndSignAssetTransferTx that assembles a full DigiAsset transfer from pre-selected UTXOs and pre-constructed outputs (recipient 700-sat marker, OP_RETURN with the transfer payload, DGB change), signs with the wallet’s own BIP84 keys, and returns signed tx hex. AssetManager.sendAsset() is no longer a stub — it ties the coin selector, the transfer encoder, native signing, and broadcast into one cohesive flow with typed error results for every failure mode. The send button UI still shows “Coming soon” — final UX wiring (input validation, confirmation dialog, broadcast progress) lands next release so each layer stays testable in isolation rather than being entangled with the UI.
DigiAsset Send Foundation (Encoder + Coin Selector) — Pure-logic prerequisites for sending DigiAssets land in this release, with rock-solid test coverage including byte-exact matches against real on-chain mainnet DigiAsset transactions. The BitWriter (inverse of the existing BitReader) handles MSB-first bit packing and SFFC fixed-precision amount encoding. The DigiAssetEncoder assembles TRANSFER opcode payloads — verified to produce identical bytes to a real block-11,000,208 mainnet transaction, and the other gold fixtures from the decoder test suite. The AssetCoinSelector picks minimal input sets for a transfer: asset UTXOs summing to the send quantity, plus only the DGB UTXOs needed to cover the per-recipient marker outputs and network fee, correctly accounting for the 700-sat marker value that asset inputs contribute. No user-facing asset send button yet — the remaining native transaction-building JNI and UI wiring ship in follow-up releases, intentionally staged so each correctness layer is verifiable in isolation.
Multi-Endpoint DigiAsset Data Layer — The wallet now resolves DigiAsset metadata through a sovereignty-first multi-endpoint client: it tries digiscope.me (our own cert-pinned proxy to a locally-hosted digiasset_core instance), then falls through to api.digiassets.net/v3 (the official endpoint maintained by the DigiByte Core team), with a per-endpoint circuit breaker borrowed from RenzoDD’s digibyte-js pattern — three consecutive failures open the circuit for 60 seconds, any success resets it. Reduces single-point dependency and means asset icons and metadata still load even when public IPFS gateways are slow or unreachable. Backend gains six read-only proxy routes (/api/assets/*) passing through to digiasset_core over loopback Basic auth, protected from accidental deploy-strip by a prominent comment block. Future releases will add user-configurable endpoints for full sovereignty.
Per-Asset Transaction History — Opening an asset’s detail screen now shows only that asset’s transactions instead of the full asset-activity firehose across every DigiAsset you hold. Under the hood the transactions table gains a nullable assetId column (Room schema v5); a one-shot backfill worker runs on first launch after the upgrade and populates it two ways: a fast SQL join against the UTXO table (already carries asset_id per row), then a Kotlin fallback that decodes the raw OP_RETURN for any asset-tx rows whose UTXOs were already spent and pruned. SharedPreferences flag blocks re-runs, a Mutex serialises concurrent SyncService restarts, and a no-progress guard prevents infinite loops on undecodable edge rows. Also fixes a latent bug where the detail screen’s history viewmodel snapshot-read the selected asset ID once at construction (always null) and never updated — history now re-collects reactively when you navigate between assets.
DigiAsset Images Now Render — The Assets list and asset-detail screens previously showed a colored letter circle for every DigiAsset regardless of whether the asset had a real icon on IPFS. Wallet now loads the actual image via Coil, routing ipfs:// URIs through the existing multi-gateway hash-verifying IpfsClient (so a malicious gateway returning the wrong bytes is caught at the CID-multihash layer, not silently rendered). HTTPS-hosted asset images reuse the Tor-aware app-wide OkHttp client. The letter fallback stays beneath the image slot — on any load failure (unreachable gateway, verification mismatch, no metadata) the letter remains visible so the UI never breaks. Also handles all four wild-west imageUrl shapes we see in the field: full HTTPS URLs, ipfs://<cid>, /ipfs/<cid>, and bare CIDv0/v1.
Synced-State Grace Window — Headers can reach the chain tip while the per-block merkleblocks matching the wallet’s bloom filter are still being delivered. The sync loop was flipping the UI to “Connected” the instant header height caught up, freezing users on a stale balance (they’d see a previous balance that didn’t yet reflect a spend whose block hadn’t been scanned). The service now requires 30 seconds of stable at-tip observation before flipping to Complete. If a new block arrives and the wallet falls behind mid-grace, the counter resets cleanly. Prevents “wallet says I’m synced but my balance is wrong” without impacting steady-state sync time.
Scan for Missing Funds Now Prunes Stale UTXOs — The reconciliation backend had always returned every tx that ever touched the wallet’s addresses (via the ElectrumX scripthash history), but the Android client was only feeding the parents of currently-unspent outputs into the native tx processor. That meant Scan for Missing Funds fixed “missing receives” but couldn’t fix “balance too high because SPV missed the block that contained your spend”. The client now passes every returned tx through BRWalletRegisterTransaction, so spending txs properly debit consumed inputs. Scan is now symmetric — corrects both directions of SPV data loss.
Stable Sync Progress Percent — The sync-progress percent is now anchored to the authoritative chain tip from the digiscope.me full node (new /api/chain/tip endpoint, 5-second cached). Previously the denominator was SPV’s peer-negotiated estimated_height which churns every time a new peer arrives with a different tip claim, causing the percent to jump back and forth (“40% → 99% → 60% → 99%”) during early sync. Honest math, confusing UX. Now: the wallet fetches the real tip every 30 seconds, takes the max against the peer-quorum value, and uses it for both the percent and the “Block X of Y” readout. On network failure the stored tip is left unchanged so a momentary blip doesn’t regress the denominator to zero. Users see a monotonically climbing number.
Keepalive Resurrection — Fixed the “wallet stuck at 0 peers forever” state that could still bite after extended idle even with the v3.5.13 UI watchdog. Root cause: the watchdog’s startForegroundService kick deduplicates into the already-running service, and the service’s early-return guard on repeat onStartCommand was skipping keepalive setup. If the original keepalive coroutine had died silently (Doze, an unhandled JNI throwable, etc), the service stayed nominally alive with its foreground notification while no one was reconnecting peers — watchdog kicks all arrived as no-ops. The keepalive is now tracked as a Job and respawned on every onStartCommand that sees it inactive. Observed in the wild after 20 hours idle, 827 fruitless watchdog kicks.
PIN-Confirm ANR Fixes — Three separate JNI/Keystore call paths were landing on the main thread during wallet creation and recovery, and on returning to the app after sockets had dropped. Moved clearPin (Keystore-backed) into the worker dispatcher, and moved startSync (opens BRPeerManager sockets, can block for seconds) off Main in both the post-PIN success callback and MainActivity.onResume’s defensive reconnect. Removes the Android “Wait / Close” dialogs that have been interrupting onboarding on slower hardware and emulators.
Sync-Progress UI Dedup — The wallet was showing two separate sync percentages (one in the header, one in the body) reading from flows that updated on different ticks — they visibly bounced against each other (“40% ↔ 99%”) as peer chain-tip estimates churned mid-sync. The verbose card in the body is now the single source of the percentage; the header shows connectivity state plus peer count plus block height with no percent. The DigiRunner mini-game overlay no longer renders its own third progress bar either. One number, one place.
Keyboard-Safe Seed Entry — The seed-phrase entry grid now actively scrolls the focused field into view when the soft keyboard appears. imePadding alone reserves space at the bottom but leaves lower-row fields hidden behind the keyboard; the new BringIntoViewRequester attached to each field ensures whichever one you tap scrolls up above the keyboard before you start typing.
Faster Address Format Toggle — Receive screen pre-derives both the legacy (D...) and bech32 (dgb1q...) addresses once on first composition, so switching the format chip no longer re-runs the JNI key-derivation path on every tap. Instant toggle, zero work per click.
Lifecycle-Aware State Collection — Swept 14 call sites across eight screens from collectAsState() to collectAsStateWithLifecycle(): Network Info, Scan for Missing Funds, Security & PIN, Display, Recovery Scan, Recovery Date, Seed Display, and the top-level navigation wallet-state collector. Prevents the ViewModels behind those screens from doing unnecessary work when the wallet is in the background — small battery and memory win on every one of these views, plus future-proofs the screens against regressions where a flow emits rapidly while off-screen.
Seed-Isolation Test Allow-list Refreshed — Internal plumbing: the security test suite that audits the JNI surface for seed-leaking method signatures is now aware of the mnemonicToSeed and derivePrivateKeyWIF paths introduced by Universal Restore in v3.5.15. Each allow-list entry carries a one-line rationale so future contributors understand why the narrow exception is safe. No runtime behavior change.
Auto-Reconcile on Upgrade — Every version bump now silently runs the “Scan for missing funds” flow in the background, once, after peers stabilize. Any UTXOs that earlier SPV windows missed — stale bloom peers, cracked filter runs, pre-Universal-Restore BIP84-only scans — are pulled in automatically. You no longer need to know Settings → Recovery exists to recover balance that went missing between releases. The wallet waits 10 seconds after unlock for peer connectivity to settle, then queries the authoritative UTXO set for every address it derives (400+ across all derivation paths) and imports any receives the SPV bloom scan missed. Gated by versionCode so the node is only contacted once per release.
Keyboard-Safe Text Inputs — Fixed the soft keyboard overlapping the seed-phrase grid on the Recover Wallet screen, and preemptively applied the same fix to the Send, Receive, Asset Send, Scan for Missing Funds, and Hub profile editor screens. Edge-to-edge rendering in newer Compose was short-circuiting the window-level “adjust resize” behavior; each affected screen now declares imePadding directly so the form shifts up cleanly when the keyboard appears.
Send-Screen Peer State Sync — Fixed a UX bug where tapping Send from the Wallet screen briefly showed a “not connected” banner even when the Wallet and Network screens were already reporting five connected peers at chain tip. Each screen was spinning up its own WalletViewModel with a peer count initialized to zero until its own 5-second poll caught up. The Send, Receive, and Transaction Detail screens now share the Wallet route’s ViewModel — peer count, sync state, balance, and transaction list are live the instant you navigate in.
Universal Restore — Seed recovery now probes every derivation path DigiByte wallets have historically used: BIP84 native DGB, legacy m/0H with both the “DigiByte seed” and “Bitcoin seed” HMAC seeds, BIP44 DGB, BIP44 wrong-coin (m/44'/0'/0'), and BIP49 P2SH-P2WPKH — all scanned in parallel against the reconcile backend before you pick a recovery date. If any non-native path holds funds, the wallet auto-sweeps them into the new BIP84 wallet after recovery completes, so legacy users who drifted across derivation standards don’t have to hunt for old addresses manually. Gap limits bumped to 200 external / 100 internal per profile to cover heavily used chains. BIP49 funds are surfaced with a “manual recovery needed” hint (automated P2SH-P2WPKH sweep deferred; BRTransactionSign doesn’t yet handle P2SH redeem-script scriptSig).
Rotating Bloom Peer Pool — The wallet now maintains a growing pool of every bloom-serving peer it has ever been told about, rather than replacing the list on each seeder fetch. Reconnect cycles rotate through the pool so the same flaky 20 peers aren’t hammered for an hour before a re-fetch. The bloom seeder at api.digiscope.me is only contacted when the pool is stale (>1h) or empty — transient seeder outages no longer degrade wallet connectivity.
Process-Death Watchdog — Fixed the long-standing “wallet stopped syncing after phone was idle” stall. Root cause identified: Android was reaping the entire app process under memory pressure (or aggressive OEM battery management on Samsung / Xiaomi / OPPO), and the sync service never recovered even though the system was supposed to restart it. The UI now runs a 5-second poll that detects sustained zero-peer state and re-launches the sync service directly — the UI process usually survives the reap, so it can act as a second line of defense. Also hardened the notification channel priority and category so OEM killers classify the sync service as a persistent background task instead of a reap candidate.
Scan for Missing Funds — New Settings → Recovery flow that queries a DigiByte full node for the authoritative UTXO set on every wallet-derived address, then injects any receives the SPV bloom scan missed directly into the wallet. Recovers balances stuck in the “showed up for a second then disappeared” state when peers drop merkleblocks or the native rescan no-ops against stale tips. Defaults to the cert-pinned api.digiscope.me endpoint; advanced users can point it at their own DGB node for full sovereignty — only public addresses are sent, never any keys.
Stale-Peer Sync Guard — Hardened the onSyncComplete native callback against stale peers that report a chain tip below the hardcoded checkpoint floor. Previously a peer announcing an old tip could latch the wallet into the “Synced” state even though the real chain is far ahead; the rescan would then stop and transactions in the unscanned range would never be found. Now both the poll loop and the JNI callback refuse to accept a completion signal below the checkpoint floor and let the bloom scan keep running until a real tip peer appears.
Recovery Year Labels Fixed — The year picker on the seed-restore date screen was off by one (the “2026” option wrote a 2025 timestamp; “2025” wrote a 2024 timestamp; etc). Users selecting their actual wallet creation year were getting scans starting one year too late, silently missing older transactions. Timestamps now match the labels.
Historical Sends Preserved — Fixed a long-standing issue where old send transactions appeared in history on app launch and then vanished the moment the wallet finished syncing. The mempool-cleanup path that fires at chain tip was removing saved transactions marked as unconfirmed (a byproduct of the transaction-serialization format not persisting block heights). The cleanup now spares any transaction the wallet has a stake in — whether we spent UTXOs producing it or received coins from it — regardless of whether peers are still relaying it. Balance was already correct for affected users; this restores the transaction-history display.
Peer Connectivity Guardrails — Send and Receive screens now show a clear banner when the wallet has zero connected peers, and the Review & Send button is disabled until connectivity is restored. Prevents the footgun of broadcasting a transaction into a disconnected peer manager where it silently strands locally.
Defensive Reconnect on Resume — Returning to the app from an extended background now explicitly re-kicks peer connection when peerCount is zero, independent of the 10-second background keepalive. The keepalive itself is also hardened against silent coroutine death. Fixes the “reopen the app, still 0 peers” state users have hit after long sleeps.
Upgrade Path Restored — From v3.5.9 on, signing lineage (APK Scheme v3) allows existing debug-key installs to update to release-signed builds without uninstalling first.
In-App Update Notifications — Releases publish as stable so the built-in UpdateChecker surfaces an update dialog when a newer version ships.
Send Transaction Reliability — Four-stage fix for sent transactions disappearing after sync in dual-key wallets. Saved txs are now bulk-added ahead of sync, parent txs are preserved when hasLegacyKey is set, the forced rescan on BIP84 upgrade is removed, and mempool cleanup is deferred until chain tip. Your send history stays intact across restarts.
BIP84 Upgrade Path — Crash-free upgrade from v3.4.0. The dual-key wallet registers all addresses (BIP84 + legacy) before processing saved transactions. About screen now correctly labels Wallet Type as “HD Wallet (BIP84)” to match the m/84'/20'/0' path shown below it.
BIP84 Standard Derivation — Addresses match Ian Coleman's BIP39 tool. Standard “Bitcoin seed” HMAC + m/84'/20'/0' path. Dual-scan recovery finds funds from both new and legacy key trees.
Bloom Peer Retention — Non-bloom gossip peers filtered from pool. All connection slots go to verified bloom peers. Zero SPV rejections.
Sync Stall Recovery & Tor — Download peer reassignment every 10s prevents stalls. All traffic routable through Tor SOCKS5 with DNS leak prevention.
What Works
Full SPV wallet — send, receive, QR scanning (mainnet tested)
Digi-ID authentication with real cryptographic signing
One-tap DigiScope login (no QR scan needed)
Community Hub — chat, forum, Enigma AI bot
DigiRunner mini-game with cross-platform leaderboard
Block & transaction persistence — balance spendable on restart
PIN lock when app backgrounds
Bloom peer discovery — 19 peers from network seeder, re-injected on reconnect
Tor privacy routing — all traffic through SOCKS5, toggle in Settings
Known Limitations
DigiAssets — detection only, send/receive NOT ready
Tor routing — functional with SOCKS5 IPv4/IPv6, DNS leak prevention, dead proxy recovery. On-device DNS leak audit pending.
Node Operators: Enable Bloom Filters
This wallet uses SPV with bloom filters to sync without downloading the full blockchain. Most DigiByte v8.26 nodes have bloom filters disabled by default.
Enable bloom filters on your node to support mobile wallets: