- copilot-instructions.md → ObsidianDragon-agent/copilot-instructions.md - ARCHITECTURE.md → ObsidianDragon-agent/ARCHITECTURE.md - Symlinks at original locations preserve Copilot auto-discovery
7.0 KiB
7.0 KiB
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
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 viaschema::UI() - Skins are TOML files in
res/themes/defining color palettes scripts/expand_themes.pymerges 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 →I18nsingleton lookup - Language files:
res/lang/{locale}.jsonwith compiled-in fallbacks - Supported: en, es, zh, ru, de, fr, pt, ja, ko
Notifications
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.
./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
- Never block the main thread — all I/O goes through
worker_->post() - Never hardcode pixel sizes — use
schema::UI()lookups fromui.toml - Always use
TR()for strings — no hardcoded English in UI code - Use
ICON_MD_*for icons — Material Design icon font std::unique_ptrfor ownership — no rawnew/delete- Check the right connection state —
state_.connectedvsrpc_->isConnected()