# Palladium Wallet **An SPV wallet built specifically for the Palladium (PLM) cryptocurrency** and optimized for its chain. Runs on desktop (Windows/Linux) and Android from a single shared codebase. Unlike generic wallets adapted to many coins, Palladium Wallet is designed around Palladium's consensus parameters — a Bitcoin-derived UTXO chain with 2-minute blocks and LWMA difficulty — and centralizes them in a single network profile. This keeps it lightweight, predictable and faithful to the chain: no client-side difficulty recalculation (trust is anchored to hardcoded checkpoints), mandatory Merkle verification on every confirmed transaction, and a network client written specifically for Palladium's indexing server. ## Features - **Lightweight SPV**: syncs against an indexing server (ElectrumX-like protocol) without downloading the full chain. - **Security**: seed and private keys encrypted on disk (AES-GCM, PBKDF2-SHA512), never in plaintext in logs or on the wire; every server response is validated with Merkle proofs + checkpoints. - **HD wallet** (BIP39/BIP32), SegWit/wrapped/legacy addresses, watch-only from xpub. - **PSBT-centric**: signing flows go through PSBT (offline / air-gapped / multisig). - **Multi-network**: mainnet, testnet, regtest. - **Cross-platform**: desktop (Windows/Linux) and Android share one Avalonia UI; a **CLI** runs on the same core. - **Multilingual**: Italian, English, Spanish, French, Portuguese, German. ## Architecture ``` PalladiumWallet.sln ├─ src/Core/ Chain/ Crypto/ Wallet/ Spv/ Net/ Storage/ (no UI dependency) ├─ src/App/ shared Avalonia UI library (Views, ViewModels, Loc, Assets) ├─ src/App.Desktop/ desktop head (Windows/Linux) → runnable ├─ src/App.Android/ Android head → apk ├─ src/Cli/ CLI on the same Core └─ tests/ xUnit ``` The UI is written **once** in `src/App`; the desktop and Android heads only add the per-platform entry point and packages. Stack: **.NET 10 + Avalonia UI 12 + NBitcoin**. --- ## Development environment For desktop and the CLI you only need the **.NET 10 SDK**. The core and crypto are fully testable without the GUI or a real network. ### Windows 1. Install the .NET 10 SDK: ```powershell winget install Microsoft.DotNet.SDK.10 ``` (alternatively, the installer from ) 2. Clone the repository and restore dependencies: ```powershell git clone cd PalladiumWallet dotnet restore ``` ### Linux 1. Install the .NET 10 SDK through your distro's package manager, or without root via the official script: ```bash curl -sSL https://dot.net/v1/dotnet-install.sh | bash -s -- --channel 10.0 --install-dir "$HOME/.dotnet10" export PATH="$HOME/.dotnet10:$PATH" DOTNET_ROOT="$HOME/.dotnet10" ``` (add the two `export` lines to your `~/.bashrc` to make them permanent) 2. Clone and restore: ```bash git clone cd PalladiumWallet dotnet restore ``` > The GUI uses Avalonia, which runs natively on Windows and Linux with no extra graphics dependencies. ### Android (additional setup) Building the apk also requires the Android workload, a JDK, and the Android SDK: ```bash dotnet workload install android # .NET Android build packs # JDK 17+ must be available (set JAVA_HOME) # Provision the Android SDK once into ~/android-sdk: dotnet build src/App.Android -t:InstallAndroidDependencies \ -p:AndroidSdkDirectory=$HOME/android-sdk -p:AcceptAndroidSDKLicenses=true ``` To run the apk on an emulator (instead of a physical device), see [*Android emulator (developer setup)*](#android-emulator-developer-setup) below. --- ## Running it ### Desktop GUI in debug (Linux & Windows) The desktop head runs the same way on both OSes (`Debug` is the default configuration). Run it from the repo root: ```bash dotnet run --project src/App.Desktop # single run (Debug) dotnet watch --project src/App.Desktop # with hot reload (edit XAML/C# and see changes live) dotnet run --project src/App.Desktop -c Release # to try the Release config ``` - **Linux** — runs natively on X11/Wayland, no extra graphics packages. On **WSL2** the window appears on the Windows desktop through WSLg (already working here, nothing to install). - **Windows** — runs natively; use the same commands from PowerShell or a terminal. The app writes its data under the per-user data folder (see *User guide → First launch*); delete it to start from a clean first-run wizard. ### CLI Same core, useful for scripts and headless environments: ```bash dotnet run --project src/Cli -- ``` Run without arguments for the full list of commands. ### Android There is no `dotnet run` for a phone: build the apk and install it (see *Building → Android apk*), or run it on an emulator (see *Android emulator (developer setup)*). --- ## Running tests Tests are the **primary verification layer** — the core logic and crypto run headless, without the GUI or a real network. Run the whole suite: ```bash dotnet test ``` Run a single test (or a group) by name: ```bash dotnet test --filter "FullyQualifiedName~TestName" ``` Run only the tests in one project: ```bash dotnet test tests/PalladiumWallet.Tests ``` > Cross-implementation tests compare addresses, txids and PSBTs against reference golden vectors: a different address or txid is a blocking bug. The suite includes **property-based tests** ([CsCheck](https://github.com/AnthonyLloyd/CsCheck)) in `tests/PalladiumWallet.Tests/PropertyTests.cs`. These generate hundreds of random inputs per test and verify invariants that must hold universally — no crash on arbitrary strings, encrypt/decrypt roundtrip for any plaintext and password, every leaf in a randomly-built Merkle tree verifies against its root. They run automatically with `dotnet test` and take ~30 s. --- ## Building ### Reproducible builds (Docker) — recommended for release binaries All three distribution targets (Windows exe, Linux binary, Android apk) can be built with one command inside Docker, with **no SDK installed on the host**: ```bash ./docker/build.sh # interactive menu, or: ./docker/build.sh all ``` Why build this way: the whole toolchain is pinned in the Dockerfiles, so every build uses exactly the same SDK versions regardless of the host machine — no toolchain drift between releases — and the build environment itself is reviewable in the repo. For a wallet this is a trust property, not a convenience: anyone can rebuild the published binaries from source and check they were produced by the process the repository declares. See [docker/README.md](docker/README.md) for prerequisites, usage, how to run each produced artifact, and troubleshooting. The sections below cover manual builds with a locally installed SDK (the normal path during development). ### Development build ```bash dotnet build # whole solution (debug) dotnet build src/App.Desktop # desktop head only ``` > A solution-wide `dotnet build` also builds the Android head, which needs the Android SDK > (see *Android emulator (developer setup)* below). If you don't have it, build the specific > non-Android projects (`src/App.Desktop`, `src/Cli`, `tests/...`). ### Desktop release (self-contained) ```bash # Windows — single self-contained .exe (output: src/App.Desktop/bin/Release/net10.0/win-x64/publish/PalladiumWallet.exe) # IncludeNativeLibrariesForSelfExtract embeds Avalonia's native libs (Skia, HarfBuzz, ANGLE): # without it they stay as separate DLLs and the .exe alone silently fails to start. dotnet publish src/App.Desktop -c Release -r win-x64 -p:PublishSingleFile=true \ -p:IncludeNativeLibrariesForSelfExtract=true --self-contained # Linux — self-contained; AppImage then produced with PupNet Deploy # (output: src/App.Desktop/bin/Release/net10.0/linux-x64/publish/PalladiumWallet) dotnet publish src/App.Desktop -c Release -r linux-x64 --self-contained ``` [PupNet Deploy](https://github.com/kuiperzone/PupNet-Deploy) turns the Linux publish into an AppImage. The executable is named `PalladiumWallet` (set via `` in the desktop head). ### Android apk Prerequisites: the Android workload + SDK (see *Development environment → Android*). The Android head already sets `true`, so the apk is **self-contained** and installs/runs standalone (a Fast-Deployment debug apk crashes at launch with "No assemblies found" when installed without `adb`). ```bash # Default debug apk — all ABIs (arm64-v8a + x86_64): runs on phones AND the x86_64 emulator. # ~79 MB. Output: src/App.Android/bin/Debug/net10.0-android/*-Signed.apk JAVA_HOME= dotnet build src/App.Android -c Debug -t:SignAndroidPackage \ -p:AndroidSdkDirectory=$HOME/android-sdk # Smaller apk for a real phone — arm64 only (~41 MB): JAVA_HOME= dotnet build src/App.Android -c Debug -t:SignAndroidPackage \ -p:AndroidSdkDirectory=$HOME/android-sdk -p:AbiArm64Only=true ``` The ABI restriction uses the `AbiArm64Only` flag, which is scoped to the Android head's `` in its csproj — do **not** pass `-p:RuntimeIdentifiers=android-arm64` on the command line, it leaks to the `net10.0` projects (`Core`/`App`) and breaks the build. (The legacy `AndroidSupportedAbis` property is deprecated and ignored.) (Set `ANDROID_HOME` to skip the `-p:AndroidSdkDirectory` flag.) Release signing with your own keystore is not set up yet; the debug apk is fine for personal sideloading. > **Verification status.** The default multi-ABI apk is verified running on the x86_64 emulator > (UI renders, connects to a server over TLS). The arm64-only apk builds correctly (41 MB, > `arm64-v8a` only) but is meant for a physical arm64 phone — on the x86_64 emulator it only runs > through slow ARM translation and stalls on the splash, so **verify it on a real device**. ### Version The application **version** is set in a single place: the `` tag in [`src/App/PalladiumWallet.App.csproj`](src/App/PalladiumWallet.App.csproj). It appears in the desktop window title, in the Help dialog, and is stamped into the published binaries (and the apk's versionName). --- ## Android emulator (developer setup) How to run the apk without a physical device. Paths assume the Android SDK in `~/android-sdk` and a JDK at `JAVA_HOME` (JDK 17+). Tested on Linux / WSL2. **1. Install the emulator, a system image and the matching platform** (once): ```bash SDK=$HOME/android-sdk $SDK/cmdline-tools/latest/bin/sdkmanager --sdk_root=$SDK \ "emulator" "system-images;android-34;google_apis;x86_64" "platforms;android-34" ``` Use an `x86_64` image so the emulator runs with hardware acceleration (KVM); the app's min SDK is 23. **2. Hardware acceleration (Linux/WSL2)** — the emulator needs access to `/dev/kvm`. Add yourself to the `kvm` group once, then start a new shell (or prefix the launch with `sg kvm -c '…'`): ```bash sudo usermod -aG kvm $USER ``` On WSL2, KVM must be enabled on the Windows host (nested virtualization); the emulator window is shown on the Windows desktop through WSLg. **3. Create an AVD** (virtual device): ```bash echo no | $SDK/cmdline-tools/latest/bin/avdmanager create avd \ -n plm -k "system-images;android-34;google_apis;x86_64" -d pixel ``` **4. Launch the emulator** (software GL is the most robust under WSLg): ```bash $SDK/emulator/emulator -avd plm -gpu swiftshader_indirect -no-snapshot -no-audio & $SDK/platform-tools/adb wait-for-device # wait for full boot: until [ "$($SDK/platform-tools/adb shell getprop sys.boot_completed | tr -d '\r')" = 1 ]; do sleep 2; done ``` If the window won't render, add `-no-window` and rely on `adb` + screenshots (`adb exec-out screencap -p > shot.png`). **5. Install and run the apk; capture logs to debug crashes:** ```bash ADB=$SDK/platform-tools/adb $ADB install -r src/App.Android/bin/Debug/net10.0-android/*-Signed.apk $ADB shell monkey -p io.github.davide3011.palladiumwallet -c android.intent.category.LAUNCHER 1 $ADB logcat -d | grep -iE "monodroid|exception|fatal|avalonia" ``` **VS Code "Android iOS Emulator" extension** (optional, click-to-launch): it only starts an existing AVD, so create one first (step 3). Point it at the emulator binary: ```jsonc // VS Code settings.json "emulator.emulatorPathLinux": "/home//android-sdk/emulator" // on WSL, use: "emulator.emulatorPathWSL": "/home//android-sdk/emulator" ``` --- ## User guide (quick) ### First launch 1. On first launch (desktop), choose **where to store data** (wallet, configuration, certificates) — the default path or a folder of your choice. On Android this step is skipped: data lives in the app's private sandbox. 2. Create a new wallet, restore from seed, or open one of the wallets already in your data folder. 3. If you create a wallet, **write the seed phrase down on paper**: it will not be shown again. You can protect the file with a password. > **Desktop vs Android.** The UI and features are the same on both. Differences: on Android the > data-location step is skipped (fixed app sandbox) and *File → Open wallet from file* (importing a > wallet from an arbitrary file) is hidden — open wallets from the in-app chooser instead. The > version is shown in the desktop window title and, on every platform, in the Help dialog. The CLI > is desktop/headless only. ### Main tabs - **History** — list of transactions. *Double-click* (double-tap on touch) a row to open the full detail (amount, fee, addresses, sizes, confirmations). - **Send** — recipient + amount (or "send all"), adjustable fee; for watch-only wallets a PSBT is produced to be signed offline. - **Receive** — next unused address, with a **QR code** and a **Copy** button. - **Addresses** — all derived addresses with balances; click for details (keys, derivation path). - **Contacts** — address book with labels. ### Connection - The status indicator at the bottom shows the connection to the **indexing server**; tapping it opens the server settings. - Sync is SPV: it downloads only what concerns your wallet and verifies every confirmed transaction with a Merkle proof. ### Settings and Help - **Settings**: language, display unit (PLM / mPLM / µPLM / sat), server. - **Help**: software information and version. ### CLI in brief ```bash # Wallet dotnet run --project src/Cli -- create [--words 12|24] [--kind segwit|wrapped|legacy] [--net mainnet|testnet|regtest] [--password P] dotnet run --project src/Cli -- restore "" [...] dotnet run --project src/Cli -- info [--net ...] [--password P] # Network dotnet run --project src/Cli -- sync [--server host[:port]] [--ssl] dotnet run --project src/Cli -- send --to ADDRESS (--amount X | --all) [--feerate sat/vB] [--broadcast] ``` The default wallet file is `~/.palladium-wallet//wallets/default.wallet.json` (override with `--file`). --- ## License Released under the MIT License. See the [LICENSE](LICENSE) file.