mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-04 12:55:02 +02:00
Merge bitcoin/bitcoin#28724: wallet: Cleanup accidental encryption keys in watchonly wallets
69e95c2b4ftests: Test cleanup of mkeys from wallets without privkeys (Andrew Chow)2b9279b50awallet: Remove unused encryption keys from watchonly wallets (Andrew Chow)813a16a463wallet: Add HasCryptedKeys (Andrew Chow) Pull request description: An earlier version allowed users to create watchonly wallets (wallets without private keys) that were "encrypted". Such wallets would have a stored encryption keys, but nothing would actually be encrypted with them. This can cause unexpected behavior such as https://github.com/bitcoin-core/gui/issues/772. We can detect such wallets as they will have the disable private keys flag set, no encrypted keys, and encryption keys. For such wallets, we can remove those encryption keys thereby avoiding any issues that may result from this unexpected situation. ACKs for top commit: sipa: utACK69e95c2b4f. laanwj: Code review re-ACK69e95c2b4ffurszy: Code review ACK69e95c2b4fTree-SHA512: 901932cd709c57e66c598f011f0105a243b5a8b539db2ef3fcf370dca4cf35ae09bc1110e8fca8353be470f159468855a4dd96b99bc9c1112adc86ccc50e1b9d
This commit is contained in:
@@ -5,7 +5,9 @@
|
||||
"""Test Wallet encryption"""
|
||||
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
from test_framework.messages import hash256
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_raises_rpc_error,
|
||||
@@ -100,6 +102,65 @@ class WalletEncryptionTest(BitcoinTestFramework):
|
||||
sig = self.nodes[0].signmessage(address, msg)
|
||||
assert self.nodes[0].verifymessage(address, sig, msg)
|
||||
|
||||
self.log.info("Test that wallets without private keys cannot be encrypted")
|
||||
self.nodes[0].createwallet(wallet_name="noprivs", disable_private_keys=True)
|
||||
noprivs_wallet = self.nodes[0].get_wallet_rpc("noprivs")
|
||||
assert_raises_rpc_error(-16, "Error: wallet does not contain private keys, nothing to encrypt.", noprivs_wallet.encryptwallet, "pass")
|
||||
|
||||
if self.is_wallet_tool_compiled():
|
||||
self.log.info("Test that encryption keys in wallets without privkeys are removed")
|
||||
|
||||
def do_wallet_tool(*args):
|
||||
proc = subprocess.Popen(
|
||||
[self.options.bitcoinwallet, f"-datadir={self.nodes[0].datadir_path}", f"-chain={self.chain}"] + list(args),
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
stdout, stderr = proc.communicate()
|
||||
assert_equal(proc.poll(), 0)
|
||||
assert_equal(stderr, "")
|
||||
|
||||
# Since it is no longer possible to encrypt a wallet without privkeys, we need to force one into the wallet
|
||||
# 1. Make a dump of the wallet
|
||||
# 2. Add mkey record to the dump
|
||||
# 3. Create a new wallet from the dump
|
||||
|
||||
# Make the dump
|
||||
noprivs_wallet.unloadwallet()
|
||||
dumpfile_path = self.nodes[0].datadir_path / "noprivs.dump"
|
||||
do_wallet_tool("-wallet=noprivs", f"-dumpfile={dumpfile_path}", "dump")
|
||||
|
||||
# Modify the dump
|
||||
with open(dumpfile_path, "r", encoding="utf-8") as f:
|
||||
dump_content = f.readlines()
|
||||
# Drop the checksum line
|
||||
dump_content = dump_content[:-1]
|
||||
# Insert a valid mkey line. This corresponds to a passphrase of "pass".
|
||||
dump_content.append("046d6b657901000000,300dc926f3b3887aad3d5d5f5a0fc1b1a4a1722f9284bd5c6ff93b64a83902765953939c58fe144013c8b819f42cf698b208e9911e5f0c544fa300000000cc52050000\n")
|
||||
with open(dumpfile_path, "w", encoding="utf-8") as f:
|
||||
contents = "".join(dump_content)
|
||||
f.write(contents)
|
||||
checksum = hash256(contents.encode())
|
||||
f.write(f"checksum,{checksum.hex()}\n")
|
||||
|
||||
# Load the dump into a new wallet
|
||||
do_wallet_tool("-wallet=noprivs_enc", f"-dumpfile={dumpfile_path}", "createfromdump")
|
||||
# Load the wallet and make sure it is no longer encrypted
|
||||
with self.nodes[0].assert_debug_log(["Detected extraneous encryption keys in this wallet without private keys. Removing extraneous encryption keys."]):
|
||||
self.nodes[0].loadwallet("noprivs_enc")
|
||||
noprivs_wallet = self.nodes[0].get_wallet_rpc("noprivs_enc")
|
||||
assert_raises_rpc_error(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.", noprivs_wallet.walletpassphrase, "pass", 1)
|
||||
noprivs_wallet.unloadwallet()
|
||||
|
||||
# Make a new dump and check that there are no mkeys
|
||||
dumpfile_path = self.nodes[0].datadir_path / "noprivs_enc.dump"
|
||||
do_wallet_tool("-wallet=noprivs_enc", f"-dumpfile={dumpfile_path}", "dump")
|
||||
with open(dumpfile_path, "r", encoding="utf-8") as f:
|
||||
# Check theres nothing with an 'mkey' prefix
|
||||
assert_equal(all([not line.startswith("046d6b6579") for line in f]), True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletEncryptionTest(__file__).main()
|
||||
|
||||
Reference in New Issue
Block a user