Merged
Conversation
Performance: - WalletManager: add getAddressBatch() for single Keystore decrypt per N addresses - RavencoinPublicNode: add getTotalBalance(), getTotalAssetBalances(), getAddressStatusBatch(), getAssetMetaBatch() for pipelined batch TLS calls - discoverCurrentIndex(): batch size 20 with getAddressBatch + getAddressStatusBatch (was 5 individual calls each) - sweepOldAddressesInternal(): use getAddressBatch + getAddressStatusBatch instead of per-address calls - loadOwnedAssets(): pre-fetch all IPFS hashes in one batch RPC call, raise Semaphore from 3 to 8 - WalletPollingWorker: use getAddressBatch + getTotalBalance + getTotalAssetBalances - NfcCounterCache: lazy init to avoid blocking main thread at startup - CONNECT_TIMEOUT_MS: 12000ms -> 5000ms Bug fixes: - Asset issuance: owner token (ROOT!, ROOT/SUB!) was sent to toAddress instead of changeAddress; if toAddress was an external recipient the brand permanently lost sub-asset issuance rights - Asset issuance: redirect toAddress to nextAddress when issuing to own wallet, asset lands quantum-safe immediately without extra sweep tx - WalletPollingWorker: trigger sweepOldAddresses() on any detected incoming transfer, consolidates HAS_OUTGOING addresses in background (app closed) - loadWalletInfo(): run discoverCurrentIndex() when balance is null (fixes Brand app showing 0 balance with same mnemonic as Consumer) - Remove redundant loadOwnedAssets() call from initWallet()
Three related bugs fixed: 1. discoverCurrentIndex() was resetting currentIndex to 0 when the first batch failed (network/Keystore unavailable at process restart). Chain: getLocalBalance() fails -> balance==null -> discoverCurrentIndex() triggered -> getAddressBatch() returns emptyMap() -> loop breaks with lastUsed=-1 -> setCurrentAddressIndex(0) -> next balance check returns 0 RVN. Fix: if first batch is empty, return stored index unchanged. Also never decrease the stored index (transient failure != rollback). 2. loadWalletInfo() reset walletInfo to balanceRvn=0.0 at the start of every load, flashing 0 on screen during reload. Fix: preserve existing balance/address while loading; only replace with new data on success, keeping stale data visible if the network call fails. 3. initWallet() was called unconditionally from onCreate(), relaunching all loading coroutines even when the ViewModel already had data (e.g. after a screen rotation where the Activity is recreated but the ViewModel survives). Fix: skip loadWalletInfo() if walletInfo != null. Also: keep asset preview images on refresh (IPFS content is immutable, no need to clear and reload images already in memory or disk cache).
…r/issue sendRvnLocal, transferAssetLocal, issueAssetLocal now use at most 2 TLS connections for all UTXO data (down from N+2 sequential connections) and a single Keystore decrypt for both signing keys (down from 2 decrypts). - Add getUtxosAndAllAssetUtxosBatch() to RavencoinPublicNode: fetches the full UTXO list in 1 TLS connection, then batch-fetches all raw transactions needed for asset classification and script extraction in a second TLS connection, regardless of how many assets are held. - Add getKeyPair() to WalletManager: single getSeed() call returning both private and public key bytes, saving ~250ms per transaction. - Convert sendRvnLocal, transferAssetLocal, issueAssetLocal to suspend fun and parallelize UTXO fetching with fee-rate lookup using async. With 19 assets: ~43 TLS connections reduced to ~3, wall time ~13s to ~2s.
During restore/first load, assetsLoading becomes true only after loadOwnedAssets() is called, leaving a brief window where the UI showed 'nessun asset' with no spinner even though walletInfo.isLoading was true. The spinner next to 'My Assets' now also triggers on walletInfo.isLoading, and the 'nessun asset' empty state is suppressed while the wallet is loading.
…ition lag WalletScreen is an expensive composable (asset cards, scroll state, dialogs). The previous when(currentTab) pattern destroyed it on every tab switch and rebuilt from scratch on return, costing more than one frame. WalletScreen is now kept in the Box composition tree after the first visit: - alpha(0f) hides it when inactive without removing it from the tree - pointer-blocking overlay prevents the invisible scrollable content from intercepting touches meant for the active tab - Other tabs (Scan, Brand, Settings) continue to use the when branch since their LaunchedEffects must only fire when actually visited
RavencoinTxBuilder - buildAndSignRvnSendWithAssetSweep: - RVN change (P2PKH) was placed after OP_RVN_ASSET outputs, violating Ravencoin consensus ordering: all P2PKH outputs must precede asset outputs. Consequence: node accepted the tx but ignored asset transfers, RVN moved but assets stayed on the old address. RavencoinTxBuilder - buildAndSignMultiAddressSend (new function): - Same P2PKH-after-OP_RVN_ASSET ordering bug, now fixed. - Dust hardcoded to 600 sat for every asset output regardless of input dust. Ravencoin requires: if input asset UTXO has 0 sat, output must also be 0 sat (assets issued with dustOut=0 would trigger bad-txns-asset-transfer-amount and the tx would be rejected, assets not moved). Fixed: dust derived from input UTXO satoshis per asset. WalletManager - sweepOldAddressesInternal: - Funding transaction used hardcoded fee of 2000 sat, far below minimum relay fee (200+ sat/byte * ~226 bytes = 45000+ sat). Funding tx was rejected and asset-only addresses could not be swept. Fixed: fee calculated from getMinRelayFeeRateSatPerByte(). WalletManager - healAndSweepTarget: - Funding transaction signed with the TARGET address key pair (index being swept) instead of the CURRENT address key pair (owner of the UTXOs being spent). Invalid signatures caused the funding tx to be rejected every time. Fixed: separate getKeyPair(currentIndex) used for the funding tx, zeroed in finally block. Fee also now calculated dynamically from satPerByte.
Two UI bugs caused the wallet to show 0 balance and no assets after a network hiccup, even when data had already been loaded: 1. loadWalletBalance: when both ElectrumX and the backend API failed, balance was silently reset to 0.0, overwriting a previously correct value. Now falls back to the last known walletInfo.balanceRvn. 2. WalletScreen: when assetsLoadError=true, the error card was shown INSTEAD of assets, hiding 19 cached assets after a failed second loadOwnedAssets call. Error card now only shows when ownedAssets is also empty.
Five concrete bugs fixed: 1. transferAssetLocal scanned only currentIndex for assets. If assets were on an old HAS_OUTGOING address (not yet swept), the transfer failed or only RVN moved. Now scans all 0..currentIndex addresses and includes every found asset UTXO in the transaction, each signed with its own address key via the new buildAndSignMultiAddressAssetTransfer. 2. refreshBalance launched healAndSweepTarget for every old address IN PARALLEL, creating dozens of simultaneous ElectrumX TCP connections that triggered server-side connection resets. Replaced with a single sequential sweepOldAddresses call. 3. sweepOldAddressesInternal funded asset-only addresses FROM the current clean address, exposing its private key and defeating post-quantum protection. That early funding block is removed; the correct sacrificial (HAS_OUTGOING) funding path (fundOldAddressForSweep) remains. 4. getLocalBalance returned null for genuinely empty wallets (takeIf > 0), triggering discoverCurrentIndex on every startup even when the index was already correct. Now returns 0.0 for empty and null only on network failure. 5. loadWalletInfo triggered discoverCurrentIndex whenever balance was null, which also fired on transient network errors. Now discovery only runs when currentIndex == 0 (wallet just restored from mnemonic).
…address checks - Replace reconcileCurrentAddressIndex() with simpler forward-only index verification - Remove ensureCurrentAddressClean() calls from wallet loading flow - Streamline address index management to post-quantum safe forward progression only
- Enhanced loadOwnedAssets() to detect funds (RVN + assets) scattered across old addresses - Added consolidateAllFundsToFreshAddress() in WalletManager to move all funds to a virgin address (currentIndex + 1) - Added consolidation banner in WalletScreen UI when funds detected on old addresses - Implemented consolidateFunds() in MainActivity to trigger the consolidation process - Portfolio scan now checks both asset balances and RVN balances in parallel - Consolidation transfers all assets and RVN to a fresh, quantum-safe address and advances the index This solves the issue where the portfolio scan wasn't finding funds on old addresses that needed to be consolidated.
- consolidateAllFundsToFreshAddress() now uses existing sweepOldAddresses() logic - Old addresses with HAS_OUTGOING status are properly funded via sacrificial address - Two-phase approach: (1) sweep old addresses to currentIndex, (2) sweep currentIndex to fresh - Handles case where only currentIndex has funds (direct sweep to fresh) - Properly filters asset UTXOs from RVN UTXOs for fee calculation - Respects post-quantum safety by only consolidating HAS_OUTGOING addresses
- Refactored consolidateAllFundsToFreshAddress to be atomic and multi-address - Optimized Keystore usage with getAddressBatch and getKeyPairBatch - Increased network throughput by adjusting OkHttp dispatcher settings - Enabled full-range address scanning without arbitrary limitations
…exposed On mnemonic import, currentIndex now points to the address where funds actually reside. If that address has never signed an outgoing transaction (public key not yet revealed), currentIndex stays there and sweep is a no-op. Only if the address has HAS_OUTGOING status is currentIndex advanced by one, triggering a sweep to a fresh address. Removes the old unconditional +1 and the follow-up HAS_OUTGOING skip loop that were causing unnecessary fund consolidation on import.
During restoreWallet, hasWallet is now forced to false immediately so the WalletSetupCard stays visible (with spinner) for the entire duration of discoverCurrentIndex. Address and balance are shown only after the correct blockchain index is resolved. Added restoreError state to surface invalid-mnemonic and network errors inside WalletSetupCard instead of the invisible BalanceCard. Added walletScanningBlockchain string (9 languages) shown below the spinner during blockchain scanning.
…0-address scan
discoverCurrentIndex: Phase 2 now uses a single getAddressesWithFunds batch call
(get_balance?asset=true pipelined) instead of N*2 sequential TLS calls per address
with history. With 15 addresses with history this drops from 30 sequential calls to
one batch (2 TLS connections). Estimated 3-5x speedup on mnemonic import.
sendRvnLocal: old-fund discovery now scans only 0..currentIndex-1 (not hardcoded
100) and uses getAddressesWithFunds to pre-filter before fetching full UTXOs.
Eliminates 100 sequential TLS calls on every send when currentIndex is low.
sweepOldAddressesInternal: converted to suspend fun, replacing runBlocking{delay()}
with proper coroutine delay(). Thread is now suspended instead of blocked.
loadOwnedAssets: consolidation check for old addresses replaced from two full
getTotalAssetBalances+getTotalBalance calls to one lightweight getAddressesWithFunds
batch call.
New: RavencoinPublicNode.getAddressesWithFunds(addresses) - single batched
get_balance?asset=true returning Set<address> of addresses with any funds.
…up display On first load after startup, loadOwnedAssets now reads the previous session's asset list from SharedPreferences and shows it immediately (no network wait). The network fetch still runs in parallel and replaces the cache once fresh data arrives. The cache is keyed by wallet address, so it auto-invalidates on wallet change. Images and IPFS metadata are preserved across sessions via the existing merge logic that already carried over enriched fields.
Enrichment jobs now use async instead of fire-and-forget launch. After all enrichments complete, the cache is saved again with imageUrl and description populated, so images appear immediately on the next startup without a network round-trip to IPFS.
Satoshis carried by other-asset UTXOs were added as inputs but not counted in totalIn, causing the miner to receive extra fee instead of the surplus going to the RVN change output at currentIndex+1.
Some Ravencoin ElectrumX servers return asset balances via get_balance?asset=true but omit asset UTXOs from listunspent. Three fixes: 1. getAddressesWithFunds: parse top-level confirmed/unconfirmed as primitives (RVN balance), then nested objects as asset balances. Previously only checked nested JsonObjects, missing addresses that had only RVN dust from asset UTXOs. 2. getUtxosAndAllAssetUtxosBatch: secondary asset check after listunspent. If no asset UTXOs found, falls back to getAssetBalances + getAssetUtxosFull to retrieve them explicitly. This ensures sendRvnLocal enters the atomic branch and includes all assets in the same transaction. 3. buildAndSignAssetIssueWithAssetSweep: include satoshis from swept other-asset UTXOs in totalIn so they go to RVN change instead of miners.
When both consumer and brand apps share the same HD wallet, one app advancing currentIndex (via a send tx) left the other showing the stale address until mnemonic re-import. Adds syncCurrentIndex() in WalletManager: lightweight 3-batch-call check that detects if the stored index is stale (current address has HAS_OUTGOING status) and scans forward up to 10 addresses to find the correct position. Much faster than full discoverCurrentIndex(). refreshBalance() now calls syncCurrentIndex() first and updates the displayed address immediately if the index changed, before reloading balance, assets, and transaction history.
On onPause sets a flag; on onResume calls refreshBalance() if the wallet exists. This ensures address, balance, and asset list are current when switching between app flavors or returning from background, without waiting for the 60-second polling loop.
assetsLoading was set to true unconditionally at the start of loadOwnedAssets, causing the spinner to appear on every background refresh even when assets were already visible. Now the spinner only shows when there is nothing to display yet (empty list and no cache). Subsequent refreshes update the list silently in the background.
7 structured documents covering stack, integrations, architecture, structure, conventions, testing, and technical concerns.
…, tappable txid Add MultiStepProgressIndicator, StepRow, PreIssuanceWarning, ConfirmationProgressRow composables. Gate SubmitButton on IssueStep.Idle. Add currentStep/issuedTxid/warningType parameters to IssueAssetScreen signature.
Add pollingLoop for N/6 confirmations after issuance. Enhance processIssueAndWrite with step state transitions, classifyIssuanceError, retryWithBackoff (SocketTimeout excluded per D-08), and post-flow polling. Update onTagTapped to set IssueStep.Failed on error.
…d UI strings - Convert FR/DE/ES to cloneStrings(stringsEn) for safe English fallback - Add 88 missing translations to FR/DE/ES (Phase 30-40 strings) - Add 92 missing translations to ZH/JA/KO/RU - Fix RU authSubtitle (was Chinese, now Russian) - Replace hardcoded strings in WalletScreen, RegisterChipScreen, BrandDashboardScreen, SplashScreen with LocalStrings references - Add 17 new AppStrings properties for hardcoded UI strings - All 9 languages now have 456/456 properties - Fix Italian hardcoded strings (Asset ciclati, Chiudi)
…le across 9 languages - Add walletCycledMultiAsset format string with translations in EN/IT/FR/DE/ES/ZH/JA/KO/RU - Add walletCycledAssetsTitle translations for 7 missing languages (FR/DE/ES/ZH/JA/KO/RU) - Fix "insufficient RVN" warning flashing on startup by guarding on walletInfo.isLoading
…over Seed balance/block-height/tx-history from SQLite cache on cold start so the wallet screen renders instantly instead of showing "Loading…". Change balanceRvn from Double to Double? so the UI distinguishes "not yet loaded" from an actual zero balance. Reduce ElectrumX connect timeout from 5s to 2.5s and persist last-good-host across restarts to skip failover rotation on resume. Also optimistically deduct sent amount after send for instant balance update before network confirms.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Server instance captured in `const server` for graceful shutdown. unhandledRejection closes HTTP server then SQLite, exits with code 1. uncaughtException logs stack trace and exits immediately (unsafe state).
…in getAssetHierarchy Concurrency limited to 5 per chunk. Failed sub-branches add entry to errors array with assetName and error. Response includes partial: true when any branch fails.
…hy route listSubAssets and getAssetHierarchy now accept optional limit (default 200) and offset (default 0) params. Hierarchy route adds response envelope with total, limit, offset, hasMore. Backward compatible with existing clients.
startLogCleanup() deletes rows older than 30 days, runs every 24h and once at startup. nfc_counters explicitly excluded (anti-replay security). Migration 6 renamed to log_retention_cleanup_one_shot with reference to the new periodic cleanup in logger.ts.
…ckup Node.js backup uses better-sqlite3 .backup() for consistent WAL snapshots before encryption. Docker backup container updated to use sqlite3 CLI .backup command, 6h interval (was 24h), keep 3 backups (was 7).
`npm run db:explore` opens a REPL with pre-built domain commands (.assets, .brands, .revoked, .stats, .help). Database opened in read-only mode. No write operations exposed.
All 6 plans executed. Verification passed (22/22 must_haves). Backend now has graceful crash handling, parallelized hierarchy, pagination, periodic log cleanup, WAL-safe backups, and DB explorer.
Move 5 backend requirements to Validated section. Update last-updated date.
30 plans across 5 phases (10-50). Archives roadmap, creates MILESTONES.md entry, full PROJECT.md evolution, retrospective, and reorganized ROADMAP.
|
🤖 Hi @ALENOC, I've received your request, and I'm working on it now! You can track my progress in the logs for more details. |
|
🤖 I'm sorry @ALENOC, but I was unable to process your request. Please see the logs for more details. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
.planning/dal tracking git, aggiunta a.gitignore.claude/a.gitignoreTest plan
.planning/no longer appears in repo.claude/no longer appears in repo.gitignoreentries are effective