180 lines
7.2 KiB
Markdown
180 lines
7.2 KiB
Markdown
|
|
# Hardware Wallet Integration Architecture
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
Archipelago supports hardware wallets for secure Bitcoin transaction signing via PSBT (Partially Signed Bitcoin Transactions). This document covers integration with ColdCard, Trezor, and Ledger hardware wallets.
|
||
|
|
|
||
|
|
## Supported Devices
|
||
|
|
|
||
|
|
| Device | Connection | PSBT Support | DID Signing | Detection |
|
||
|
|
|--------|-----------|-------------|-------------|-----------|
|
||
|
|
| ColdCard Mk4 | USB / MicroSD / NFC | Native PSBT | No (Bitcoin-only) | USB VID `0xd13e` |
|
||
|
|
| Trezor Model T/Safe 3 | USB / WebUSB | Via trezorctl | No | USB VID `0x534c` (SatoshiLabs) |
|
||
|
|
| Ledger Nano S/X/Plus | USB / Bluetooth | Via HWI | No | USB VID `0x2c97` |
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
||
|
|
│ Archipelago Node │
|
||
|
|
│ │
|
||
|
|
│ ┌──────────┐ ┌────────────┐ ┌──────────────────────────┐ │
|
||
|
|
│ │ Web UI │───▸│ RPC Server │───▸│ LND (gRPC) │ │
|
||
|
|
│ │ │ │ │ │ - FundPsbt │ │
|
||
|
|
│ │ QR code │ │ lnd.create │ │ - SignPsbt (partial) │ │
|
||
|
|
│ │ display │ │ -psbt │ │ - FinalizePsbt │ │
|
||
|
|
│ │ │ │ │ │ - PublishTransaction │ │
|
||
|
|
│ │ File │ │ lnd.final │ └──────────────────────────┘ │
|
||
|
|
│ │ upload │ │ ize-psbt │ │
|
||
|
|
│ └──────────┘ └────────────┘ │
|
||
|
|
│ ▲ │
|
||
|
|
│ │ PSBT (base64) │
|
||
|
|
│ ▼ │
|
||
|
|
│ ┌──────────────────────┐ │
|
||
|
|
│ │ Hardware Wallet │ │
|
||
|
|
│ │ - USB direct │ │
|
||
|
|
│ │ - QR code scan │ │
|
||
|
|
│ │ - MicroSD (CC) │ │
|
||
|
|
│ └──────────────────────┘ │
|
||
|
|
└─────────────────────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
## PSBT Signing Flow
|
||
|
|
|
||
|
|
### 1. Create Unsigned PSBT
|
||
|
|
|
||
|
|
The user initiates a transaction (send coins, open channel, close channel). Instead of LND signing automatically, we create an unsigned PSBT.
|
||
|
|
|
||
|
|
**RPC endpoint**: `lnd.create-psbt`
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"method": "lnd.create-psbt",
|
||
|
|
"params": {
|
||
|
|
"outputs": [{"address": "bc1q...", "amount_sats": 50000}],
|
||
|
|
"fee_rate_sat_per_vbyte": 10,
|
||
|
|
"change_address": "bc1q..."
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response**:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"psbt_base64": "cHNidP8BAH...",
|
||
|
|
"psbt_hex": "70736274ff...",
|
||
|
|
"estimated_fee_sats": 1420,
|
||
|
|
"inputs": [{"txid": "abc...", "vout": 0, "amount_sats": 100000}],
|
||
|
|
"outputs": [{"address": "bc1q...", "amount_sats": 50000}, {"address": "bc1q...", "amount_sats": 48580}]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**LND gRPC mapping**: Uses `WalletKit.FundPsbt` to select UTXOs and create the PSBT template.
|
||
|
|
|
||
|
|
### 2. Sign with Hardware Wallet
|
||
|
|
|
||
|
|
Three transfer methods supported:
|
||
|
|
|
||
|
|
#### QR Code (ColdCard NFC, Trezor via companion app)
|
||
|
|
- Display PSBT as animated QR code (BBQr format for large PSBTs)
|
||
|
|
- User scans with hardware wallet
|
||
|
|
- Hardware wallet displays transaction details for verification
|
||
|
|
- User confirms on device
|
||
|
|
- Signed PSBT returned as QR code — user scans with camera or uploads screenshot
|
||
|
|
|
||
|
|
#### USB Direct (Trezor, Ledger)
|
||
|
|
- Detect hardware wallet USB device
|
||
|
|
- Pass PSBT via USB HID protocol
|
||
|
|
- User confirms on device
|
||
|
|
- Signed PSBT returned via USB
|
||
|
|
|
||
|
|
#### MicroSD (ColdCard)
|
||
|
|
- Export PSBT file for download
|
||
|
|
- User transfers to ColdCard via MicroSD
|
||
|
|
- ColdCard signs and saves signed PSBT to MicroSD
|
||
|
|
- User uploads signed PSBT file back to Archipelago
|
||
|
|
|
||
|
|
### 3. Finalize and Broadcast
|
||
|
|
|
||
|
|
**RPC endpoint**: `lnd.finalize-psbt`
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"method": "lnd.finalize-psbt",
|
||
|
|
"params": {
|
||
|
|
"signed_psbt_base64": "cHNidP8BAH..."
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response**:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"txid": "abc123...",
|
||
|
|
"raw_tx_hex": "0200000001...",
|
||
|
|
"broadcast": true
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**LND gRPC mapping**: Uses `WalletKit.FinalizePsbt` then `WalletKit.PublishTransaction`.
|
||
|
|
|
||
|
|
## USB Device Detection
|
||
|
|
|
||
|
|
**RPC endpoint**: `system.detect-usb-devices`
|
||
|
|
|
||
|
|
Scans `/sys/bus/usb/devices/` or uses `lsusb` to detect known hardware wallet vendor IDs:
|
||
|
|
|
||
|
|
| Vendor | VID | Product IDs |
|
||
|
|
|--------|-----|-------------|
|
||
|
|
| ColdCard (Coinkite) | `0xd13e` | `0xcc10` (Mk4), `0xcc20` (Q) |
|
||
|
|
| Trezor (SatoshiLabs) | `0x534c` | `0x0001` (One), `0x0002` (T) |
|
||
|
|
| Ledger | `0x2c97` | `0x0001` (Nano S), `0x0004` (Nano X), `0x0005` (Nano S+) |
|
||
|
|
|
||
|
|
Implementation approach:
|
||
|
|
```rust
|
||
|
|
// Read from /sys/bus/usb/devices/*/idVendor and idProduct
|
||
|
|
async fn detect_usb_devices(known_vids: &[(u16, &str)]) -> Vec<DetectedDevice> {
|
||
|
|
// Parse /sys/bus/usb/devices/X-Y/idVendor
|
||
|
|
// Match against known VIDs
|
||
|
|
// Return device list with type identification
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
The detection runs server-side since the hardware wallet is plugged into the Archipelago node (not the browser).
|
||
|
|
|
||
|
|
## UI Integration Points
|
||
|
|
|
||
|
|
### Send Coins View
|
||
|
|
- Add "Sign with Hardware Wallet" toggle/option
|
||
|
|
- When enabled: create unsigned PSBT → show QR/download → accept signed PSBT → finalize
|
||
|
|
|
||
|
|
### Channel Management
|
||
|
|
- Open Channel: PSBT funding option
|
||
|
|
- Close Channel: Cooperative close via PSBT
|
||
|
|
|
||
|
|
### Hardware Wallet Status
|
||
|
|
- Show notification banner when USB device detected
|
||
|
|
- Display device type and connection status
|
||
|
|
- Auto-detect on the Server/Dashboard page
|
||
|
|
|
||
|
|
## Security Considerations
|
||
|
|
|
||
|
|
1. **PSBT verification**: Display transaction details (amounts, addresses, fees) before and after hardware signing — user should verify they match
|
||
|
|
2. **No private keys on node**: When using hardware wallet flow, LND's internal wallet creates watch-only inputs; the hardware wallet holds the actual signing keys
|
||
|
|
3. **PSBT size limits**: QR codes can handle ~2KB; larger PSBTs need animated QR (BBQr) or file transfer
|
||
|
|
4. **USB permissions**: The `archipelago` user needs access to USB HID devices (`udev` rules)
|
||
|
|
|
||
|
|
## Implementation Priority
|
||
|
|
|
||
|
|
1. **Phase 1** (HW-02): PSBT create/finalize RPC endpoints via LND gRPC
|
||
|
|
2. **Phase 2** (HW-03): QR code display + file upload UI
|
||
|
|
3. **Phase 3** (HW-04): USB device detection and notification
|
||
|
|
4. **Future**: Direct USB HID communication (trezorlib, ledger-transport)
|
||
|
|
|
||
|
|
## Dependencies
|
||
|
|
|
||
|
|
- LND v0.18+ (PSBT API via WalletKit)
|
||
|
|
- `qrcode` npm package (QR generation in UI)
|
||
|
|
- `lsusb` or `/sys/bus/usb/` access (device detection)
|
||
|
|
- No external hardware wallet libraries needed for Phase 1-3 (PSBT is a standard format)
|