docs: archive stale/dormant-feature markdown out of git tracking
Move 8 dated-snapshot / dormant-feature docs to docs/_archive/ (git-ignored,
kept locally), leaving only repo essentials + the active lite plan tracked:
- docs/codebase-audit-2026-04-27.md, docs/codebase-overview.md — "current as of
2026-04-27" snapshots, superseded by CLAUDE.md and the v2 plan.
- docs/ui-static-state.md — Phase-9-era UI static-state review snapshot.
- docs/chat-port-feasibility-2026-05-06.md, docs/chat-protocol-spec-2026-05-06.md
— superseded/old-"Batch"-framing docs for the dormant, gated-OFF chat module.
- tests/fixtures/hushchat/{README,CAPTURE_MANIFEST,IMPORT_CHECKLIST}.md -> docs/
_archive/hushchat/ — human docs (not tool input) for the dormant chat fixtures;
the .json fixtures the HushChatFixtureCheck tool globs remain tracked.
These docs only cross-referenced each other (no code/CMake/script refs); no
dangling tracked links remain. Tracked .md (non-libs): 14 -> 6.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
# Chat Port Feasibility Audit (2026-05-06)
|
||||
|
||||
Scope:
|
||||
- Evaluate whether SilentDragonXLite chat can be ported to the current full-node ImGui wallet.
|
||||
- If feasible, define a safe path with feature flagging.
|
||||
|
||||
## Source Audit (SilentDragonXLite)
|
||||
|
||||
Primary chat surfaces in the reference codebase:
|
||||
- `external/SilentDragonXLite/src/chatmodel.h`
|
||||
- `external/SilentDragonXLite/src/chatmodel.cpp`
|
||||
- `external/SilentDragonXLite/src/Chat/Chat.h`
|
||||
- `external/SilentDragonXLite/src/Chat/Chat.cpp`
|
||||
- `external/SilentDragonXLite/src/controller.cpp` (transaction scan + memo decrypt/encrypt)
|
||||
- `external/SilentDragonXLite/src/Model/ChatItem.*`
|
||||
- `external/SilentDragonXLite/src/Model/ContactRequest*`
|
||||
- `external/SilentDragonXLite/src/DataStore/ChatDataStore.*`
|
||||
- `external/SilentDragonXLite/src/mainwindow.ui` and related Qt UI widgets.
|
||||
|
||||
Observed characteristics:
|
||||
- Chat is tightly coupled to Qt widget/UI classes (`QListView`, `QStandardItemModel`, `.ui` forms).
|
||||
- Message transport relies on memo payload parsing/decryption embedded directly in transaction refresh/controller flow.
|
||||
- Contact requests, chat IDs, and message rendering are mixed with Qt view models and DataStore semantics.
|
||||
- Crypto flow depends on shared secret derivation and memo header conventions implemented inline in controller/chat code.
|
||||
|
||||
## Current Wallet Architecture Gap
|
||||
|
||||
Current app (`src/`) is Dear ImGui + SDL3 with different state/render model:
|
||||
- No existing chat domain model/service in ImGui code path.
|
||||
- No Qt runtime/view-model infrastructure to reuse reference chat modules.
|
||||
- Transaction refresh path is centralized in `NetworkRefreshService` and app refresh orchestration, not Qt controller callbacks.
|
||||
|
||||
## Feasibility Decision
|
||||
|
||||
Decision: **Not safe to directly port in this batch**.
|
||||
|
||||
Reason:
|
||||
- A direct transplant would require substantial re-architecture, not incremental copy-over.
|
||||
- High risk of regressions in transaction refresh correctness, memo handling, and wallet UX stability.
|
||||
- Crypto/message parsing code requires dedicated security review before enabling in production.
|
||||
|
||||
## Estimated Effort (for a proper staged port)
|
||||
|
||||
1. Domain extraction (chat/contact/message models, storage interfaces): 3-5 days
|
||||
2. Service integration with current refresh pipeline (`NetworkRefreshService` + app): 4-7 days
|
||||
3. ImGui chat UI (list, composer, contact requests, actions): 4-6 days
|
||||
4. Security hardening + memo/crypto validation tests: 3-5 days
|
||||
5. End-to-end QA/regression/perf pass: 2-4 days
|
||||
|
||||
Total estimate: **16-27 engineer-days**.
|
||||
|
||||
## Risk Profile
|
||||
|
||||
- Security risk: high (memo crypto path and malformed payload handling).
|
||||
- Stability risk: high (transaction refresh path is core-wallet critical).
|
||||
- UX risk: medium (new asynchronous chat states and contact flows).
|
||||
|
||||
## Recommended Safe Plan
|
||||
|
||||
1. Add compile/runtime feature flag (`DRAGONX_ENABLE_CHAT`) default OFF.
|
||||
2. Implement read-only parser + storage first (no send), fully test malformed memos.
|
||||
3. Add send path behind same flag with strict size/format guards.
|
||||
4. Ship disabled by default until security and regression acceptance criteria are met.
|
||||
@@ -1,628 +0,0 @@
|
||||
# HushChat Protocol Spec (2026-05-06)
|
||||
|
||||
Scope:
|
||||
- Document the memo protocol observed in `external/SilentDragonXLite` for a future safe ImGui implementation.
|
||||
- Batch 1 implements header parsing/validation only.
|
||||
- Batch 2 implements transaction-output grouping for validated header/payload memo pairs only.
|
||||
- Batch 3 implements read-only transaction extraction as sanitized refresh metadata behind `DRAGONX_ENABLE_CHAT`.
|
||||
- Batch 4 implements pre-decryption validation/error typing only.
|
||||
- Batch 5 implements decrypt-input byte boundary preparation and fixture readiness only.
|
||||
- Batch 6 implements non-sensitive cross-wallet fixture shape verification only.
|
||||
- Batch 7 implements fixture-file loading for pending and ready compatibility vectors.
|
||||
- Batch 8 implements a strict import checklist for replacing pending fixture placeholders only when all required categories are supplied.
|
||||
- Batch 9 implements a no-plaintext SDXL seed/public-key projection verifier for ready fixture files.
|
||||
- Batch 10 implements an inert corrupted-authentication-failure readiness scaffold for future secretstream auth failure checks.
|
||||
- Batch 11 implements a strict ready-fixture replacement dry-run report for real non-sensitive SDXL vectors.
|
||||
- Batch 12 implements a redacted capture-manifest validator for staged real-vector directories.
|
||||
- These batches do not decrypt, store, render, or send chat messages.
|
||||
|
||||
## Reference Sources
|
||||
|
||||
- `external/SilentDragonXLite/src/chatmodel.cpp`
|
||||
- `MainWindow::createHeaderMemo(...)`
|
||||
- `MainWindow::createTxFromChatPage()`
|
||||
- `MainWindow::createTxForSafeContactRequest()`
|
||||
- `external/SilentDragonXLite/src/controller.cpp`
|
||||
- `Controller::refreshTransactions()` outgoing/incoming memo handling
|
||||
- `external/SilentDragonXLite/src/DataStore/ChatDataStore.*`
|
||||
- `external/SilentDragonXLite/src/Model/ChatItem.*`
|
||||
- `external/SilentDragonXLite/src/Model/ContactItem.*`
|
||||
- `external/SilentDragonXLite/src/Model/ContactRequest.*`
|
||||
|
||||
## Transaction Shape
|
||||
|
||||
SilentDragonXLite sends HushChat data as shielded transactions with paired memo outputs:
|
||||
|
||||
1. Header memo: UTF-8 JSON object beginning with `{`.
|
||||
2. Payload memo: non-JSON string paired with the same transaction.
|
||||
|
||||
For encrypted chat messages, the payload memo is libsodium secretstream ciphertext encoded as hex. For contact requests, the payload memo is plaintext request text in the reference implementation.
|
||||
|
||||
The reference may also add unrelated dust/noise memo outputs around the chat pair, so a future parser must group by transaction, output order/position, and validated header metadata rather than assuming the transaction contains only two outputs.
|
||||
|
||||
## Header JSON
|
||||
|
||||
`createHeaderMemo(...)` emits short field names to preserve memo space:
|
||||
|
||||
```json
|
||||
{
|
||||
"h": 1,
|
||||
"v": 0,
|
||||
"z": "zs-reply-address",
|
||||
"cid": "conversation-id",
|
||||
"t": "Memo",
|
||||
"e": "48 hex chars for message headers, empty for contact requests",
|
||||
"p": "64 hex chars"
|
||||
}
|
||||
```
|
||||
|
||||
Fields:
|
||||
- `h`: integer header number, starting at `1`.
|
||||
- `v`: integer HushChat version. Current reference uses `0`.
|
||||
- `z`: sender reply z-address.
|
||||
- `cid`: conversation/contact id.
|
||||
- `t`: type string. Known values are `Memo` and `Cont`.
|
||||
- `e`: hex-encoded `crypto_secretstream_xchacha20poly1305_HEADERBYTES` value. Present for `Memo`, empty for `Cont` in the reference.
|
||||
- `p`: hex-encoded `crypto_kx_PUBLICKEYBYTES` public key generated from `crypto_kx_seed_keypair()`.
|
||||
|
||||
## Type Semantics
|
||||
|
||||
`Memo`:
|
||||
- Header includes non-empty `e` with 24 bytes encoded as 48 hex characters.
|
||||
- Payload memo is ciphertext encoded as hex.
|
||||
- Reference derives a deterministic keypair from the wallet/chat password, derives a shared session key with the peer public key, and decrypts via `crypto_secretstream_xchacha20poly1305_pull()`.
|
||||
|
||||
`Cont`:
|
||||
- Header includes empty `e`.
|
||||
- Payload memo is the contact request text.
|
||||
- Header `z`, `cid`, and `p` are used to create/update a contact record.
|
||||
|
||||
## SilentDragonXLite Decryption Audit
|
||||
|
||||
Reference initialization:
|
||||
- `main.cpp` calls `sodium_init()` at startup and aborts if libsodium cannot initialize.
|
||||
|
||||
Reference chat key material:
|
||||
- Wallet/chat passphrase input is wrapped with fixed strings (`DRGX` + user passphrase + `SDXL`).
|
||||
- The wrapped passphrase is hashed with `blake3_PW(...)`.
|
||||
- `crypto_pwhash(...)` is then called with `crypto_pwhash_OPSLIMIT_SENSITIVE`, `crypto_pwhash_MEMLIMIT_SENSITIVE`, and `crypto_pwhash_ALG_DEFAULT`, producing `crypto_box_SEEDBYTES` bytes.
|
||||
- The resulting bytes are hex-encoded and stored in `ChatDataStore::_password` for later HushChat operations.
|
||||
|
||||
Reference `Memo` send path:
|
||||
- `ChatDataStore::getPassword()` returns the stored hex chat key string.
|
||||
- The Qt code passes `passphraseHash.toUtf8()` directly as the seed input to `crypto_kx_seed_keypair(pk, sk, seed)`.
|
||||
- The peer public key is loaded from contact/address-book metadata using `QByteArray::fromHex(...)`.
|
||||
- Sending uses `crypto_kx_server_session_keys(server_rx, server_tx, pk, sk, peer_pk)` and encrypts with `server_tx`.
|
||||
- `crypto_secretstream_xchacha20poly1305_init_push(&state, header, server_tx)` creates the 24-byte stream header.
|
||||
- `crypto_secretstream_xchacha20poly1305_push(..., TAG_FINAL)` encrypts the UTF-8 message, producing `crypto_secretstream_xchacha20poly1305_ABYTES + plaintext_length` bytes.
|
||||
- The header memo stores `e` as the stream header hex and `p` as the sender public key hex.
|
||||
- The payload memo stores ciphertext hex.
|
||||
|
||||
Reference outgoing-history decrypt path:
|
||||
- Re-derives the local deterministic keypair from `ChatDataStore::getPassword()`.
|
||||
- Looks up the peer public key by address.
|
||||
- Uses `crypto_kx_server_session_keys(...)` and decrypts with `server_tx`.
|
||||
- Uses `crypto_secretstream_xchacha20poly1305_init_pull(&state, header, server_tx)` and `crypto_secretstream_xchacha20poly1305_pull(...)`.
|
||||
- Stores decrypted text as `ChatItem` in `ChatDataStore`.
|
||||
|
||||
Reference incoming decrypt path:
|
||||
- Parses header memo fields `cid`, `t`, `z`, `e`, and `p` from transaction memo JSON.
|
||||
- Stores/caches `cid`, reply z-address, secretstream header, and sender public key.
|
||||
- Re-derives the local deterministic keypair from `ChatDataStore::getPassword()`.
|
||||
- Uses `crypto_kx_client_session_keys(client_rx, client_tx, pk, sk, peer_pk)` and decrypts with `client_rx`.
|
||||
- Uses `crypto_secretstream_xchacha20poly1305_init_pull(&state, header, client_rx)` and `crypto_secretstream_xchacha20poly1305_pull(...)`.
|
||||
- Stores decrypted text/contact request text as `ChatItem` in `ChatDataStore`.
|
||||
|
||||
Open questions before real decryption:
|
||||
- Confirm the `crypto_pwhash(...)` salt input length and derivation semantics from the BLAKE3 output. The reference passes a pointer derived from a string without an explicit salt object.
|
||||
- Confirm outgoing-vs-incoming role selection (`server_tx` for sends/outgoing history, `client_rx` for received messages) against cross-wallet fixtures.
|
||||
- Decide whether new DragonX-originated messages must preserve SilentDragonXLite's UTF-8-hex seed behavior forever or can support a versioned decoded-byte seed later.
|
||||
- Decide whether Batch 3 metadata fields `cid` and reply z-address are safe to persist, hash, or keep transient only.
|
||||
- Decide how corrupted ciphertext/header failures should surface to users without logging memo contents or plaintext.
|
||||
|
||||
## Batch 1 Parser Contract
|
||||
|
||||
Implemented parser: `dragonx::chat::parseHushChatHeaderMemo()`.
|
||||
|
||||
The parser accepts only the clear, structural header format:
|
||||
- memo must be non-empty JSON object text beginning with `{`
|
||||
- maximum memo size is 512 bytes
|
||||
- required fields must exist and have expected JSON types
|
||||
- only version `0` is accepted
|
||||
- only `Memo` and `Cont` types are accepted
|
||||
- public key must be 32 bytes encoded as 64 hex characters
|
||||
- `Memo` must include a 24 byte secretstream header encoded as 48 hex characters
|
||||
- `Cont` must have an empty secretstream header
|
||||
|
||||
The parser intentionally does not:
|
||||
- decrypt ciphertext
|
||||
- derive keys
|
||||
- parse contact request payload text
|
||||
- write chat storage
|
||||
- expose UI
|
||||
- log memo contents
|
||||
|
||||
## Batch 2 Transaction Grouping Contract
|
||||
|
||||
Implemented grouping helper: `dragonx::chat::groupHushChatMemoOutputs()`.
|
||||
|
||||
Inputs:
|
||||
- one transaction's memo outputs as `(position, memo)` records
|
||||
- output `position` should be the wallet/RPC output position where available
|
||||
- equal positions retain caller order as a stable fallback
|
||||
|
||||
Output:
|
||||
- zero or more validated HushChat header/payload pairs
|
||||
- grouping issues for malformed or incomplete HushChat-looking data
|
||||
- ignored memo count for unrelated dust/noise payloads
|
||||
|
||||
Grouping rules:
|
||||
- outputs are processed in ascending output position
|
||||
- non-header memos before any valid header are ignored as dust/noise
|
||||
- memos larger than 512 bytes produce `OversizedMemo` and are skipped
|
||||
- memos beginning with `{` must parse as valid HushChat headers or produce `InvalidHeader`
|
||||
- a valid header opens a pending pair
|
||||
- a valid `Memo` header pairs with the next non-header memo that is non-empty, <=512 bytes, even-length hex
|
||||
- a valid `Cont` header pairs with the next non-header memo that is non-empty and <=512 bytes
|
||||
- non-matching payload candidates after a pending `Memo` header are ignored so unrelated dust/noise can appear before ciphertext
|
||||
- a second valid header before a payload produces `DuplicateHeader`; the previous header also produces `MissingPayload`, then the new header becomes pending
|
||||
- a valid header left pending at end-of-transaction produces `MissingPayload`
|
||||
|
||||
The grouping helper intentionally does not:
|
||||
- decrypt ciphertext
|
||||
- inspect contact request payload text
|
||||
- decide whether a pair belongs to the local wallet
|
||||
- write storage
|
||||
- trigger notifications/UI
|
||||
- run from the transaction refresh pipeline while `DRAGONX_ENABLE_CHAT` is off
|
||||
|
||||
## Batch 3 Transaction Extraction Contract
|
||||
|
||||
Implemented extractor: `dragonx::chat::extractHushChatTransactionMetadata()`.
|
||||
|
||||
Inputs:
|
||||
- one transaction id
|
||||
- one transaction's memo outputs as `(position, memo)` records
|
||||
- an explicit feature-enabled switch, defaulting to the build-time `DRAGONX_ENABLE_CHAT` value
|
||||
|
||||
Output when enabled:
|
||||
- zero or more sanitized transaction metadata records
|
||||
- sanitized grouping issues for HushChat-looking malformed data
|
||||
- ignored memo count for unrelated dust/noise payloads
|
||||
|
||||
Sanitized metadata fields:
|
||||
- transaction id
|
||||
- HushChat type (`Memo` or `Cont`)
|
||||
- conversation id
|
||||
- reply z-address
|
||||
- header output position
|
||||
- payload output position
|
||||
- payload memo byte length only
|
||||
|
||||
The extractor intentionally does not expose:
|
||||
- payload memo contents
|
||||
- secretstream header hex
|
||||
- peer public key hex
|
||||
- decrypted plaintext
|
||||
- derived keys
|
||||
|
||||
Refresh integration:
|
||||
- `NetworkRefreshService::TransactionRefreshResult` carries transient `hushChatMetadata` records.
|
||||
- Received shielded memo outputs and outgoing `z_viewtransaction` memo outputs are eligible for extraction only while `DRAGONX_ENABLE_CHAT` is enabled.
|
||||
- The default build leaves `hushChatMetadata` empty and does not change transaction history, caches, wallet state, UI, sends, or logs.
|
||||
- `applyTransactionRefreshResult()` currently ignores chat metadata, so no Batch 3 data is persisted.
|
||||
|
||||
## Batch 4 Pre-Decryption Safety Contract
|
||||
|
||||
Implemented preflight helper: `dragonx::chat::validateHushChatMemoDecryptPreflight()`.
|
||||
|
||||
Inputs:
|
||||
- parsed HushChat header metadata
|
||||
- ciphertext payload memo hex
|
||||
- an explicit feature-enabled switch, defaulting to the build-time `DRAGONX_ENABLE_CHAT` value
|
||||
|
||||
Output:
|
||||
- `ok=true` only when the input is structurally safe to hand to a future decrypt implementation
|
||||
- typed errors for feature-disabled, non-`Memo` header, invalid header number/version, missing reply address/conversation id, malformed secretstream header, malformed public key, empty ciphertext, oversized ciphertext, odd-length ciphertext, non-hex ciphertext, and truncated ciphertext
|
||||
- decoded ciphertext byte length only on success
|
||||
|
||||
Validation rules:
|
||||
- no work is performed while `DRAGONX_ENABLE_CHAT` is off
|
||||
- only `Memo` headers are valid for decrypt preflight; `Cont` remains plaintext/contact-request handling for later batches
|
||||
- secretstream header metadata must remain 24 bytes encoded as 48 hex characters
|
||||
- peer public key metadata must remain 32 bytes encoded as 64 hex characters
|
||||
- ciphertext memo must be non-empty, <=512 bytes as memo text, even-length hex, and decode to more than `crypto_secretstream_xchacha20poly1305_ABYTES` bytes
|
||||
|
||||
The preflight helper intentionally does not:
|
||||
- call libsodium
|
||||
- derive keypairs or session keys
|
||||
- decode or return ciphertext bytes
|
||||
- decrypt plaintext
|
||||
- store chat/contact data
|
||||
- render UI
|
||||
- send messages
|
||||
- log memo contents or plaintext
|
||||
|
||||
## Batch 5 Decrypt-Input Boundary Contract
|
||||
|
||||
Implemented helpers:
|
||||
- `dragonx::chat::decodeHushChatHexBytes()`
|
||||
- `dragonx::chat::prepareHushChatDecryptInput()`
|
||||
- `dragonx::chat::inspectHushChatDecryptFixtureReadiness()`
|
||||
- `dragonx::chat::hushChatSessionKeySelectionForDirection()`
|
||||
|
||||
Inputs:
|
||||
- stored chat key material as the 64-character hex string currently stored by SilentDragonXLite `ChatDataStore::getPassword()`
|
||||
- parsed `Memo` header metadata
|
||||
- ciphertext payload memo hex
|
||||
- decrypt direction (`Incoming` or `Outgoing`)
|
||||
- an explicit feature-enabled switch, defaulting to the build-time `DRAGONX_ENABLE_CHAT` value
|
||||
|
||||
Prepared fields:
|
||||
- `stored_chat_key_bytes`: decoded 32 bytes from the stored chat key hex string
|
||||
- `seed_bytes`: SilentDragonXLite-compatible `crypto_kx_seed_keypair()` seed bytes
|
||||
- `peer_public_key_bytes`: decoded 32 bytes from header field `p`
|
||||
- `stream_header_bytes`: decoded 24 bytes from header field `e`
|
||||
- `ciphertext_bytes`: decoded ciphertext payload bytes
|
||||
- `session_key_selection`: `ClientRx` for incoming messages, `ServerTx` for outgoing-history messages
|
||||
- `plaintext_capacity`: ciphertext byte length minus `crypto_secretstream_xchacha20poly1305_ABYTES`
|
||||
|
||||
Seed compatibility decision:
|
||||
- SilentDragonXLite stores a 32-byte `crypto_pwhash(...)` output as 64 lowercase hex characters.
|
||||
- SilentDragonXLite then passes `ChatDataStore::getPassword().toUtf8()` directly to `crypto_kx_seed_keypair(...)`.
|
||||
- Because `crypto_kx_seed_keypair(...)` consumes 32 bytes, DragonX's compatibility contract prepares `seed_bytes` as the first 32 UTF-8 bytes of the stored 64-character hex string.
|
||||
- The decoded 32-byte stored key is kept separate as `stored_chat_key_bytes` so future code cannot accidentally conflate the two interpretations.
|
||||
|
||||
Validation rules:
|
||||
- no work is performed while `DRAGONX_ENABLE_CHAT` is off
|
||||
- stored chat key hex must decode to exactly 32 bytes
|
||||
- Batch 4 preflight must pass before any payload decoding
|
||||
- peer public key must decode to exactly 32 bytes
|
||||
- stream header must decode to exactly 24 bytes
|
||||
- ciphertext must decode to the preflight-reported byte length
|
||||
- incoming direction selects `client_rx`; outgoing-history direction selects `server_tx`
|
||||
- fixture readiness requires all prepared byte fields to have exact lengths and ciphertext to contain more than secretstream authentication overhead
|
||||
|
||||
The Batch 5 helpers intentionally do not:
|
||||
- call libsodium
|
||||
- derive keypairs or session keys
|
||||
- decrypt plaintext
|
||||
- store key material, ciphertext, chat, or contact data outside temporary result objects
|
||||
- render UI
|
||||
- send messages
|
||||
- log key material, memo contents, ciphertext bytes, or plaintext
|
||||
|
||||
Required cross-wallet fixtures before real decryption:
|
||||
- SilentDragonXLite outgoing `Memo` fixture with stored chat key hex, local public key, peer public key, header memo, ciphertext memo, direction, expected plaintext byte length, and a non-sensitive plaintext hash.
|
||||
- SilentDragonXLite incoming `Memo` fixture with the same metadata plus role expectation (`client_rx`).
|
||||
- Round-trip fixture between SilentDragonXLite and DragonX confirming the UTF-8-hex seed projection produces the observed public key.
|
||||
- Corrupted fixture variants for bad peer public key, bad stream header, truncated ciphertext, and authentication failure.
|
||||
- Contact-request fixture proving `Cont` remains outside encrypted `Memo` decrypt handling.
|
||||
|
||||
## Batch 6 Cross-Wallet Fixture Contract
|
||||
|
||||
Implemented verifier: `dragonx::chat::verifyHushChatCompatibilityFixture()`.
|
||||
|
||||
Static fixture fields:
|
||||
- fixture id
|
||||
- stored chat key hex
|
||||
- local public key hex
|
||||
- peer public key hex
|
||||
- header memo
|
||||
- ciphertext memo
|
||||
- direction (`Incoming` or `Outgoing`)
|
||||
- expected session key selection (`ClientRx` or `ServerTx`)
|
||||
- expected stored chat key byte length
|
||||
- expected seed byte length
|
||||
- expected local public key byte length
|
||||
- expected peer public key byte length
|
||||
- expected stream header byte length
|
||||
- expected ciphertext byte length
|
||||
- expected plaintext byte length
|
||||
- optional expected plaintext hash hex
|
||||
|
||||
Verifier behavior:
|
||||
- no work is performed while `DRAGONX_ENABLE_CHAT` is off
|
||||
- fixture id must be present so test/report output can identify the vector without logging memo contents
|
||||
- local public key hex must decode to exactly 32 bytes
|
||||
- peer public key hex must decode to exactly 32 bytes
|
||||
- header memo is parsed through `parseHushChatHeaderMemo()`
|
||||
- `(header memo, ciphertext memo)` is grouped through `groupHushChatMemoOutputs()`
|
||||
- only `Memo` fixtures are accepted; `Cont` fixtures are rejected as outside encrypted decrypt handling
|
||||
- for incoming fixtures, header field `p` must match the fixture peer public key
|
||||
- for outgoing-history fixtures, header field `p` must match the fixture local public key
|
||||
- decrypt input is prepared through `prepareHushChatDecryptInput()` with the fixture peer public key kept separate from the header public key
|
||||
- readiness is checked through `inspectHushChatDecryptFixtureReadiness()`
|
||||
- expected byte lengths and expected role must match the prepared/readiness output exactly
|
||||
- optional plaintext hash must be hex text only; the verifier records its byte length but never evaluates plaintext
|
||||
|
||||
The Batch 6 verifier intentionally does not:
|
||||
- derive a keypair
|
||||
- derive session keys
|
||||
- call libsodium
|
||||
- decrypt ciphertext
|
||||
- compare plaintext
|
||||
- persist fixture data
|
||||
- render UI
|
||||
- send messages
|
||||
- log key material, memo contents, ciphertext bytes, plaintext hashes, or plaintext
|
||||
|
||||
Remaining requirements before real decryption:
|
||||
- collect real SilentDragonXLite incoming and outgoing `Memo` fixtures using non-sensitive plaintext and recorded hashes
|
||||
- verify that the observed local public key matches the UTF-8-hex seed projection in an isolated test vector
|
||||
- add corrupted authentication-failure fixtures that pass structural validation but must fail future `crypto_secretstream_xchacha20poly1305_pull()`
|
||||
- decide how fixture hashes will be generated and compared without retaining plaintext in the repository
|
||||
- keep contact-request fixtures separate from encrypted `Memo` fixtures
|
||||
|
||||
## Batch 7 Fixture File Import Contract
|
||||
|
||||
Checked-in fixture files live under `tests/fixtures/hushchat/`. The current files are intentionally `pending` placeholders for real, non-sensitive SilentDragonXLite vectors. They prove the import format and required categories without committing key material, memo contents, ciphertext bytes, plaintext, or made-up compatibility data.
|
||||
|
||||
The loader-supported format is JSON. The field names are intentionally TOML-friendly and the equivalent TOML shape is documented below for future fixture capture tooling.
|
||||
|
||||
Required top-level fields:
|
||||
- `schema`: must be `dragonx.hushchat.compat-fixture.v1`
|
||||
- `kind`: one of `incoming_memo`, `outgoing_memo`, `seed_public_key_projection`, `corrupted_auth_failure`, or `cont_exclusion`
|
||||
- `status`: `pending` or `ready`
|
||||
- `id`: required for `pending` files
|
||||
- `pending_reason`: required for `pending` files and must explain what real SDXL data is still missing
|
||||
- `fixture`: required for `ready` files
|
||||
|
||||
Ready JSON shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "dragonx.hushchat.compat-fixture.v1",
|
||||
"status": "ready",
|
||||
"kind": "incoming_memo",
|
||||
"fixture": {
|
||||
"id": "sdxl-incoming-memo-example",
|
||||
"stored_chat_key_hex": "<64 hex chars from SDXL test wallet>",
|
||||
"local_public_key_hex": "<64 hex chars>",
|
||||
"peer_public_key_hex": "<64 hex chars>",
|
||||
"header_memo": "<header memo JSON string>",
|
||||
"ciphertext_memo": "<payload memo hex string>",
|
||||
"direction": "Incoming",
|
||||
"expected": {
|
||||
"session_key_selection": "ClientRx",
|
||||
"stored_chat_key_bytes": 32,
|
||||
"seed_bytes": 32,
|
||||
"local_public_key_bytes": 32,
|
||||
"peer_public_key_bytes": 32,
|
||||
"stream_header_bytes": 24,
|
||||
"ciphertext_bytes": 21,
|
||||
"plaintext_bytes": 4,
|
||||
"plaintext_hash_hex": "<optional 64 hex chars>"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Equivalent TOML shape for future tooling:
|
||||
|
||||
```toml
|
||||
schema = "dragonx.hushchat.compat-fixture.v1"
|
||||
status = "ready"
|
||||
kind = "incoming_memo"
|
||||
|
||||
[fixture]
|
||||
id = "sdxl-incoming-memo-example"
|
||||
stored_chat_key_hex = "<64 hex chars from SDXL test wallet>"
|
||||
local_public_key_hex = "<64 hex chars>"
|
||||
peer_public_key_hex = "<64 hex chars>"
|
||||
header_memo = "<header memo JSON string>"
|
||||
ciphertext_memo = "<payload memo hex string>"
|
||||
direction = "Incoming"
|
||||
|
||||
[fixture.expected]
|
||||
session_key_selection = "ClientRx"
|
||||
stored_chat_key_bytes = 32
|
||||
seed_bytes = 32
|
||||
local_public_key_bytes = 32
|
||||
peer_public_key_bytes = 32
|
||||
stream_header_bytes = 24
|
||||
ciphertext_bytes = 21
|
||||
plaintext_bytes = 4
|
||||
plaintext_hash_hex = "<optional 64 hex chars>"
|
||||
```
|
||||
|
||||
Ready `incoming_memo`, `outgoing_memo`, `seed_public_key_projection`, and `corrupted_auth_failure` fixture files are accepted only after the Batch 6 verifier succeeds. That means the file is parsed through the Batch 1 header parser, Batch 2 memo grouper, Batch 4 preflight checks, Batch 5 decrypt-input preparation, and Batch 6 readiness/byte-length/role checks. `cont_exclusion` is accepted only when the verifier rejects the fixture as `NonMemoHeader`, proving contact requests stay outside encrypted Memo decrypt handling.
|
||||
|
||||
The fixture loader does not derive keys, call libsodium, decrypt, compare plaintext, persist chat data, render UI, send messages, or log key material/memo contents/plaintext/ciphertext bytes. It reports `pending` for missing real vectors and `verified` only for structurally ready vectors.
|
||||
|
||||
Exact data still needed from SilentDragonXLite before changing pending files to ready:
|
||||
- a non-sensitive incoming `Memo` vector with stored chat key hex, local public key hex, peer public key hex, header memo, ciphertext memo, expected `ClientRx` role, expected byte lengths, and optional plaintext hash
|
||||
- a non-sensitive outgoing-history `Memo` vector with the same structural fields and expected `ServerTx` role
|
||||
- a seed/public-key projection vector proving the SDXL UTF-8-hex seed input produces the recorded local public key, without committing passphrase or plaintext
|
||||
- a corrupted authentication-failure vector that passes structural validation but must fail future secretstream pull authentication
|
||||
- a `Cont` vector proving contact requests remain excluded from encrypted Memo decrypt preparation
|
||||
- the exact capture command/procedure from SDXL so future vectors are reproducible without exposing sensitive wallet data
|
||||
|
||||
## Batch 8 Fixture Import Checklist Contract
|
||||
|
||||
Implemented import gate:
|
||||
- `dragonx::chat::hushChatRequiredCompatibilityFixtureKinds()`
|
||||
- `dragonx::chat::inspectHushChatCompatibilityFixtureImportChecklist()`
|
||||
- developer tool target `HushChatFixtureCheck`
|
||||
|
||||
The import checklist is the conservative replacement path for the pending JSON placeholders. It accepts a caller-supplied candidate file for each expected category and loads every file through `loadHushChatCompatibilityFixtureFile()`. It reports replacement-ready only when:
|
||||
- all five required categories are supplied exactly once
|
||||
- no required category is missing
|
||||
- no duplicate category candidate is supplied
|
||||
- no supplied file is malformed or unreadable
|
||||
- no file remains `pending`
|
||||
- each non-`Cont` ready file passes the Batch 7 loader and Batch 6 structural verifier
|
||||
- each non-`Cont` ready file passes the Batch 9 seed/public-key projection verifier
|
||||
- the `corrupted_auth_failure` ready file is marked structurally ready for a future secretstream authentication-failure check, without decrypting or authenticating it now
|
||||
- the `cont_exclusion` ready file is accepted only by being excluded from encrypted Memo handling
|
||||
|
||||
The current checked-in placeholder directory is expected to pass only with pending allowed:
|
||||
|
||||
```sh
|
||||
./build/bin/HushChatFixtureCheck --allow-pending tests/fixtures/hushchat
|
||||
```
|
||||
|
||||
Before replacing pending placeholders with real vectors, the strict check must pass:
|
||||
|
||||
```sh
|
||||
./build/bin/HushChatFixtureCheck tests/fixtures/hushchat
|
||||
```
|
||||
|
||||
The tool intentionally prints only category names, file basenames, status/error names, and aggregate counts. It does not print key material, memo contents, plaintext, ciphertext bytes, plaintext hashes, or derived bytes. The helper/tool does not derive session keys, decrypt, persist chat data, render UI, send messages, or enable `DRAGONX_ENABLE_CHAT` in the app; Batch 9 uses libsodium only for the local seed/public-key projection check.
|
||||
|
||||
Capture checklist for real SDXL fixtures is tracked in `tests/fixtures/hushchat/IMPORT_CHECKLIST.md`. Pending files may be replaced only with non-sensitive, disposable-wallet vectors that pass the strict checklist. If any category is pending, missing, malformed, mismatched, duplicated, or not verified, the replacement gate remains closed.
|
||||
|
||||
## Batch 9 Seed/Public-Key Projection Contract
|
||||
|
||||
Implemented verifier:
|
||||
- `dragonx::chat::verifyHushChatSeedPublicKeyProjection()`
|
||||
|
||||
The projection verifier is a no-plaintext compatibility check for ready non-`Cont` fixture files. It accepts the existing fixture fields only:
|
||||
- fixture id
|
||||
- stored SDXL chat key hex string
|
||||
- declared local public key hex
|
||||
- expected stored chat key byte length
|
||||
- expected seed byte length
|
||||
- expected local public key byte length
|
||||
|
||||
Verification rules:
|
||||
- no work is performed while `DRAGONX_ENABLE_CHAT` is off unless a caller explicitly supplies the test/import feature switch
|
||||
- fixture id must be present
|
||||
- stored chat key hex must decode to the expected 32-byte stored key length
|
||||
- seed bytes are exactly the first 32 UTF-8 bytes of the stored 64-character SDXL chat key hex string
|
||||
- seed byte length must match the fixture's declared expected seed length
|
||||
- local public key hex must decode to the expected 32-byte local public key length
|
||||
- libsodium is initialized only for this local public-key projection check
|
||||
- `crypto_kx_seed_keypair()` is called with the SDXL-compatible seed bytes
|
||||
- only the projected public key is compared to the declared local public key
|
||||
- the derived secret key, temporary seed bytes, and temporary projected public key buffer are zeroed before returning
|
||||
|
||||
The verifier intentionally does not:
|
||||
- derive session keys
|
||||
- decrypt ciphertext
|
||||
- compare plaintext
|
||||
- store chat/contact data
|
||||
- render UI
|
||||
- send messages
|
||||
- log or return key material, memo contents, plaintext, ciphertext bytes, plaintext hashes, seed bytes, public key bytes, or secret key bytes
|
||||
|
||||
Batch 9 integration:
|
||||
- `inspectHushChatCompatibilityFixtureImportChecklist()` now requires all four non-`Cont` ready fixtures to pass the seed/public-key projection verifier.
|
||||
- The developer checker reports only the aggregate `seed_projected` count.
|
||||
- Pending files still block strict replacement until real SilentDragonXLite vectors are supplied.
|
||||
|
||||
## Batch 10 Corrupted Authentication-Failure Scaffold
|
||||
|
||||
Implemented scaffold:
|
||||
- `dragonx::chat::inspectHushChatCorruptedAuthFailureReadiness()`
|
||||
|
||||
The corrupted-auth scaffold is an inert marker for ready `corrupted_auth_failure` fixture files. It exists so a real non-sensitive SilentDragonXLite corrupted vector can be carried through the current structural pipeline and reserved for a future `crypto_secretstream_xchacha20poly1305_pull()` authentication-failure assertion.
|
||||
|
||||
Readiness rules:
|
||||
- no work is performed while `DRAGONX_ENABLE_CHAT` is off unless a caller explicitly supplies the test/import feature switch
|
||||
- pending fixtures are rejected as not ready
|
||||
- only `corrupted_auth_failure` fixture files are accepted
|
||||
- the fixture file must already be parsed and verified through the Batch 7 loader and Batch 6 fixture verifier
|
||||
- the fixture must already pass the Batch 9 seed/public-key projection verifier
|
||||
- success sets `structurally_ready_for_future_auth_check=true`
|
||||
- success sets `requires_future_secretstream_auth_failure=true`
|
||||
- success keeps `decrypted=false` and `authenticated=false`
|
||||
|
||||
The scaffold intentionally does not:
|
||||
- derive session keys
|
||||
- initialize a secretstream pull state
|
||||
- decrypt ciphertext
|
||||
- authenticate ciphertext
|
||||
- compare plaintext
|
||||
- store chat/contact data
|
||||
- render UI
|
||||
- send messages
|
||||
- log or return key material, memo contents, plaintext, ciphertext bytes, plaintext hashes, seed bytes, public key bytes, or secret key bytes
|
||||
|
||||
Batch 10 integration:
|
||||
- `inspectHushChatCompatibilityFixtureImportChecklist()` now requires the corrupted fixture to be structurally ready for future auth-failure validation before replacement can be ready.
|
||||
- The developer checker reports aggregate `future_auth_required` and `auth_structural_ready` counts, and per-file labels only for those states.
|
||||
- These counts do not mean the fixture has decrypted or authenticated; they mean it is ready for a future auth-failure assertion once decrypt support exists.
|
||||
|
||||
## Batch 11 Strict Replacement Dry-Run Report
|
||||
|
||||
Implemented dry-run workflow:
|
||||
- `dragonx::chat::inspectHushChatCompatibilityFixtureReplacementDryRun()`
|
||||
- `./build/bin/HushChatFixtureCheck --replacement-dry-run <fixture-file-or-directory>...`
|
||||
|
||||
The dry-run workflow wraps the Batch 10 import checklist and reports whether a supplied ready-vector directory would be eligible to replace the checked-in pending placeholders. It is strict by design: the CLI refuses `--replacement-dry-run` when `--allow-pending` is also supplied, and the dry-run result only sets `would_replace=true` when the underlying checklist is fully `replacement_ready`.
|
||||
|
||||
Replacement is refused if any required vector is:
|
||||
- missing
|
||||
- duplicated
|
||||
- unreadable or malformed
|
||||
- supplied under the wrong expected kind
|
||||
- still marked `pending`
|
||||
- not verified through the fixture loader and structural contracts
|
||||
- unable to pass the SDXL seed/public-key projection check
|
||||
- a `corrupted_auth_failure` vector that is not structurally ready for a future auth-failure check
|
||||
- a `cont_exclusion` vector that is not excluded from encrypted `Memo` handling
|
||||
|
||||
The replacement report is intentionally redacted. It may print only category names, file basenames, status/error names, boolean decision flags, and aggregate counts. It must not print fixture ids, stored chat key material, memo contents, secretstream headers, public key bytes, plaintext hashes, plaintext, ciphertext bytes, seed bytes, derived secret keys, or derived session keys.
|
||||
|
||||
The dry-run workflow intentionally does not:
|
||||
- copy or overwrite fixture files
|
||||
- derive session keys
|
||||
- initialize a secretstream pull state
|
||||
- decrypt ciphertext
|
||||
- authenticate ciphertext
|
||||
- compare plaintext
|
||||
- store chat/contact data
|
||||
- render UI
|
||||
- send messages
|
||||
- enable `DRAGONX_ENABLE_CHAT` in the app
|
||||
|
||||
## Batch 12 Capture Manifest Workflow
|
||||
|
||||
Implemented manifest validator:
|
||||
- `dragonx::chat::validateHushChatCaptureManifest()`
|
||||
- `dragonx::chat::loadHushChatCaptureManifestFile()`
|
||||
- `./build/bin/HushChatFixtureCheck --validate-capture-manifest <manifest-file-or-directory>`
|
||||
|
||||
The capture manifest is a redacted provenance and handling record for a staged directory of real disposable SilentDragonXLite fixture files. It does not validate fixture contents and does not replace the Batch 11 strict replacement dry run. Instead, it verifies that the staged directory has the required metadata before strict fixture validation is used.
|
||||
|
||||
The checked-in template is `tests/fixtures/hushchat/templates/capture-manifest.template.json`. The template is intentionally marked `template`; a staged copy must be named `capture-manifest.json`, set top-level `status` to `staged`, and set each required category status to `ready` before the validator accepts it.
|
||||
|
||||
Required manifest fields:
|
||||
- schema `dragonx.hushchat.capture-manifest.v1`
|
||||
- top-level status `staged`
|
||||
- redacted manifest id
|
||||
- staged fixture directory name
|
||||
- dry-run command containing `HushChatFixtureCheck --replacement-dry-run`
|
||||
- provenance object with source client `SilentDragonXLite`, source client version or commit, capture date, network, and capture method
|
||||
- handling object with all required safety flags set to `true`
|
||||
- exactly one category entry for each required fixture kind, each with a staged filename and status `ready`
|
||||
|
||||
Rejected manifest conditions:
|
||||
- unreadable, non-object, or malformed JSON
|
||||
- wrong schema
|
||||
- template or unknown status
|
||||
- missing provenance, handling, dry-run, fixture-directory, or category metadata
|
||||
- missing, duplicate, or unknown required category entries
|
||||
- any known prohibited fixture-data field such as `fixture`, `stored_chat_key_hex`, `header_memo`, `ciphertext_memo`, `plaintext`, `passphrase`, private-key fields, wallet-file fields, derived-key fields, or session-key fields
|
||||
|
||||
The manifest validator prints only redacted status/error names, counts, category names, basenames, and boolean safety flags. It intentionally does not inspect or print fixture contents, key material, memo contents, plaintext, ciphertext bytes, plaintext hashes, derived keys, session keys, or wallet files. It also does not copy files, decrypt, authenticate, derive session keys, store chat/contact data, render UI, send messages, or enable `DRAGONX_ENABLE_CHAT` in the app.
|
||||
|
||||
The staged-vector workflow is:
|
||||
|
||||
```sh
|
||||
./build/bin/HushChatFixtureCheck --validate-capture-manifest /path/to/staged/hushchat-fixtures
|
||||
./build/bin/HushChatFixtureCheck --replacement-dry-run /path/to/staged/hushchat-fixtures
|
||||
```
|
||||
|
||||
Both commands must succeed before replacing the checked-in pending placeholders.
|
||||
|
||||
## Remaining Implementation Batches
|
||||
|
||||
1. Capture real non-sensitive SilentDragonXLite fixture files and replace pending placeholders only after the Batch 12 manifest validator and Batch 11 strict dry-run report both succeed.
|
||||
2. Add the real secretstream auth-failure assertion for ready corrupted fixtures after decrypt support exists.
|
||||
3. Implement encrypted message decryption only after fixture confirmation.
|
||||
4. Add wallet-scoped chat/contact storage.
|
||||
5. Add read-only ImGui Chat UI.
|
||||
6. Add contact request actions.
|
||||
7. Add send/composer flow after receive-side validation.
|
||||
@@ -1,671 +0,0 @@
|
||||
# Codebase Cleanup Audit
|
||||
|
||||
Current as of 2026-04-27 for ObsidianDragon `1.2.0-rc1`.
|
||||
|
||||
## Scope
|
||||
|
||||
This audit covers architecture, threading, UI/layout code, build/resource integration, RPC/daemon behavior, and general cleanup opportunities. It is a static maintainability audit, not a runtime performance profile or security penetration test.
|
||||
|
||||
Evidence was gathered from source-tree scans, largest-file metrics, targeted code inspection, and focused architecture/UI/build review passes.
|
||||
|
||||
## Snapshot
|
||||
|
||||
- `src/` contains about 77,639 handwritten C++ source/header lines, excluding generated language outputs.
|
||||
- Generated language headers previously accounted for about 34,675 source-tree lines under `src/embedded/lang_*.h`; Phase 4 moved those outputs to `build/generated/embedded/`.
|
||||
- Largest handwritten files are concentrated in UI and application orchestration:
|
||||
- `src/ui/windows/balance_tab.cpp`: 3,434 lines
|
||||
- `src/app.cpp`: 3,097 lines
|
||||
- `src/ui/windows/mining_tab.cpp`: 2,544 lines
|
||||
- `src/ui/pages/settings_page.cpp`: 2,092 lines
|
||||
- `src/main.cpp`: 2,014 lines
|
||||
- `src/app_security.cpp`: 1,918 lines
|
||||
- `src/app_network.cpp`: 1,912 lines
|
||||
- `src/ui/windows/console_tab.cpp`: 1,572 lines
|
||||
- `src/app_wizard.cpp`: 1,378 lines
|
||||
- `src/daemon/embedded_daemon.cpp`: 1,213 lines
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The codebase has a solid functional foundation: RPC work is already separated from UI-thread callbacks, the schema-driven UI system is useful, and embedded resource handling is well established. The largest opportunities are mostly structural rather than feature bugs.
|
||||
|
||||
The main cleanup theme is ownership. `App` currently coordinates lifecycle, rendering, daemon state, RPC refreshes, wallet security, dialogs, shutdown, mining, and first-run flow. That centralization makes features easy to wire in the short term, but it also pushes thread lifetime, error propagation, UI state, and background polling into a few very large files.
|
||||
|
||||
The second theme is consistency. Dialogs mostly use Material overlay helpers, but some still use raw ImGui modals. Many UI dimensions are schema-driven, but dialog/button/icon sizes still have scattered literals. RPC auth exists in more than one place, generated resources live partly in source and partly in build output, and error handling ranges from user-visible reporting to silent `catch (...)` blocks.
|
||||
|
||||
## Implementation Status
|
||||
|
||||
### Phase 1 Completed On 2026-04-27
|
||||
|
||||
- Replaced time-seeded `std::rand()` RPC credential generation in `src/rpc/connection.cpp` with libsodium-backed random generation.
|
||||
- Replaced direct UI `system()` explorer/about launch calls with `util::Platform::openUrl()`.
|
||||
- Reworked `util::Platform::openUrl()` and `openFolder()` so macOS/Linux launchers use `posix_spawnp()` arguments instead of shell-built command strings, and folder creation uses `std::filesystem::create_directories()`.
|
||||
- Added URL scheme validation for platform URL opens.
|
||||
- Added CMake visibility for missing `xxd` and Python theme expansion dependencies.
|
||||
- Added `xxd` and Python checks/package coverage to `setup.sh`.
|
||||
- Added shared `App::sendStopCommandSafely()` logging helper and routed repeated daemon `stop` RPC calls through it.
|
||||
- Confirmed `libs/incbin.h` is already represented in `THIRD_PARTY_LICENSES`, so no additional license entry was needed.
|
||||
- Verified with staged builds after logical batches, ending with `cd build && make -j$(nproc)` successfully linking `bin/ObsidianDragon`.
|
||||
|
||||
Remaining high-priority follow-ups from this audit include the larger `App`/security/refresh service boundaries and tests around the newly centralized runtime behavior.
|
||||
|
||||
### Phase 2 Completed On 2026-04-27
|
||||
|
||||
- Added schema-backed dialog layout tokens in `res/themes/ui.toml` and central accessors in `src/ui/layout.h` for common dialog widths, form width, action width/gap, max height ratio, and compact bottom alignment.
|
||||
- Registered the `dialog` UI schema section in `src/ui/schema/ui_schema.cpp` so those tokens are available through the existing schema cache.
|
||||
- Extended `material::BeginOverlayDialog()` with an optional ID suffix and added shared overlay action/footer helpers in `src/ui/material/draw_helpers.h`.
|
||||
- Migrated address book Add/Edit address dialogs in `src/ui/windows/address_book_dialog.cpp` from raw `ImGui::BeginPopupModal()` usage to `material::BeginOverlayDialog()` with one shared form/action renderer.
|
||||
- Added `src/ui/material/project_icons.h` as the wallet icon registry and moved pickaxe-specific font rendering behind that helper.
|
||||
- Kept `AddressLabelDialog::drawIconByName()` and `iconGlyphForName()` as compatibility wrappers for existing call sites.
|
||||
- Verified with `cd build && make -j$(nproc)` successfully linking `bin/ObsidianDragon`; diagnostics were clean on Phase 2 touched files, `git diff --check` passed, and scans found no remaining Add/Edit raw modal usage or local address-label icon arrays.
|
||||
|
||||
### Phase 3 Completed On 2026-04-27
|
||||
|
||||
- Added `src/util/async_task_manager.h/.cpp` as a named task owner with cancellation tokens, completed-task reaping, and join-on-shutdown behavior.
|
||||
- Routed App-owned daemon maintenance, wizard daemon stop/check, encryption daemon restart, and decrypt restart/import background work through `AsyncTaskManager` instead of detached App threads.
|
||||
- Kept `Bootstrap`'s worker joinable so its existing destructor cancellation can join the download/extract thread instead of losing ownership through `detach()`.
|
||||
- Added `src/daemon/daemon_controller.h/.cpp` as the first daemon ownership boundary; it now owns `EmbeddedDaemon`, syncs settings into the daemon, and centralizes start/stop calls while `App` keeps a non-owning bridge pointer for low-churn follow-up migration.
|
||||
- Extended `rpc::ConnectionConfig` with auth-source tracking and `use_tls`, parsed `rpctls`/`rpcssl`-style config flags, and centralized `.cookie` auth retry construction in `Connection::buildCookieAuthConfig()`.
|
||||
- Updated main, fast-lane, temporary stop, wizard stop, and decrypt-import RPC clients to pass the TLS flag to `RPCClient`.
|
||||
- Added a one-per-session runtime warning and Settings-page warning when a non-localhost RPC host is configured without TLS.
|
||||
- Verified after each logical batch with `cmake .. && make -j$(nproc)` or `make -j$(nproc)` successfully linking `bin/ObsidianDragon`; diagnostics were clean on Phase 3 touched files, `git diff --check` passed, and scans found no remaining App-owned detached background tasks or App-level manual `.cookie` fallback.
|
||||
|
||||
### Phase 4 Completed On 2026-04-27
|
||||
|
||||
- Added `src/services/refresh_scheduler.h/.cpp` and moved refresh interval/timer policy out of `App`, while leaving RPC refresh bodies behavior-preserving in the existing app/network methods.
|
||||
- Replaced App refresh timer fields with `services::RefreshScheduler`, including page-specific refresh policy, wallet mutation refresh marking, transaction-age throttling, OPID polling cadence, price refresh cadence, and fast mining/rescan ticks.
|
||||
- Moved generated language headers from tracked `src/embedded/lang_*.h` files into `${CMAKE_BINARY_DIR}/generated/embedded/`, keeping the existing `embedded/lang_*.h` include strings working through the generated include directory.
|
||||
- Added CTest infrastructure and `ObsidianDragonTests` with focused coverage for connection config parsing, cookie fallback and plaintext/TLS checks, payment URI parsing, fixed amount formatting, spendable wallet address filtering, and scheduler behavior.
|
||||
- Added `src/util/amount_format.h/.cpp` for shared fixed-decimal amount formatting and used it in transaction send payload construction.
|
||||
- Added pure spendability helpers in `src/data/wallet_state.h/.cpp` and routed Send-tab source address selection through them.
|
||||
- Split low-risk helpers out of large UI tabs: balance helper formatting/drawing code, mining formatting/estimate/thread helpers, and the console RPC command reference registry.
|
||||
- Verified focused tests with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`, then verified the UI split batch with `cd build && cmake .. && make -j$(nproc)` successfully linking `bin/ObsidianDragon`.
|
||||
|
||||
### Phase 5 Completed On 2026-04-27
|
||||
|
||||
- Added `src/services/wallet_security_controller.h/.cpp` and moved deferred wizard encryption/PIN state, connection retry throttling, PIN validation, and secure clearing behind a wallet-security boundary while keeping the existing RPC/UI behavior in `App`.
|
||||
- Added `src/services/network_refresh_service.h/.cpp` as the fuller refresh boundary around `RefreshScheduler`, named refresh jobs, in-flight job guards, and explicit queue-pressure skips for core, address, transaction, mining, and peer refreshes.
|
||||
- Extended `DaemonController` with shutdown/external-daemon policy decisions and routed `App::beginShutdown()` through that boundary.
|
||||
- Grouped Settings-page static UI state into `SettingsPageState` and first-run wizard static UI state into `WizardUiState`, preserving existing file-local behavior while making state ownership explicit.
|
||||
- Continued low-risk renderer splitting with console layout helpers, balance recent-transaction visual/amount helpers, and mining active-state/thread clamp helpers.
|
||||
- Expanded focused tests for daemon shutdown policy, wallet security transitions, network refresh job guards, renderer helpers, and generated resource fallback behavior.
|
||||
- Documented the `src/config/version.h` policy: it remains committed source for editor/release tooling compatibility, and CMake regenerates it from `src/config/version.h.in` during configure.
|
||||
- Verified Phase 5 batches with repeated `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure` runs.
|
||||
|
||||
### Phase 6 Completed On 2026-04-27
|
||||
|
||||
- Expanded `WalletSecurityController` with mockable RPC and secure-vault gateways for deferred encryption, unlock, export, import, key classification, import messaging, decrypt export naming, and secure handoff behavior.
|
||||
- Routed wallet encryption, deferred encryption retry, unlock, and key import/export classification through the wallet-security service boundary while preserving existing App-visible behavior.
|
||||
- Extended `DaemonController` with lifecycle decisions for manual restart, rescan, blockchain-data deletion, and bootstrap daemon stop sequencing, then routed the corresponding App flows through those decisions.
|
||||
- Promoted `NetworkRefreshService` to dispatch-ticket ownership with queue-depth telemetry, queue-pressure skips, in-flight skips, completion stats, cancellation, and stale-callback detection for named refresh jobs.
|
||||
- Continued renderer splitting with stable modules for recent transaction presentation, pool-worker default selection, and console output filtering.
|
||||
- Removed Settings-page compatibility aliases so the page now uses `SettingsPageState` fields directly.
|
||||
- Expanded focused integration-style tests with mock wallet-security RPC/vault collaborators, daemon lifecycle policy checks, refresh dispatch telemetry/stale-callback edges, and UI helper coverage.
|
||||
- Verified with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 7 completed on 2026-04-27:
|
||||
- `DaemonController` now owns lifecycle execution ordering for restart, rescan, blockchain-data deletion, and bootstrap stops through mockable `LifecycleRuntime` and `LifecycleTaskContext` collaborators.
|
||||
- App removed the temporary non-owning `embedded_daemon_` bridge; daemon state/output reads now route through `DaemonController` wrappers or explicit controller access.
|
||||
- Added `WalletSecurityWorkflow` for decrypt/export/import dialog phase, step, import-active state, and wallet file planning, so the dialog state can be tested without constructing `App`.
|
||||
- `NetworkRefreshService` now owns enqueue/callback wrapping for named refresh jobs, including queue-depth sampling, queue-pressure skips, stale callback suppression, and UI-thread callback handoff.
|
||||
- Core, address, transaction, mining, peer, and encryption refresh jobs now use the service enqueue wrapper instead of app-local dispatch-ticket boilerplate.
|
||||
- Split additional UI helpers into `balance_address_list`, `mining_benchmark`, and `console_input_model`, with focused tests for filtering/sorting, benchmark state estimates, history navigation, and autocomplete.
|
||||
- Expanded `ObsidianDragonTests` with mock daemon lifecycle execution tests, wallet workflow tests, refresh enqueue/stale-callback tests, and the new UI module coverage.
|
||||
- Verified after each logical batch with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 8 completed on 2026-04-27:
|
||||
- Added `WalletSecurityWorkflowExecutor` with mockable RPC, import, file, daemon, and secure-vault cleanup gateways; decrypt wallet unlock/export/backup/restart/import/cleanup sequencing now runs through that executor instead of nested App-local orchestration.
|
||||
- Completed named refresh enqueue coverage by routing price refresh through `NetworkRefreshService::enqueue(Job::Price)` and connect-time `getinfo`/`getwalletinfo` prefetch through `Job::ConnectionInit`.
|
||||
- Added daemon lifecycle collaborators for async/immediate task contexts and blockchain-data cleanup, reducing App-specific lifecycle runtime code where the extracted pieces are directly testable.
|
||||
- Continued large-view reduction with balance address-row layout/USD helpers, mining benchmark transition and pool saved/default helpers, and console command parse/result classification helpers.
|
||||
- Expanded `ObsidianDragonTests` for workflow executor edges, ConnectionInit enqueue coverage, daemon lifecycle adapters, and the new UI helper/model behavior.
|
||||
- Verified each logical Phase 8 batch with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 9 completed on 2026-04-27:
|
||||
- Added typed refresh result models, JSON parsers, and service-owned apply contracts on `NetworkRefreshService` for connection info, wallet encryption, core balance/sync, mining, peers, and price refreshes.
|
||||
- Routed `App` refresh callbacks through those contracts for connect-time prefetch, warmup info application, core refresh, encryption refresh, mining refresh, peer refresh, and price refresh while preserving the existing RPC call ordering.
|
||||
- Expanded `ObsidianDragonTests` with focused refresh result parsing/application coverage for balance/sync state, connection metadata, wallet lock state, mining history, peer lists, banned peers, and price history.
|
||||
- Documented remaining intentional process-wide ImGui state and legacy compatibility wrappers in `docs/ui-static-state.md`, and linked that policy from `docs/codebase-overview.md`.
|
||||
- Verified the Phase 9 implementation batch with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 10 completed on 2026-04-27:
|
||||
- Added `NetworkRefreshService::AddressRefreshResult` and routed address snapshot application through `applyAddressRefreshResult()` while preserving App-owned RPC collection and dirty-flag behavior.
|
||||
- Added service-owned transaction view-cache models plus `TransactionRefreshResult`/`TransactionCacheUpdate` so transaction refresh callbacks now apply `WalletState::transactions`, `last_tx_update`, `last_tx_block_height_`, `viewtx_cache_`, `send_txids_`, and confirmed transaction caches through one cache-update contract.
|
||||
- Moved z-viewtransaction output parsing and outgoing-send enrichment into `NetworkRefreshService` helpers, leaving only the RPC call order and per-cycle throttling in `App`.
|
||||
- Evaluated the remaining decrypt workflow orchestration and kept the nested UI step handoffs App-owned because those boundaries still carry progress updates, worker-thread callback ordering, and shutdown/token cancellation semantics.
|
||||
- Did not touch high-churn tab/dialog statics in this phase; `docs/ui-static-state.md` remains the policy for future behavior-adjacent UI state changes.
|
||||
- Expanded `ObsidianDragonTests` with address/transaction applicator coverage, view-transaction enrichment/cache-update tests, stale callback cancellation coverage, additional remote/TLS config parsing, and shutdown-cancellation lifecycle coverage.
|
||||
- Verified the Phase 10 implementation batch with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 11 completed on 2026-04-27:
|
||||
- Revisited the decrypt restart/import flow and kept the remaining UI handoff choreography App-owned because each boundary still corresponds to an explicit progress step, cancellation check, or background import transition. No partial executor move was made.
|
||||
- Moved additional address refresh construction into `NetworkRefreshService` helpers for shielded-address validation results, transparent address parsing, and unspent-output balance application while preserving the existing daemon RPC call order in `App::refreshAddressData()`.
|
||||
- Moved transaction pre-refresh snapshot construction and list parsing helpers into `NetworkRefreshService`, including shielded address snapshots, fully enriched txid snapshots, transparent transaction parsing, shielded receive parsing, and final transaction sorting. `App::refreshTransactionData()` still owns RPC call order and per-cycle `z_viewtransaction` throttling.
|
||||
- Expanded `ObsidianDragonTests` with focused coverage for the new address/transaction snapshot helpers, worker callback ordering across independent refresh jobs, and reconnect-style stale transaction callbacks.
|
||||
- Did not migrate additional tab/dialog statics because Phase 11 did not touch those views for behavior changes.
|
||||
- Verified with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 12 completed on 2026-04-27:
|
||||
- Added a shared ordered mock refresh RPC fixture in `ObsidianDragonTests` so service-level refresh collectors can assert exact daemon method ordering and parameters.
|
||||
- Introduced `NetworkRefreshService::RefreshRpcGateway` plus `collectAddressRefreshResult()` and `collectTransactionRefreshResult()`; address and transaction refresh RPC collection now lives behind the service boundary while preserving the same daemon call order and per-cycle `z_viewtransaction` cap.
|
||||
- Routed `App::refreshAddressData()` and `App::refreshTransactionData()` through a small `RPCClient` adapter for those collectors, keeping App focused on enqueueing and applying the returned results.
|
||||
- Expanded refresh lifecycle coverage for ordered callbacks, reconnect-style stale transaction callbacks, and collector ordering around address validation, z-balance fallback, shielded receive polling, cached viewtransaction entries, fresh `z_viewtransaction`, and `gettransaction` enrichment.
|
||||
- Revisited decrypt restart/import orchestration again and made no extra move because the remaining boundaries are still the explicit progress/cancellation/import handoff points.
|
||||
- Did not migrate additional tab/dialog statics because Phase 12 did not touch those views for behavior changes.
|
||||
- Verified with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 13 completed on 2026-04-27:
|
||||
- Added service-owned core and peer refresh collectors behind `NetworkRefreshService::RefreshRpcGateway`, moving the fixed `z_gettotalbalance`/`getblockchaininfo` and `getpeerinfo`/`listbanned` RPC bodies out of `App`.
|
||||
- Extended the ordered mock RPC fixture tests to assert the exact core and peer daemon call order, empty parameter arrays, parsed result values, and partial-failure behavior where the second RPC still runs after the first fails.
|
||||
- Kept warmup `getinfo`, mining refresh, connection init, and price fetch orchestration App-owned because those paths either have UI status handoffs, cadence-specific behavior, or non-RPC HTTP/callback details that were not part of this small collector move.
|
||||
- Did not add new reconnect/shutdown lifecycle tests because Phase 13 did not change async cancellation, daemon restart, or worker lifecycle ownership.
|
||||
- Revisited decrypt restart/import orchestration and high-churn UI state migrations by scope only; no behavior work touched those complete progress/cancellation or tab/dialog seams.
|
||||
- Verified with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 14 completed on 2026-04-27:
|
||||
- Added `NetworkRefreshService::collectMiningRefreshResult()` behind `RefreshRpcGateway`; the service now owns the ordered `getlocalsolps` plus optional `getmininginfo` RPC collection while `App` still owns the fast/slow cadence decision and daemon-memory snapshot.
|
||||
- Added `NetworkRefreshService::collectConnectionInitResult()` so the initial `getinfo` then `getwalletinfo` prefetch is service-owned while `App::onConnected()` keeps the high-priority enqueue and `encryption_state_prefetched_` lock-screen timing flag.
|
||||
- Extended ordered mock RPC tests for mining slow ticks, mining fast-only ticks, mining partial failure, connection-init success, and connection-init wallet-info prefetch after `getinfo` failure.
|
||||
- Did not add reconnect/shutdown lifecycle tests because Phase 14 did not change worker callback ownership, shutdown, reconnect, daemon restart, or cancellation behavior.
|
||||
- Did not touch decrypt orchestration or high-churn UI state because no complete behavior-preserving step in those areas was part of the batch.
|
||||
- Verified with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 15 completed on 2026-04-27:
|
||||
- Added `NetworkRefreshService::WarmupPollResult` and `collectWarmupPollResult()` behind `RefreshRpcGateway`; the service now owns the single warmup `getinfo` call and preserves either parsed connection info or the raw RPC error string.
|
||||
- Routed the warmup branch of `App::refreshCoreData()` through the new collector while keeping UI status translation, daemon block-height decoration, `connection_status_`, and the `refreshData()` transition App-owned.
|
||||
- Extended ordered mock RPC tests for warmup success and warmup failure so the `getinfo` call, empty params, parsed ready state, and error propagation are directly covered.
|
||||
- Reviewed price HTTP fetching and kept it App-owned because the current worker callback, libcurl setup, HTTP-status handling, logging, and parse/apply handoff are clearer in one place; only JSON response parsing remains service-owned.
|
||||
- Left command-style RPC actions App-owned because Phase 15 did not include behavior changes for send, address creation, mining toggles, ban operations, or import/export commands.
|
||||
- Did not add reconnect/shutdown lifecycle tests because worker callback ownership, shutdown, reconnect, daemon restart, and cancellation behavior did not change.
|
||||
- Did not touch decrypt orchestration or high-churn UI state because no complete behavior-preserving step in those areas was part of the batch.
|
||||
- Verified with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 16 completed on 2026-04-27:
|
||||
- Added `NetworkRefreshService::PriceHttpResponse`, `PriceHttpResult`, and `parsePriceHttpResponse()` so libcurl transport status, HTTP status, parse success, and failure messages are represented as a focused service-owned boundary.
|
||||
- Kept libcurl initialization, request execution, worker callback ownership, successful price logging, and UI-thread price application in `App::refreshPrice()`.
|
||||
- Expanded focused tests for successful price HTTP parsing, HTTP non-200 failures, transport failures, and unrecognized response bodies.
|
||||
- Kept command-style RPC actions App-owned because Phase 16 did not include a complete behavior change for send, address creation, mining toggles, ban operations, or import/export commands.
|
||||
- Did not add reconnect/shutdown lifecycle tests because callback ownership, cancellation, shutdown, reconnect, and daemon restart behavior did not change.
|
||||
- Did not touch decrypt orchestration or high-churn UI state because no behavior work touched those workflows.
|
||||
- Verified with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 17 completed on 2026-04-27:
|
||||
- Reviewed command-style RPC actions for send, address creation, mining toggles, ban operations, and key import/export; kept them App-owned because no complete behavior change created a focused extraction boundary with tests.
|
||||
- Reviewed lifecycle, reconnect, shutdown, and daemon restart ownership; no new lifecycle tests were added because callback ownership, cancellation, shutdown, reconnect, and daemon restart behavior did not change.
|
||||
- Reviewed decrypt and high-churn UI-state seams by scope; no changes were made because Phase 17 did not directly touch those workflows for behavior.
|
||||
- Kept refresh and price boundaries stable because the current service collectors/result helpers already cover the testable seams introduced in prior phases, and no new smaller behavior seam appeared.
|
||||
- Verified the stable-boundary decision with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 18 completed on 2026-04-27:
|
||||
- Re-reviewed the remaining command-style RPC actions (`setgenerate`, `setban`, new address creation, key import, send) and kept them App-owned because Phase 18 did not include a real behavior change that supplied a focused extraction boundary and tests.
|
||||
- Re-reviewed reconnect, shutdown, daemon restart, and callback ownership; no lifecycle tests were added because those ownership and cancellation behaviors did not change.
|
||||
- Re-reviewed decrypt and high-churn UI-state seams by scope; no code changes were made because those workflows were not directly touched for behavior.
|
||||
- Preserved the stable refresh and price boundaries introduced in prior phases because no new behavior made a smaller tested seam useful.
|
||||
- Verified the feature-driven cleanup decision with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 19 completed on 2026-04-27:
|
||||
- Treated the audit as feature-scoped maintenance rather than an extraction-only phase and made no source changes because no real behavior change created a new focused test seam.
|
||||
- Re-reviewed command-style RPC actions (`setgenerate`, `setban`, new address creation, key import, send) and kept them App-owned because their current boundaries are command-specific and tied to immediate UI/app-state updates.
|
||||
- Re-reviewed lifecycle, reconnect, shutdown, daemon restart, decrypt, and high-churn UI-state seams; no tests or migrations were added because those behaviors did not change directly.
|
||||
- Preserved the stable refresh and price service boundaries because the existing collectors/result helpers already cover the testable seams created by earlier work.
|
||||
- Verified the maintenance decision with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 20 completed on 2026-04-27:
|
||||
- Moved the cleanup audit into maintenance mode and avoided additional extraction-only work because no concrete behavior change created a smaller tested seam.
|
||||
- Kept command-style RPC actions App-owned until a complete command workflow change supplies focused tests.
|
||||
- Added no lifecycle, decrypt, or UI-state tests because callback ownership, cancellation, daemon restart, decrypt flow, and UI state behavior did not change directly.
|
||||
- Preserved stable refresh and price service boundaries because no new behavior exposed a better tested boundary.
|
||||
- Verified maintenance mode with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 21 completed on 2026-04-27:
|
||||
- Sustained maintenance mode and avoided extraction-only work because no concrete feature change created a focused tested seam.
|
||||
- Kept command-style RPC actions App-owned until a complete command workflow change supplies focused tests.
|
||||
- Added no lifecycle, decrypt, or UI-state coverage because callback ownership, cancellation, daemon restart, decrypt flow, and UI state behavior did not change directly.
|
||||
- Preserved stable refresh and price service boundaries because no new behavior exposed a smaller tested seam.
|
||||
- Verified sustained maintenance mode with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Phase 22 completed on 2026-04-27:
|
||||
- Continued sustained maintenance mode and avoided extraction-only work because no concrete feature change created a focused tested seam.
|
||||
- Kept command-style RPC actions App-owned until a complete command workflow change supplies focused tests.
|
||||
- Added no lifecycle, decrypt, or UI-state coverage because callback ownership, cancellation, daemon restart, decrypt flow, and UI state behavior did not change directly.
|
||||
- Preserved stable refresh and price service boundaries because no new behavior exposed a smaller tested seam.
|
||||
- Verified the maintenance checkpoint with `cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure`.
|
||||
|
||||
Remaining Phase 23 follow-ups:
|
||||
- Continue sustained maintenance mode and avoid extraction-only phases unless concrete feature work creates a focused tested seam.
|
||||
- Keep command-style RPC actions App-owned until a complete command workflow change supplies focused tests.
|
||||
- Add lifecycle, decrypt, or UI-state coverage only when directly changing callback ownership, cancellation, daemon restart, decrypt flow, or UI state behavior.
|
||||
- Preserve stable refresh and price service boundaries unless new behavior exposes a smaller tested seam.
|
||||
|
||||
## High-Priority Findings
|
||||
|
||||
### 1. `App` Is Carrying Too Many Responsibilities
|
||||
|
||||
Evidence:
|
||||
- `src/app.h`, `src/app.cpp`, `src/app_network.cpp`, `src/app_security.cpp`, and `src/app_wizard.cpp` form a broad partial-class style application layer.
|
||||
- `src/app.cpp` is 3,141 lines, with lifecycle, rendering, daemon/RPC startup, shutdown, modal rendering, and restart flows.
|
||||
- `src/app_network.cpp` is 1,926 lines and contains many refresh, polling, send, mining, market, peer, and address metadata paths.
|
||||
- `src/app_security.cpp` is 1,819 lines and combines wallet encryption, PIN handling, secure vault, import/export, and restart flows.
|
||||
|
||||
Why it matters:
|
||||
- Changes in one workflow can easily affect unrelated lifecycle or UI behavior.
|
||||
- It is hard to unit-test isolated flows without instantiating most of the app.
|
||||
- Shutdown and async callback ownership are difficult to reason about because many subsystems capture or mutate `App` state directly.
|
||||
|
||||
Recommended cleanup:
|
||||
- Extract a `DaemonController` for embedded/external daemon ownership, restart, port ownership, and shutdown sequencing.
|
||||
- Extract a `WalletSecurityController` for PIN, encryption, secure vault, and key import/export flow state.
|
||||
- Extract a `NetworkRefreshService` or `WalletRefreshScheduler` for balance/address/transaction/mining/market polling.
|
||||
- Keep `App` focused on initialization, top-level navigation, frame dispatch, and service composition.
|
||||
|
||||
Suggested first slice:
|
||||
- Completed in Phase 3: add the first `DaemonController` boundary around `EmbeddedDaemon` ownership, settings sync, and start/stop behavior. Follow-up work should move more restart/shutdown sequencing and external-daemon policy into the controller.
|
||||
|
||||
### 2. Background Thread Lifetime Is Not Centrally Owned
|
||||
|
||||
Evidence:
|
||||
- Detached threads appear in `src/app.cpp`, `src/app_security.cpp`, `src/app_wizard.cpp`, `src/util/bootstrap.cpp`, and `src/main.cpp`.
|
||||
- Several lambdas capture `this` and then call back after waits or daemon operations.
|
||||
- Managed thread members also exist (`shutdown_thread_`, `daemon_restart_thread_`, `wizard_stop_thread_`, monitor threads), but ownership patterns vary by subsystem.
|
||||
|
||||
Why it matters:
|
||||
- Detached work can outlive the `App` object or UI state it touches.
|
||||
- Shutdown order is harder to guarantee.
|
||||
- Cancellation is inconsistent, especially across daemon restart, wallet import, bootstrap download, and PIN unlock flows.
|
||||
|
||||
Recommended cleanup:
|
||||
- Introduce an `AsyncTaskManager` owned by `App`, with named tasks, cancellation flags/tokens, and join-on-shutdown behavior.
|
||||
- Replace new detached threads with submitted tasks and explicit cancellation.
|
||||
- Require background callbacks to hop through the existing UI callback queue before touching UI state.
|
||||
- Add shutdown assertions or logging for tasks that fail to stop promptly.
|
||||
|
||||
Suggested first slice:
|
||||
- Completed in Phase 3: convert App-owned daemon restart/import/wizard background work to `AsyncTaskManager` and keep `Bootstrap`'s worker joinable. Follow-up work should move remaining subsystem monitor/watchdog policies behind explicit owners where useful.
|
||||
|
||||
### 3. RPC Credential Generation And Remote RPC Handling Need Hardening
|
||||
|
||||
Evidence:
|
||||
- Before Phase 1, `src/rpc/connection.cpp` generated default `rpcuser`/`rpcpassword` with `std::srand(std::time(nullptr))` and `std::rand()`.
|
||||
- Phase 1 replaced that path with libsodium-backed random generation.
|
||||
- Before Phase 3, `src/rpc/rpc_client.cpp` always constructed an `http://host:port/` URL.
|
||||
- Before Phase 3, `.cookie` auth handling appeared in `src/rpc/connection.cpp` and also as a 401 fallback in `src/app_network.cpp`.
|
||||
|
||||
Why it matters:
|
||||
- `std::rand()` is not appropriate for credentials.
|
||||
- HTTP is acceptable for strict localhost daemon RPC, but remote hosts can expose credentials unless the UI explicitly warns or supports TLS.
|
||||
- Duplicate auth fallback paths make it harder to reason about precedence and failures.
|
||||
|
||||
Recommended cleanup:
|
||||
- Completed in Phase 1: generate RPC credentials with libsodium-backed random generation.
|
||||
- Completed in Phase 3: centralize `.cookie` retry construction in `Connection` after config-password failure.
|
||||
- Completed in Phase 3: add a `use_tls` transport flag to `ConnectionConfig` and pass it through `RPCClient` connections.
|
||||
- Completed in Phase 3: warn at runtime and in Settings when a non-localhost RPC host uses plaintext HTTP.
|
||||
|
||||
Suggested first slice:
|
||||
- Completed in Phase 1 and Phase 3: replace `std::rand()` credential generation, centralize cookie fallback, and add remote plaintext/TLS handling.
|
||||
|
||||
### 4. Shell Launching Uses `system()` In UI And Platform Paths
|
||||
|
||||
Evidence:
|
||||
- Before Phase 1, `src/util/platform.cpp` used `system()` for URL/folder opening and directory creation on some platforms.
|
||||
- Before Phase 1, `src/ui/windows/transactions_tab.cpp`, `src/ui/windows/transaction_details_dialog.cpp`, and `src/ui/windows/about_dialog.cpp` called `system()` directly.
|
||||
- Phase 1 routed these UI calls through `util::Platform::openUrl()` and removed shell-built launcher strings from `Platform` on macOS/Linux.
|
||||
|
||||
Why it matters:
|
||||
- Shell invocation increases quoting and command-injection risk.
|
||||
- Direct calls bypass the existing platform abstraction.
|
||||
- Behavior varies by shell and desktop environment.
|
||||
|
||||
Recommended cleanup:
|
||||
- Route all URL/file/folder launch behavior through `Platform` helpers.
|
||||
- Replace shell strings with platform APIs where possible: `ShellExecute` on Windows, `open` or `xdg-open` via `fork/exec` or `posix_spawn` on Unix-like systems.
|
||||
- Validate URL schemes before opening external links.
|
||||
- Remove direct `system()` calls from UI windows.
|
||||
|
||||
Suggested first slice:
|
||||
- Completed in Phase 1: update About, transaction details, and transaction tab actions to use `Platform::openUrl()` and safer platform launcher behavior.
|
||||
|
||||
## Medium-Priority Findings
|
||||
|
||||
### 5. Silent And Broad Exception Handling Is Common
|
||||
|
||||
Evidence:
|
||||
- Empty or near-empty `catch (...)` blocks appear in `src/app.cpp`, `src/app_network.cpp`, `src/app_security.cpp`, `src/app_wizard.cpp`, `src/rpc/rpc_worker.cpp`, `src/rpc/rpc_client.cpp`, `src/main.cpp`, and several UI files.
|
||||
- Repeated `try { rpc_->call("stop"); } catch (...) {}` patterns appear in daemon stop flows.
|
||||
|
||||
Why it matters:
|
||||
- Failures vanish from logs and UI, making support and diagnosis difficult.
|
||||
- Some swallowed failures are acceptable during best-effort shutdown, but the code does not consistently explain that intent.
|
||||
- Repeated patterns invite copy-paste drift.
|
||||
|
||||
Recommended cleanup:
|
||||
- Add small helpers such as `stopDaemonSafely(context)` and `parseFloatOrDefault(value, fallback, context)`.
|
||||
- Log best-effort failures with enough context to debug without spamming normal shutdown.
|
||||
- Prefer typed catches where the thrown type is known.
|
||||
- For RPC work, propagate categorized failures to the notification system when they affect user-visible state.
|
||||
|
||||
Suggested first slice:
|
||||
- Completed in Phase 1: replace repeated daemon stop catch blocks with a single helper that logs at debug/verbose level.
|
||||
|
||||
### 6. UI Dialogs Have Mixed Modal Systems And Repeated Layout Literals
|
||||
|
||||
Evidence:
|
||||
- Most dialogs use `Material::BeginOverlayDialog()`, but `src/ui/windows/address_book_dialog.cpp` still uses raw `ImGui::BeginPopupModal()` for Add/Edit address modals.
|
||||
- Dialog widths such as `480.0f`, `500.0f`, `620.0f`, and `660.0f` appear across UI files.
|
||||
- Button widths and padding literals such as `140.0f`, `160.0f`, and `24.0f` are repeated.
|
||||
- Icon font sizes are hardcoded in `src/ui/material/typography.cpp` as 14, 18, 24, and 40 px families.
|
||||
|
||||
Why it matters:
|
||||
- The schema system already solves this class of problem, but not all dimensions use it yet.
|
||||
- Modal behavior diverges when some dialogs bypass overlay scrim/input blocking helpers.
|
||||
- Theme and density changes require hunting through many files.
|
||||
|
||||
Recommended cleanup:
|
||||
- Add schema-backed dialog tokens under `res/themes/ui.toml`, such as `globals.dialog.width-default`, `width-lg`, `min-width`, and `max-height-ratio`.
|
||||
- Add schema-backed action widths under a shared `button` or `globals.button-sizes` section.
|
||||
- Move icon size selection into schema or a single icon token helper.
|
||||
- Migrate address book Add/Edit dialogs to `BeginOverlayDialog()` and extract one shared form renderer.
|
||||
|
||||
Suggested first slice:
|
||||
- Completed in Phase 2: refactor the address book Add/Edit dialog because it had duplicated modal form code and was still outside the overlay helper path.
|
||||
|
||||
### 7. UI State Uses Many Static Locals And File-Scoped Globals
|
||||
|
||||
Evidence:
|
||||
- `src/ui/pages/settings_page.cpp` has many `sp_*` static variables for page state.
|
||||
- `src/app_wizard.cpp` uses local statics for first-run appearance and daemon checks.
|
||||
- Theme/color/effect modules use static state for current theme and effect initialization.
|
||||
|
||||
Why it matters:
|
||||
- Static state is hard to reset in tests.
|
||||
- It makes multi-window or future session-reset behavior harder.
|
||||
- Initialization order and stale state can become subtle bugs after hot reloads or account switching.
|
||||
|
||||
Recommended cleanup:
|
||||
- Introduce `SettingsPageState` and `WizardState` structures owned by `App` or a UI state container.
|
||||
- Keep global/static state only for immutable constants or explicit process-wide singletons.
|
||||
- Add reset/apply methods for state structures to support theme reload and test setup.
|
||||
|
||||
Suggested first slice:
|
||||
- Move settings-page statics into a single struct without changing behavior. This is mostly mechanical and improves readability.
|
||||
|
||||
### 8. Build And Generated Resource Lifecycle Is Split Across Source, Build, And Scripts
|
||||
|
||||
Evidence:
|
||||
- `src/config/version.h` is generated from `src/config/version.h.in` but the generated file is present in source control.
|
||||
- CMake generates `src/embedded/lang_*.h` from `res/lang/*.json` with `xxd`.
|
||||
- `setup.sh` does not appear to check for `xxd`.
|
||||
- `find_package(Python3 QUIET COMPONENTS Interpreter)` is used for theme expansion; CMake falls back when Python is unavailable.
|
||||
- Font `OBJECT_DEPENDS` are listed manually in `CMakeLists.txt`.
|
||||
- `src/resources/embedded_resources.cpp` conditionally includes `embedded_data.h`, which is produced by release/build scripts for some platforms.
|
||||
|
||||
Why it matters:
|
||||
- Generated files under `src/` make the source tree noisier and can cause stale diffs.
|
||||
- Missing tools fail late or silently reduce build output quality.
|
||||
- Resource embedding behavior differs by platform without one obvious matrix.
|
||||
|
||||
Recommended cleanup:
|
||||
- Generate language headers under `build/generated/embedded/` and include that directory instead of writing under `src/embedded/`.
|
||||
- Decide whether `src/config/version.h` should be generated-only or committed, then enforce that decision with `.gitignore` and documentation.
|
||||
- Add explicit `xxd` checks to `setup.sh` and CMake configure output.
|
||||
- Make the Python theme-expansion fallback a warning, not a quiet behavior change.
|
||||
- Replace manual font `OBJECT_DEPENDS` with a generated list or CMake glob configured for the font directory.
|
||||
- Document the embedded resource matrix by platform and build type.
|
||||
|
||||
Suggested first slice:
|
||||
- Completed in Phase 1: add `xxd` detection and make missing Python theme expansion noisy.
|
||||
|
||||
### 9. Polling And Refresh Flow Would Benefit From A Scheduler Boundary
|
||||
|
||||
Evidence:
|
||||
- `src/app_network.cpp` coordinates many refresh paths and timings.
|
||||
- `src/app.cpp` also contains timed daemon/mining/rescan update logic.
|
||||
- `src/rpc/rpc_worker.cpp` already provides a worker queue, but refresh orchestration is still spread through app-level methods.
|
||||
|
||||
Why it matters:
|
||||
- Polling order, throttle behavior, and cancellation are difficult to audit.
|
||||
- Adding one more refresh path can accidentally affect responsiveness or RPC queue pressure.
|
||||
- Console fast-lane RPC is a good pattern, but regular refresh batches still need a clear scheduler owner.
|
||||
|
||||
Recommended cleanup:
|
||||
- Add a `PollingScheduler` or `RefreshScheduler` that owns timer intervals, dependencies, cancellation, and rate limiting.
|
||||
- Model refreshes as named jobs with last-run time, minimum interval, and in-flight state.
|
||||
- Keep `RPCWorker` as execution plumbing while moving scheduling decisions out of `App`.
|
||||
|
||||
Suggested first slice:
|
||||
- Completed in Phase 4: added `RefreshScheduler` for refresh timers, page intervals, transaction age throttling, OPID cadence, price refresh, and fast tick behavior.
|
||||
- Completed in Phase 7 and Phase 8: `NetworkRefreshService` now owns enqueue/callback wrapping for core, address, transaction, mining, peer, encryption, price, and connection-init jobs, including queue pressure and stale callback suppression.
|
||||
|
||||
### 10. Large UI Tabs Need Feature-Sliced Components
|
||||
|
||||
Evidence:
|
||||
- `src/ui/windows/balance_tab.cpp` is 3,562 lines.
|
||||
- `src/ui/windows/mining_tab.cpp` is 2,842 lines.
|
||||
- `src/ui/windows/console_tab.cpp` is 1,861 lines.
|
||||
- Several of these files combine state management, drawing, filtering, formatting, and modal coordination.
|
||||
|
||||
Why it matters:
|
||||
- UI changes become difficult to review because unrelated drawing and state logic share one file.
|
||||
- Reusable patterns stay local and get reimplemented elsewhere.
|
||||
- Testing smaller formatting/state helpers is harder when they are buried in render functions.
|
||||
|
||||
Recommended cleanup:
|
||||
- Split large tabs by feature area: panels, table/list renderers, local state structs, formatting helpers, and action handlers.
|
||||
- Keep ImGui draw calls close to the view but move data preparation and command decisions into smaller helpers.
|
||||
- Avoid introducing inheritance-heavy UI abstractions; simple namespaces and structs should be enough.
|
||||
|
||||
Suggested first slice:
|
||||
- Completed in Phase 4 through Phase 8: extracted low-risk balance helpers, balance address-list/recent-transaction models, mining benchmark/pool helpers, console command/input/output models, and additional address-row/mining/console execution helpers. Remaining work should continue moving draw-heavy action glue only when it lowers review/test cost.
|
||||
|
||||
## Low-Priority And Quick-Win Findings
|
||||
|
||||
### 11. Test Infrastructure Is Missing Or Not Obvious
|
||||
|
||||
Before Phase 4, no test target was visible in the workspace snapshot. Phase 4 added a small CTest executable covering connection config parsing, `.cookie` fallback priority, plaintext/TLS detection, wallet state filtering, amount formatting, payment URI parsing, and scheduler behavior.
|
||||
|
||||
Recommended cleanup:
|
||||
- Completed in Phase 4: add a small CTest target with a lightweight assertion harness for pure utility/RPC/scheduler helpers.
|
||||
- Consider migrating to Catch2 or GoogleTest if the test suite grows beyond a few focused files.
|
||||
- Add mocks for `RPCClient` and daemon process state once service boundaries exist.
|
||||
|
||||
### 12. Translation Key Usage Is Inconsistent
|
||||
|
||||
`TR()` and `TrId()` coexist in places such as Settings. This is workable, but it increases typo risk and makes missing translations harder to audit.
|
||||
|
||||
Recommended cleanup:
|
||||
- Define constants for high-traffic translation keys.
|
||||
- Prefer one translation call style inside each module.
|
||||
- Add a script that compares referenced keys against `res/lang/*.json`.
|
||||
|
||||
### 13. Project Icon Handling Needs One Registry
|
||||
|
||||
Material icons are referenced directly in many files, while the pickaxe icon uses a special one-glyph font path. The special case is now documented in `docs/codebase-overview.md`, but a code-level registry would make future icon work cleaner.
|
||||
|
||||
Recommended cleanup:
|
||||
- Completed in Phase 2: add `src/ui/material/project_icons.h` for app-specific icon names.
|
||||
- Completed in Phase 2: encapsulate pickaxe rendering behind one helper, so feature code does not need to know which font supplies it.
|
||||
|
||||
### 14. Release/Portability Docs Can Be Closer To The Real Build
|
||||
|
||||
`build.sh` contains much more platform-specific behavior than the README currently explains. AppImage dependency bundling, macOS universal build defaults, Windows MinGW assumptions, embedded daemon/resource behavior, and third-party license sync deserve their own build notes.
|
||||
|
||||
Recommended cleanup:
|
||||
- Add a focused `docs/build-and-release.md`.
|
||||
- Document development build vs release packaging per platform.
|
||||
- Add a checklist for third-party license updates, including `libs/incbin.h`.
|
||||
|
||||
## Suggested Roadmap
|
||||
|
||||
### Phase 1: Small High-Value Fixes
|
||||
|
||||
- [x] Replace weak RPC credential generation in `src/rpc/connection.cpp`.
|
||||
- [x] Route direct URL/open actions away from raw `system()` calls.
|
||||
- [x] Add `xxd` checks and noisy Python theme-expansion warnings.
|
||||
- [x] Confirm missing third-party license entry for INCBIN is not needed because `THIRD_PARTY_LICENSES` already includes it.
|
||||
- [x] Extract repeated daemon stop try/catch blocks into a helper with contextual logging.
|
||||
|
||||
### Phase 2: UI Consistency Pass
|
||||
|
||||
- [x] Move common dialog sizes and action button widths into `res/themes/ui.toml` or existing layout helpers.
|
||||
- [x] Migrate address book Add/Edit dialogs to `BeginOverlayDialog()`.
|
||||
- [x] Add a shared dialog content/footer helper.
|
||||
- [x] Add an icon registry and hide the pickaxe font special case behind it.
|
||||
|
||||
### Phase 3: Ownership And Runtime Boundaries
|
||||
|
||||
- [x] Introduce `AsyncTaskManager` for background work.
|
||||
- [x] Extract `DaemonController` from `App`.
|
||||
- [x] Consolidate RPC auth and connection fallback behavior.
|
||||
- [x] Add warning/TLS configuration for non-localhost RPC.
|
||||
|
||||
### Phase 4: Larger Maintainability Work
|
||||
|
||||
- [x] Extract `NetworkRefreshService` or `RefreshScheduler`.
|
||||
- [x] Split `balance_tab.cpp`, `mining_tab.cpp`, and `console_tab.cpp` into smaller feature files where safe.
|
||||
- [x] Move generated language headers into `build/generated`.
|
||||
- [x] Add focused unit tests for connection config, URI parsing, amount formatting, wallet address spendability filtering, and scheduler behavior.
|
||||
|
||||
### Phase 5: Service Boundaries And Test Depth
|
||||
|
||||
- [x] Move wallet encryption, PIN, secure vault, key import/export, and restart flow state toward a `WalletSecurityController`.
|
||||
- [x] Move more daemon restart/shutdown policy and external-daemon behavior from `App` into `DaemonController`.
|
||||
- [x] Evolve `RefreshScheduler` into a fuller network refresh service by modeling named refresh jobs, in-flight state, and RPC queue pressure explicitly.
|
||||
- [x] Split larger UI feature renderers after the low-risk helper extractions, starting with balance address/recent transaction panels and mining benchmark/pool panels.
|
||||
- [x] Move settings/wizard static UI state into explicit state structs.
|
||||
- [x] Add deeper tests around daemon/RPC service boundaries, scheduler edge cases, wallet security state transitions, and generated resource behavior.
|
||||
- [x] Decide and document whether `src/config/version.h` is committed source or generated-only output.
|
||||
|
||||
### Phase 6: Deeper Service Extraction And Integration Tests
|
||||
|
||||
- [x] Move wallet-security RPC orchestration, secure-vault handoffs, key import/export state, and daemon restart sequencing behind service interfaces that can be exercised without constructing the full UI `App`.
|
||||
- [x] Move daemon restart/rescan/bootstrap shutdown sequencing and external-daemon ownership policy into `DaemonController` with mockable collaborators.
|
||||
- [x] Promote `NetworkRefreshService` from refresh policy/guard owner to dispatcher owner for named RPC jobs, queue-pressure telemetry, and stale-callback handling.
|
||||
- [x] Split large UI files into stable feature modules, especially balance address-list/recent-transaction rendering, mining benchmark/pool rendering, and console output/input rendering.
|
||||
- [x] Replace remaining compatibility aliases around extracted UI state with direct state-struct use.
|
||||
- [x] Add integration tests with mock RPC/daemon collaborators for wallet security, daemon lifecycle, and refresh dispatch edges.
|
||||
|
||||
### Phase 7: Service-Owned Execution And UI Module Finish
|
||||
|
||||
- [x] Move async daemon lifecycle execution, restart delays, rescan flags, blockchain-data deletion, bootstrap stops, and external-daemon ownership enforcement deeper into `DaemonController` with mock daemon/filesystem/task collaborators.
|
||||
- [x] Extract wallet decrypt/export/import dialog workflow state and secure-vault restart handoffs into a wallet-security workflow service that can be tested without constructing `App`.
|
||||
- [x] Promote `NetworkRefreshService` from dispatch-ticket/telemetry owner to the enqueue/callback wrapper for named RPC refresh jobs, including stale callback suppression and queue-pressure reporting at the service boundary.
|
||||
- [x] Continue splitting balance address-list rendering, mining benchmark controls, and console input/history/autocomplete into stable modules with tests.
|
||||
- [x] Remove temporary App compatibility bridges around daemon ownership and any remaining extracted UI state once direct service/state use is complete.
|
||||
|
||||
### Phase 8: Workflow Executors And Large-View Reduction
|
||||
|
||||
- [x] Move wallet decrypt import/restart RPC orchestration out of `App`'s nested lambdas into a workflow executor with mock RPC/daemon/vault/file collaborators.
|
||||
- [x] Split concrete daemon lifecycle runtime responsibilities into smaller adapters only where it lowers App coupling without adding indirection for its own sake.
|
||||
- [x] Complete named refresh enqueue coverage for price/market refresh and connect-time one-off polling.
|
||||
- [x] Continue feature-slicing large ImGui views by extracting address-row, mining benchmark/pool, and console command execution helper/model seams.
|
||||
- [x] Add focused tests around the new workflow, refresh, daemon lifecycle, and UI helper seams.
|
||||
|
||||
### Phase 9: Typed Refresh Results And State Cleanup
|
||||
|
||||
- [x] Evolve refresh jobs toward typed result models and service-owned result application contracts for refresh paths that still require complex app-local snapshots.
|
||||
- [x] Review remaining static UI state and compatibility glue, then move it to explicit state structs or document what is intentionally process-wide.
|
||||
- [x] Reviewed draw-heavy ImGui bodies and deferred additional slicing to Phase 10 unless adjacent to a required behavior-preserving change.
|
||||
- [x] Add focused tests around refresh result application, reconnect/stale-callback edges, and any state-struct migrations.
|
||||
|
||||
### Phase 10: Transaction Refresh And State Follow-Through
|
||||
|
||||
- [x] Evaluate typed address/transaction refresh result models and cache-update applicators for the refresh paths that still build large App-local snapshots.
|
||||
- [x] Reduce decrypt workflow orchestration in `App` further only where executor-owned operations can preserve progress reporting and cancellation behavior; Phase 10 evaluated the remaining chain and deferred extra movement because the current App handoffs are the progress/cancellation boundary.
|
||||
- [x] Convert remaining high-churn tab/dialog statics to explicit state structs as those views are touched, following `docs/ui-static-state.md`; Phase 10 did not touch those views for behavior changes.
|
||||
- [x] Add stronger reconnect/stale-callback, remote/TLS RPC, and shutdown-cancellation tests.
|
||||
|
||||
### Phase 11: Workflow And UI State Hardening
|
||||
|
||||
- [x] Revisit decrypt restart/import orchestration for one complete executor-owned step if progress and cancellation can stay explicit.
|
||||
- [x] Continue high-churn tab/dialog state-struct migrations only when those views are touched for real behavior changes.
|
||||
- [x] Move more address/transaction RPC snapshot construction into testable helpers if it reduces App coupling without duplicating daemon call ordering.
|
||||
- [x] Keep broadening refresh/RPC lifecycle tests around realistic worker callback ordering and reconnect races.
|
||||
|
||||
### Phase 12: Integration Fixtures And Remaining Orchestration Edges
|
||||
|
||||
- [x] Add shared mock RPC fixtures only if they let tests assert daemon call ordering directly instead of duplicating production sequencing in test setup.
|
||||
- [x] Move more refresh body construction out of `App` only where call ordering remains obvious and covered by tests.
|
||||
- [x] Revisit decrypt restart/import orchestration only if a complete progress/cancellation step can be executor-owned end to end.
|
||||
- [x] Continue UI state-struct migrations opportunistically when behavior work touches those tabs/dialogs.
|
||||
|
||||
### Phase 13: Ordered Collectors And Lifecycle Coverage
|
||||
|
||||
- [x] Extend ordered mock RPC coverage to additional refresh collectors only when those collectors move beyond App-owned call bodies.
|
||||
- [x] Keep refresh call ordering explicit in tests whenever daemon call sequencing moves into a service.
|
||||
- [x] Broaden reconnect/shutdown lifecycle coverage around async refresh cancellation only when those flows are touched.
|
||||
- [x] Continue decrypt and UI-state hardening opportunistically at complete, behavior-preserving seams.
|
||||
|
||||
### Phase 14: Mining And Connection Edge Review
|
||||
|
||||
- [x] Move mining refresh collection only if the fast/slow polling cadence remains explicit and covered by ordered mock RPC tests.
|
||||
- [x] Move connection-init prefetch only if lock-screen timing and `getinfo`/`getwalletinfo` ordering remain obvious and tested.
|
||||
- [x] Add lifecycle coverage only when changing worker callback, shutdown, reconnect, or daemon restart ownership.
|
||||
- [x] Continue decrypt and UI-state work only when a complete behavior-preserving step is touched.
|
||||
|
||||
### Phase 15: Remaining App-Owned Edges
|
||||
|
||||
- [x] Review warmup status polling only if the UI status/progress handoff can stay explicit and covered.
|
||||
- [x] Review price HTTP fetching only if network parsing, callback behavior, and failure handling become easier to test without hiding libcurl details.
|
||||
- [x] Leave command-style RPC actions App-owned unless a complete behavior change creates a focused extraction boundary.
|
||||
- [x] Add lifecycle, decrypt, or UI-state tests only when touching those behaviors directly.
|
||||
|
||||
### Phase 16: Price And Command Boundary Review
|
||||
|
||||
- [x] Revisit price HTTP fetching only if libcurl status/error behavior can be represented as a focused testable boundary.
|
||||
- [x] Keep send, address creation, mining toggle, ban, and import/export command RPC actions App-owned unless a complete behavior change justifies extraction.
|
||||
- [x] Add reconnect/shutdown lifecycle coverage only when callback ownership or cancellation behavior changes.
|
||||
- [x] Continue decrypt and UI-state work only when directly touching those workflows for behavior.
|
||||
|
||||
### Phase 17: Command And Lifecycle Guardrails
|
||||
|
||||
- [x] Review command-style RPC actions only when a complete behavior change creates a focused extraction boundary.
|
||||
- [x] Add reconnect/shutdown lifecycle coverage only when callback ownership, cancellation, shutdown, reconnect, or daemon restart behavior changes.
|
||||
- [x] Continue decrypt and UI-state work only when directly touching those workflows for behavior.
|
||||
- [x] Keep existing refresh and price boundaries stable unless new behavior creates a smaller tested seam.
|
||||
|
||||
### Phase 18: Feature-Driven Cleanup Only
|
||||
|
||||
- [x] Move command-style RPC actions only when a real behavior change supplies a focused extraction boundary and tests.
|
||||
- [x] Add lifecycle tests only when callback ownership, cancellation, shutdown, reconnect, or daemon restart behavior changes.
|
||||
- [x] Continue decrypt and UI-state cleanup only when those workflows are touched for behavior.
|
||||
- [x] Preserve stable refresh and price boundaries unless new behavior makes a smaller tested seam useful.
|
||||
|
||||
### Phase 19: Feature-Scoped Maintenance
|
||||
|
||||
- [x] Prefer feature-scoped maintenance over additional extraction-only phases.
|
||||
- [x] Move command-style RPC actions only with a complete behavior change and focused tests.
|
||||
- [x] Add lifecycle/decrypt/UI-state coverage only when those behaviors change directly.
|
||||
- [x] Preserve existing refresh and price service boundaries unless new behavior exposes a smaller tested seam.
|
||||
|
||||
### Phase 20: Maintenance Mode
|
||||
|
||||
- [x] Convert the cleanup roadmap from extraction phases to maintenance-mode guidance.
|
||||
- [x] Keep command-style RPC actions App-owned unless a complete feature change supplies focused tests.
|
||||
- [x] Add lifecycle/decrypt/UI-state coverage only when those behaviors change directly.
|
||||
- [x] Preserve stable refresh and price service boundaries unless new behavior exposes a smaller tested seam.
|
||||
|
||||
### Phase 21: Sustained Maintenance Mode
|
||||
|
||||
- [x] Continue maintenance-mode cleanup only when concrete feature work creates a focused tested seam.
|
||||
- [x] Keep command-style RPC actions App-owned unless a complete command workflow change supplies focused tests.
|
||||
- [x] Add lifecycle/decrypt/UI-state coverage only when those behaviors change directly.
|
||||
- [x] Preserve stable refresh and price service boundaries unless new behavior exposes a smaller tested seam.
|
||||
|
||||
### Phase 22: Maintenance Checkpoint
|
||||
|
||||
- [x] Continue sustained maintenance mode and avoid extraction-only phases unless concrete feature work creates a focused tested seam.
|
||||
- [x] Keep command-style RPC actions App-owned unless a complete command workflow change supplies focused tests.
|
||||
- [x] Add lifecycle/decrypt/UI-state coverage only when those behaviors change directly.
|
||||
- [x] Preserve stable refresh and price service boundaries unless new behavior exposes a smaller tested seam.
|
||||
|
||||
### Phase 23: Maintenance Checkpoint
|
||||
|
||||
- [ ] Continue sustained maintenance mode and avoid extraction-only phases unless concrete feature work creates a focused tested seam.
|
||||
- [ ] Keep command-style RPC actions App-owned unless a complete command workflow change supplies focused tests.
|
||||
- [ ] Add lifecycle/decrypt/UI-state coverage only when those behaviors change directly.
|
||||
- [ ] Preserve stable refresh and price service boundaries unless new behavior exposes a smaller tested seam.
|
||||
|
||||
## Residual Risk
|
||||
|
||||
Several findings are maintainability risks rather than confirmed user-facing bugs. Remote/plaintext RPC handling now warns and has a TLS config path, and Phase 10 added focused parsing tests, but real daemon TLS setups still deserve manual validation.
|
||||
|
||||
The safest next step is Phase 23: continue sustained maintenance mode, preserving tested boundaries and tying future cleanup to concrete feature work.
|
||||
@@ -1,76 +0,0 @@
|
||||
# Codebase Overview
|
||||
|
||||
Current as of 2026-04-27 for ObsidianDragon `1.2.0-rc1`.
|
||||
|
||||
## Purpose
|
||||
|
||||
ObsidianDragon is a Dear ImGui full-node wallet for DragonX (DRGX). It manages an embedded or external `dragonxd`, renders a schema-driven desktop UI, and provides shielded transactions, mining, market data, address management, explorer tools, and bootstrap download support.
|
||||
|
||||
## Runtime Architecture
|
||||
|
||||
- `src/main.cpp` initializes SDL3, graphics backends, ImGui, and the main loop.
|
||||
- `src/app.cpp` owns application lifecycle, navigation, rendering, dialogs, and daemon/RPC startup.
|
||||
- `src/app_network.cpp` contains refresh and transaction flows, including balance, address, transaction, mining, market, peer, and address metadata updates.
|
||||
- `src/app_security.cpp` covers wallet encryption, PIN unlock, auto-lock, and secure vault integration.
|
||||
- `src/app_wizard.cpp` handles first-run setup and bootstrap/encryption flow.
|
||||
|
||||
The UI thread renders ImGui and drains callback queues. RPC work runs through `RPCWorker`, which posts worker-thread RPC calls and returns UI-thread callbacks. Console commands can use a separate fast-lane RPC client/worker so they do not queue behind regular refresh batches.
|
||||
|
||||
`NetworkRefreshService` owns refresh timing, named job enqueue/callback guards, typed refresh result models, and service-owned result applicators for connection, core balance/sync, encryption, mining, peers, price, address snapshots, and transaction cache updates. Warmup polling, connection-init, core, mining, peer, address, and transaction refresh collection now run through a testable `RefreshRpcGateway`, and price HTTP response evaluation has a service-owned status/error/result model. `App` keeps enqueueing, UI timing decisions, cadence decisions, libcurl execution, and application handoff code for those paths.
|
||||
|
||||
## Source Map
|
||||
|
||||
| Path | Role |
|
||||
|------|------|
|
||||
| `src/config/` | Settings JSON persistence and generated `version.h` |
|
||||
| `src/data/` | Wallet state, address book, exchange info |
|
||||
| `src/rpc/` | libcurl JSON-RPC client, connection config, worker queue |
|
||||
| `src/daemon/` | Embedded `dragonxd` manager and xmrig pool miner manager |
|
||||
| `src/ui/windows/` | Main tabs and modal dialogs |
|
||||
| `src/ui/pages/` | Page-style screens such as Settings |
|
||||
| `src/ui/schema/` | TOML schema loader, skin manager, color resolver |
|
||||
| `src/ui/material/` | Material-style typography, layout, drawing, components |
|
||||
| `src/ui/effects/` | Acrylic, blur, noise, theme effects, low-spec fallback |
|
||||
| `src/resources/` | Embedded resource extraction for params, daemon assets, themes, images |
|
||||
| `src/platform/` | Windows DX11 and backdrop helpers |
|
||||
| `src/util/` | i18n, logging, platform paths, bootstrap, vault, URI/base64/texture utilities |
|
||||
|
||||
## Build And Resources
|
||||
|
||||
- CMake uses C++17 and outputs `build/bin/ObsidianDragon`.
|
||||
- Version comes from `project(... VERSION 1.2.0)` plus `DRAGONX_VERSION_SUFFIX=-rc1`.
|
||||
- `src/config/version.h` is intentionally committed source for editor/release tooling compatibility, but CMake regenerates it from `src/config/version.h.in` during configure. Version bumps should update the CMake version/suffix and commit the regenerated header with the template if it changes.
|
||||
- SDL3 is found from the system first, then fetched by CMake if unavailable.
|
||||
- nlohmann/json and toml++ are fetched with CMake FetchContent.
|
||||
- libcurl is system-provided on Linux/macOS and fetched statically for Windows.
|
||||
- libsodium is system-provided on Linux or local under `libs/libsodium/`, `libs/libsodium-win/`, or `libs/libsodium-mac/`.
|
||||
- Fonts are embedded with INCBIN: Ubuntu, Material Icons, a one-glyph MDI pickaxe subset, and Noto CJK subset.
|
||||
- `res/themes/ui.toml` is embedded as a fallback and expanded into build themes with `scripts/expand_themes.py`.
|
||||
- `res/default_banlist.txt` is embedded into `build/generated/default_banlist_embedded.h`.
|
||||
|
||||
## RPC And Daemon Notes
|
||||
|
||||
- Default RPC port is `21769`.
|
||||
- `RPCClient` uses local HTTP with Basic auth. TLS is not assumed for localhost daemon RPC.
|
||||
- DragonX daemon config paths:
|
||||
- Linux: `~/.hush/DRAGONX/DRAGONX.conf`
|
||||
- Windows: `%APPDATA%/Hush/DRAGONX/DRAGONX.conf`
|
||||
- macOS: `~/Library/Application Support/Hush/DRAGONX/DRAGONX.conf`
|
||||
- `Connection::autoDetectConfig()` creates missing config files, appends `exportdir`, `experimentalfeatures=1`, and `developerencryptwallet=1`, and falls back to `.cookie` auth if no `rpcpassword` is configured.
|
||||
- The embedded daemon detects an external daemon on the RPC port and connects to it instead of taking ownership.
|
||||
- Chain args include TLS-only mode, adaptive `-dbcache`, DragonX asset parameters, node seeds `node.dragonx.is` through `node4.dragonx.is`, and optional `-maxconnections=<n>` from Settings.
|
||||
|
||||
## UI And Data Notes
|
||||
|
||||
- Sidebar navigation is driven by `NavPage`: Overview, Send, Receive, History, Mining, Market, Console, Network, Explorer, Settings.
|
||||
- Explorer lives in `src/ui/windows/explorer_tab.cpp`; Settings uses `src/ui/pages/settings_page.cpp`.
|
||||
- Address labels, icons, favorites, hidden state, and manual ordering are persisted in Settings, especially `address_meta`.
|
||||
- `AddressInfo::has_spending_key` tracks view-only shielded addresses; send flows filter or reject non-spendable z-addresses.
|
||||
- The pickaxe icon is not a normal `ICON_MD_*` glyph. Use `AddressLabelDialog::drawIconByName()` or `Typography::pickaxeFontForSize()` for that special case.
|
||||
- Remaining process-wide ImGui state and legacy compatibility wrappers are documented in `docs/ui-static-state.md`.
|
||||
- Warmup, connection-init, core, mining, peer, address, and transaction refreshes use typed `NetworkRefreshService` result contracts plus gateway-backed collectors. Price refresh keeps libcurl setup/execution and callback ownership in App, while `NetworkRefreshService` owns JSON parsing plus HTTP status/error result evaluation. App still owns warmup status handoffs, mining fast/slow cadence, and command-style RPC actions. Transaction application updates `WalletState`, `viewtx_cache_`, `send_txids_`, the confirmed transaction cache, and block-height markers as one cache-update operation.
|
||||
|
||||
## Remaining Work
|
||||
|
||||
- Investigate `todo.md`: determine whether DragonX/Komodo wallet storage supports a safe compaction or consolidation workflow for wallets with too many addresses.
|
||||
- Phase 23 should continue sustained maintenance mode: keep refresh and price boundaries stable, and revisit command/lifecycle/decrypt/UI-state seams only when concrete feature work creates a focused tested boundary.
|
||||
@@ -1,17 +0,0 @@
|
||||
# UI Static State Policy
|
||||
|
||||
The wallet currently runs one `App` instance inside one ImGui context. Some UI state is still file-static because it represents process-wide tab or modal state that must persist across frames. Phase 9 reviewed these remaining cases and treats them as intentional until the owning view is next refactored.
|
||||
|
||||
## Intentional Process-Wide State
|
||||
|
||||
- Modal payload state in `src/ui/windows/*_dialog*` files owns one open dialog instance at a time. These statics are acceptable for singleton dialogs, but new async workflow state should live in a service or App-owned controller.
|
||||
- Tab-local form, filter, and selection state in views such as Send, Receive, Market, Mining, Peers, Transactions, and Explorer persists user input across frames. When those views are touched for behavior changes, prefer an explicit `*TabState` struct over adding more independent statics.
|
||||
- Material/theme/effect singletons hold process-wide rendering caches or current theme state. They should remain resettable only through their owning theme/effect APIs.
|
||||
- Compatibility glue remains where external call sites still rely on legacy names: `App::setCurrentTab()`/`getCurrentTab()`, `WalletState` balance aliases, layout `k*` accessors, and icon helper wrappers. Remove these only after all call sites have moved to the newer names.
|
||||
|
||||
## Rules For New UI Code
|
||||
|
||||
- Add new mutable UI state to an existing explicit state struct when one exists.
|
||||
- Use file-static state only for singleton UI surfaces that cannot have multiple instances in the current app model.
|
||||
- Keep long-running workflow progress outside render functions and expose it through services or App-owned controllers.
|
||||
- Document any new compatibility wrapper with the call sites it protects and delete it when the migration is complete.
|
||||
27
tests/fixtures/hushchat/CAPTURE_MANIFEST.md
vendored
27
tests/fixtures/hushchat/CAPTURE_MANIFEST.md
vendored
@@ -1,27 +0,0 @@
|
||||
# HushChat Capture Manifest
|
||||
|
||||
The capture manifest is a redacted provenance file for a staged directory of real, disposable, non-sensitive SilentDragonXLite HushChat vectors. It does not replace fixture validation; it records that the staged directory was handled under the safety rules required before the Batch 11 strict replacement dry run.
|
||||
|
||||
Copy `templates/capture-manifest.template.json` into the staged fixture directory as `capture-manifest.json`, then change `status` to `staged` and each category `status` to `ready`. Keep the file limited to provenance, handling flags, category filenames, and the dry-run command.
|
||||
|
||||
Validate only the manifest metadata with:
|
||||
|
||||
```sh
|
||||
./build/bin/HushChatFixtureCheck --validate-capture-manifest /path/to/staged/hushchat-fixtures
|
||||
```
|
||||
|
||||
The validator accepts a directory containing `capture-manifest.json` or a direct manifest file path. It prints only a redacted report with schema status, category coverage, handling-flag counts, basenames, and error names.
|
||||
|
||||
The manifest must include:
|
||||
- schema `dragonx.hushchat.capture-manifest.v1`
|
||||
- status `staged`
|
||||
- a redacted manifest id
|
||||
- staged fixture directory name
|
||||
- dry-run command containing `HushChatFixtureCheck --replacement-dry-run`
|
||||
- provenance fields for source client, source client version or commit, capture date, network, and capture method
|
||||
- all required handling flags set to `true`
|
||||
- exactly one entry for each required fixture category
|
||||
|
||||
The manifest must not include passphrases, plaintext, memo contents, private keys, wallet files, ciphertext byte dumps, stored chat key fields, public-key fields, secretstream headers, derived keys, session keys, or any fixture object. The manifest validator rejects known prohibited field names, but it is still a metadata guard rather than a secret scanner.
|
||||
|
||||
After the manifest validates, run the strict replacement dry run against the same staged directory. Pending checked-in fixtures must not be replaced until both commands succeed.
|
||||
40
tests/fixtures/hushchat/IMPORT_CHECKLIST.md
vendored
40
tests/fixtures/hushchat/IMPORT_CHECKLIST.md
vendored
@@ -1,40 +0,0 @@
|
||||
# HushChat Fixture Import Checklist
|
||||
|
||||
This checklist is for replacing the pending placeholders with real, non-sensitive SilentDragonXLite compatibility vectors.
|
||||
|
||||
Do not commit passphrases, plaintext, memo contents from real wallets, private keys, wallet files, or arbitrary ciphertext dumps. Use only disposable test wallets and non-sensitive test messages. The checked-in ready files must contain only the schema fields required by the loader.
|
||||
|
||||
Required categories:
|
||||
- `incoming_memo`: received encrypted Memo vector, expected `ClientRx`
|
||||
- `outgoing_memo`: outgoing-history encrypted Memo vector, expected `ServerTx`
|
||||
- `seed_public_key_projection`: vector proving the SDXL UTF-8-hex seed projection matches the recorded local public key
|
||||
- `corrupted_auth_failure`: structurally valid Memo vector reserved for future authentication-failure verification
|
||||
- `cont_exclusion`: contact request vector that remains excluded from encrypted Memo decrypt preparation
|
||||
|
||||
Capture rules:
|
||||
- Use disposable SilentDragonXLite wallets only.
|
||||
- Use a fixed non-sensitive test phrase and record only its hash if a plaintext expectation is needed.
|
||||
- Do not store decrypted message text in fixture files.
|
||||
- Do not include private keys, viewing keys, spending keys, wallet seed phrases, or wallet database contents.
|
||||
- Keep the top-level `schema` value as `dragonx.hushchat.compat-fixture.v1`.
|
||||
- Change `status` from `pending` to `ready` only when the fixture has the full `fixture` object documented in the protocol spec.
|
||||
- Preserve one fixture file per required category.
|
||||
- For every ready non-`Cont` vector, the stored SDXL chat key string must project to the declared local public key using SDXL's first-32-UTF-8-bytes seed behavior.
|
||||
- The projection check records only byte lengths and match status; it must not write passphrases, plaintext, derived secret keys, memo contents, or ciphertext bytes.
|
||||
- A ready `corrupted_auth_failure` vector must be structurally valid through the same loader, verifier, and projection checks, then marked as requiring a future secretstream authentication failure.
|
||||
- The corrupted-auth marker is not a decrypt result and is not an authentication result.
|
||||
- Run the strict replacement dry-run report against a staged directory before copying any ready files over the checked-in pending placeholders.
|
||||
- The dry-run report must remain redacted; it may contain category names, basenames, status/error names, boolean flags, and counts only.
|
||||
- Add `capture-manifest.json` to the staged directory from `templates/capture-manifest.template.json` and validate it before strict replacement dry-run checks.
|
||||
- The capture manifest records provenance, handling flags, category filenames, and dry-run instructions only; it must not contain fixture objects or sensitive fields.
|
||||
|
||||
Verification flow:
|
||||
|
||||
```sh
|
||||
./build/bin/HushChatFixtureCheck --allow-pending tests/fixtures/hushchat
|
||||
./build/bin/HushChatFixtureCheck --validate-capture-manifest /path/to/staged/hushchat-fixtures
|
||||
./build/bin/HushChatFixtureCheck --replacement-dry-run /path/to/staged/hushchat-fixtures
|
||||
./build/bin/HushChatFixtureCheck tests/fixtures/hushchat
|
||||
```
|
||||
|
||||
The first command is for the current scaffold state and allows pending files. The manifest command validates only redacted staged-directory metadata. The dry-run command is strict, refuses `--allow-pending`, performs no file replacement, and must fail until the staged directory contains all five real ready vectors with no pending, malformed, mismatched, projection-failed, auth-not-ready, or Cont-not-excluded entries. The final command is the post-copy strict check on the checked-in fixture directory; it must report `future_auth_required=1` and `auth_structural_ready=1`. A ready import is acceptable only when the manifest command and both strict fixture commands exit successfully.
|
||||
31
tests/fixtures/hushchat/README.md
vendored
31
tests/fixtures/hushchat/README.md
vendored
@@ -1,31 +0,0 @@
|
||||
# HushChat Compatibility Fixtures
|
||||
|
||||
This directory contains the checked-in fixture-file schema for non-sensitive HushChat compatibility vectors.
|
||||
|
||||
The current files are intentionally marked `pending`. They define the exact vector categories and required fields without pretending that real SilentDragonXLite compatibility data has been captured yet.
|
||||
|
||||
Use the developer checker after building:
|
||||
|
||||
```sh
|
||||
./build/bin/HushChatFixtureCheck --allow-pending tests/fixtures/hushchat
|
||||
```
|
||||
|
||||
The command above is expected to pass while files are still pending. Before replacing pending placeholders with real `ready` vectors, run the same command without `--allow-pending`; it exits successfully only when all five required categories are supplied exactly once, pass the fixture loader/import checklist, satisfy the seed/public-key projection verifier, and mark the corrupted-auth vector as structurally ready for a future authentication-failure check.
|
||||
|
||||
For a strict replacement dry run against a staged directory of real vectors, use:
|
||||
|
||||
```sh
|
||||
./build/bin/HushChatFixtureCheck --replacement-dry-run /path/to/staged/hushchat-fixtures
|
||||
```
|
||||
|
||||
The dry-run mode refuses pending vectors and prints only a redacted replacement report with categories, basenames, status/error names, boolean flags, and aggregate counts. It does not copy files or print key material, memo contents, plaintext, ciphertext bytes, or hashes.
|
||||
|
||||
Before running the strict replacement dry run on real staged vectors, add a redacted capture manifest and validate it:
|
||||
|
||||
```sh
|
||||
./build/bin/HushChatFixtureCheck --validate-capture-manifest /path/to/staged/hushchat-fixtures
|
||||
```
|
||||
|
||||
The manifest template lives at `templates/capture-manifest.template.json`. See `CAPTURE_MANIFEST.md` for the metadata rules.
|
||||
|
||||
See `IMPORT_CHECKLIST.md` for the capture rules.
|
||||
Reference in New Issue
Block a user