Wallet: no way to remove a private key (add removeprivkey, and signmessagewithprivkey) #46

Closed
opened 2026-09-30 00:16:48 +00:00 by btcbob · 1 comment
Owner

There is no way to take a private key back out of a wallet once it is in. The only key RPCs are importprivkey and dumpprivkey (src/rpcdump.cpp); there is no removal, and no way to sign with a key without importing it first.

Why it matters

Signing a message with a cold or paper key (for example, to claim an address on a block explorer rich list, or to prove ownership) currently means importprivkey into a running wallet, then signmessage. After that the key is in wallet.dat for good, and in every backup made from then on. The coins are effectively hot for the rest of the wallet's life, which is the opposite of what the owner wanted from a one-off signature.

Proposal

  1. signmessagewithprivkey "privkey" "message": port Bitcoin Core's RPC (added in 0.13, bitcoin/bitcoin#7953). It signs with a key passed in the call and never touches the wallet. That alone covers the signing use case.
  2. removeprivkey "address" ( keepwatchonly ): remove a key from the wallet.
    • Requires an unlocked wallet. Erases the key/ckey and keymeta records and drops the key from mapKeys/mapCryptedKeys.
    • Refuses keys that are still in the keypool, or that the wallet uses for change, unless forced.
    • With keepwatchonly=true, adds the address as watch-only so the balance stays visible.
    • Warns that coins at that address can no longer be spent by this wallet.
    • Documents that Berkeley DB may keep the deleted bytes in free pages until the file is rewritten, so a true purge needs a rewrite (backupwallet to a new file, or a salvage-style compaction). An optional flag to do that rewrite would be better still.
  3. Optional GUI: a "Forget private key" action in the receiving-address book, behind a confirmation.

Acceptance

  • qa/rpc-tests: signmessagewithprivkey output verifies with verifymessage. After removeprivkey, dumpprivkey fails, validateaddress reports ismine: false (or watch-only), and the key survives neither a restart nor a backupwallet.

Related: #41 (wallet backend). Whatever backend replaces Berkeley DB should support key removal from the start.

There is no way to take a private key back out of a wallet once it is in. The only key RPCs are `importprivkey` and `dumpprivkey` (`src/rpcdump.cpp`); there is no removal, and no way to sign with a key without importing it first. ### Why it matters Signing a message with a cold or paper key (for example, to claim an address on a block explorer rich list, or to prove ownership) currently means `importprivkey` into a running wallet, then `signmessage`. After that the key is in `wallet.dat` for good, and in every backup made from then on. The coins are effectively hot for the rest of the wallet's life, which is the opposite of what the owner wanted from a one-off signature. ### Proposal 1. **`signmessagewithprivkey "privkey" "message"`**: port Bitcoin Core's RPC (added in 0.13, bitcoin/bitcoin#7953). It signs with a key passed in the call and never touches the wallet. That alone covers the signing use case. 2. **`removeprivkey "address" ( keepwatchonly )`**: remove a key from the wallet. - Requires an unlocked wallet. Erases the `key`/`ckey` and `keymeta` records and drops the key from `mapKeys`/`mapCryptedKeys`. - Refuses keys that are still in the keypool, or that the wallet uses for change, unless forced. - With `keepwatchonly=true`, adds the address as watch-only so the balance stays visible. - Warns that coins at that address can no longer be spent by this wallet. - Documents that Berkeley DB may keep the deleted bytes in free pages until the file is rewritten, so a true purge needs a rewrite (backupwallet to a new file, or a salvage-style compaction). An optional flag to do that rewrite would be better still. 3. Optional GUI: a "Forget private key" action in the receiving-address book, behind a confirmation. ### Acceptance - `qa/rpc-tests`: `signmessagewithprivkey` output verifies with `verifymessage`. After `removeprivkey`, `dumpprivkey` fails, `validateaddress` reports `ismine: false` (or watch-only), and the key survives neither a restart nor a `backupwallet`. Related: #41 (wallet backend). Whatever backend replaces Berkeley DB should support key removal from the start.
Author
Owner

Implemented on branch feat/46-removeprivkey (62b6577a).

  • signmessagewithprivkey "privkey" "message" signs without touching the wallet. Its output is identical to signmessage (RFC 6979) and checks with verifymessage.
  • removeprivkey "address" ( keepwatchonly ) removes the key from memory (mapKeys / mapCryptedKeys, mapKeyMetadata) and from disk (key/wkey/ckey/keymeta records).
    • Needs an unlocked wallet.
    • Refuses the default key and any key still in the keypool.
    • keepwatchonly=true leaves the address as watch-only.

Correction to the issue text: no file rewrite is needed. The wallet is SQLite since v0.14.0 (#41's "Berkeley DB only" is out of date), and it already runs with PRAGMA secure_delete = ON, so deleted records are overwritten in place. Backups made before a removal still contain the key, which the RPC help says.

Not done yet: the GUI "Forget private key" action, and refusing addresses already used for change (only keys still in the keypool are guarded).

Tests: new qa/rpc-tests/removeprivkey.py. It covers signing on a node without the key; removal on plain and encrypted wallets; persistence across restart; the secret bytes and key records absent from wallet.dat; keepwatchonly; and re-import. qa/run-all.sh: 27 passed, 0 failed.

Implemented on branch [`feat/46-removeprivkey`](https://git.subgenius.finance/SubGeniusFinance/dobbscoin-source/compare/main...feat/46-removeprivkey) (`62b6577a`). - **`signmessagewithprivkey "privkey" "message"`** signs without touching the wallet. Its output is identical to `signmessage` (RFC 6979) and checks with `verifymessage`. - **`removeprivkey "address" ( keepwatchonly )`** removes the key from memory (`mapKeys` / `mapCryptedKeys`, `mapKeyMetadata`) and from disk (`key`/`wkey`/`ckey`/`keymeta` records). - Needs an unlocked wallet. - Refuses the default key and any key still in the keypool. - `keepwatchonly=true` leaves the address as watch-only. **Correction to the issue text:** no file rewrite is needed. The wallet is SQLite since v0.14.0 (#41's "Berkeley DB only" is out of date), and it already runs with `PRAGMA secure_delete = ON`, so deleted records are overwritten in place. Backups made *before* a removal still contain the key, which the RPC help says. **Not done yet:** the GUI "Forget private key" action, and refusing addresses already used for change (only keys still in the keypool are guarded). **Tests:** new `qa/rpc-tests/removeprivkey.py`. It covers signing on a node without the key; removal on plain and encrypted wallets; persistence across restart; the secret bytes and key records absent from `wallet.dat`; `keepwatchonly`; and re-import. `qa/run-all.sh`: 27 passed, 0 failed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
SubGeniusFinance/dobbscoin-source#46
No description provided.