diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..b6a9d78 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,154 @@ +# ObsidianDragon — Architecture Guide + +A native C++17 desktop wallet for DragonX (DRGX), built with ImGui + SDL3. + +## Directory Layout + +``` +src/ +├── main.cpp Entry point, SDL3/backend init, render loop +├── app.h / app.cpp App class: init, shutdown, frame dispatch +├── app_network.cpp RPC connection, data refresh, mining ops +├── app_security.cpp Encryption, lock screen, PIN management +├── app_wizard.cpp First-run setup wizard +├── config/ +│ └── settings.h/.cpp JSON settings persistence (~/.config/ObsidianDragon/) +├── daemon/ +│ ├── embedded_daemon.h/.cpp dragonxd process lifecycle +│ └── xmrig_manager.h/.cpp xmrig-hac pool mining management +├── rpc/ +│ ├── rpc_client.h/.cpp JSON-RPC over HTTPS (libcurl) +│ ├── rpc_worker.h/.cpp Background work queue + main-thread drain +│ ├── connection.h/.cpp DRAGONX.conf auto-detection +│ └── types.h Transaction, address data types +├── ui/ +│ ├── sidebar.h NavPage enum, sidebar rendering +│ ├── notifications.h/.cpp Toast notification system +│ ├── schema/ +│ │ ├── ui_schema.h/.cpp UISchema singleton (TOML → typed lookups) +│ │ ├── skin_manager.h/.cpp Skin enumeration, switching, import +│ │ └── color_var_resolver.h/.cpp CSS-style color resolution +│ └── windows/ Per-tab render functions (console, settings, etc.) +├── util/ +│ ├── i18n.h/.cpp Internationalization (TR() macro, I18n singleton) +│ ├── platform.h/.cpp Cross-platform utilities (paths, URLs) +│ └── logger.h DEBUG_LOGF / VERBOSE_LOGF macros +├── platform/ Windows-specific (DX11 context, backdrop effects) +├── embedded/ Build-generated headers (embedded resources) +└── resources/ Runtime resource loading (fonts, images) +``` + +## Threading Model + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Main Thread (UI) │ +│ • ImGui render loop (App::update()) │ +│ • drainResults() each frame → executes completed callbacks │ +│ • NEVER does blocking I/O │ +└────────────┬────────────────────────┬───────────────────────┘ + │ post() │ post() + ▼ ▼ +┌────────────────────┐ ┌────────────────────────────────┐ +│ worker_ (RPCWorker)│ │ fast_worker_ (RPCWorker) │ +│ General RPC polling│ │ Console commands, hashrate │ +│ refreshData() 5s │ │ Avoids head-of-line blocking │ +└────────────────────┘ └────────────────────────────────┘ + +┌────────────────────────────────────────────┐ +│ Monitor Threads (per child process) │ +│ • EmbeddedDaemon: reads dragonxd stdout │ +│ • XmrigManager: reads xmrig-hac stdout │ +└────────────────────────────────────────────┘ +``` + +### Work Submission Pattern + +```cpp +worker_->post([this]() -> rpc::RPCWorker::MainCb { + json result = rpc_->call("getinfo"); // Runs on worker thread + return [this, result]() { + state_.connected = true; // Runs on main thread + }; +}); +``` + +## RPC Architecture + +**Dual-client design** avoids head-of-line blocking: + +| Client | Worker | Purpose | +|--------|--------|---------| +| `rpc_` | `worker_` | Main polling: balance, transactions, sync status | +| `fast_rpc_` | `fast_worker_` | Console commands, mining stats | + +Both are `RPCClient` instances wrapping libcurl for JSON-RPC over HTTPS to `dragonxd`. + +**Error flow:** `RPCClient::call()` throws `std::runtime_error` → +caught in worker lambda → error string captured → `MainCb` calls +`Notifications::instance().error(msg)` on main thread. + +## Connection Lifecycle + +``` +[Disconnected] → tryConnect() every 5s + → auto-detect DRAGONX.conf (host, port, rpcuser, rpcpassword) + → if no config: start embedded daemon → retry + → post async rpc_->connect() to worker_ + → success: onConnected() → state_.connected = true + → auth 401: try .cookie auth → retry + → failure: onDisconnected(reason) → may restart daemon +``` + +## UI System + +### Page Routing +`NavPage` enum defines all pages. `App::update()` dispatches via switch on +`current_page_` to render functions (e.g., `RenderBalanceTab(this)`). + +### Theme System +- Layout defined in `res/themes/ui.toml`, accessed via `schema::UI()` +- Skins are TOML files in `res/themes/` defining color palettes +- `scripts/expand_themes.py` merges layout into skins at build time +- Colors support CSS-style variables resolved by `ColorVarResolver` +- Hot-reload: `UISchema::pollForChanges()` watches file mtime + +### Internationalization +- All UI strings use `TR("key")` macro → `I18n` singleton lookup +- Language files: `res/lang/{locale}.json` with compiled-in fallbacks +- Supported: en, es, zh, ru, de, fr, pt, ja, ko + +### Notifications +```cpp +Notifications::instance().success("Transaction sent"); +Notifications::instance().error("Insufficient funds"); +``` +Toast-style popups, bottom-right corner, 5-second default duration. + +## Build System + +CMake 3.20+, C++17. FetchContent for SDL3, nlohmann/json, toml++. +Bundled: ImGui, GLAD, miniz, libsodium, qrcode. + +```bash +./build.sh # Dev build +./build.sh --mac-release # macOS universal .app + DMG +./build.sh --linux-release # Linux AppImage + zip +./build.sh --win-release # Windows cross-compile (MinGW) +``` + +### Platform Backends +| Platform | Renderer | Notes | +|----------|----------|-------| +| Windows | DirectX 11 | `imgui_impl_dx11`, backdrop effects | +| macOS | OpenGL 3 | Universal binary (arm64+x86_64), target 11.0+ | +| Linux | OpenGL 3 | GLAD loader | + +## Key Conventions + +1. **Never block the main thread** — all I/O goes through `worker_->post()` +2. **Never hardcode pixel sizes** — use `schema::UI()` lookups from `ui.toml` +3. **Always use `TR()` for strings** — no hardcoded English in UI code +4. **Use `ICON_MD_*` for icons** — Material Design icon font +5. **`std::unique_ptr` for ownership** — no raw `new`/`delete` +6. **Check the right connection state** — `state_.connected` vs `rpc_->isConnected()`