Skip to content

Commit 69e95c2

Browse files
committed
tests: Test cleanup of mkeys from wallets without privkeys
1 parent 2b9279b commit 69e95c2

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

test/functional/wallet_encryption.py

+61
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
"""Test Wallet encryption"""
66

77
import time
8+
import subprocess
89

10+
from test_framework.messages import hash256
911
from test_framework.test_framework import BitcoinTestFramework
1012
from test_framework.util import (
1113
assert_raises_rpc_error,
@@ -100,6 +102,65 @@ def run_test(self):
100102
sig = self.nodes[0].signmessage(address, msg)
101103
assert self.nodes[0].verifymessage(address, sig, msg)
102104

105+
self.log.info("Test that wallets without private keys cannot be encrypted")
106+
self.nodes[0].createwallet(wallet_name="noprivs", disable_private_keys=True)
107+
noprivs_wallet = self.nodes[0].get_wallet_rpc("noprivs")
108+
assert_raises_rpc_error(-16, "Error: wallet does not contain private keys, nothing to encrypt.", noprivs_wallet.encryptwallet, "pass")
109+
110+
if self.is_wallet_tool_compiled():
111+
self.log.info("Test that encryption keys in wallets without privkeys are removed")
112+
113+
def do_wallet_tool(*args):
114+
proc = subprocess.Popen(
115+
[self.options.bitcoinwallet, f"-datadir={self.nodes[0].datadir_path}", f"-chain={self.chain}"] + list(args),
116+
stdin=subprocess.PIPE,
117+
stdout=subprocess.PIPE,
118+
stderr=subprocess.PIPE,
119+
text=True
120+
)
121+
stdout, stderr = proc.communicate()
122+
assert_equal(proc.poll(), 0)
123+
assert_equal(stderr, "")
124+
125+
# Since it is no longer possible to encrypt a wallet without privkeys, we need to force one into the wallet
126+
# 1. Make a dump of the wallet
127+
# 2. Add mkey record to the dump
128+
# 3. Create a new wallet from the dump
129+
130+
# Make the dump
131+
noprivs_wallet.unloadwallet()
132+
dumpfile_path = self.nodes[0].datadir_path / "noprivs.dump"
133+
do_wallet_tool("-wallet=noprivs", f"-dumpfile={dumpfile_path}", "dump")
134+
135+
# Modify the dump
136+
with open(dumpfile_path, "r", encoding="utf-8") as f:
137+
dump_content = f.readlines()
138+
# Drop the checksum line
139+
dump_content = dump_content[:-1]
140+
# Insert a valid mkey line. This corresponds to a passphrase of "pass".
141+
dump_content.append("046d6b657901000000,300dc926f3b3887aad3d5d5f5a0fc1b1a4a1722f9284bd5c6ff93b64a83902765953939c58fe144013c8b819f42cf698b208e9911e5f0c544fa300000000cc52050000\n")
142+
with open(dumpfile_path, "w", encoding="utf-8") as f:
143+
contents = "".join(dump_content)
144+
f.write(contents)
145+
checksum = hash256(contents.encode())
146+
f.write(f"checksum,{checksum.hex()}\n")
147+
148+
# Load the dump into a new wallet
149+
do_wallet_tool("-wallet=noprivs_enc", f"-dumpfile={dumpfile_path}", "createfromdump")
150+
# Load the wallet and make sure it is no longer encrypted
151+
with self.nodes[0].assert_debug_log(["Detected extraneous encryption keys in this wallet without private keys. Removing extraneous encryption keys."]):
152+
self.nodes[0].loadwallet("noprivs_enc")
153+
noprivs_wallet = self.nodes[0].get_wallet_rpc("noprivs_enc")
154+
assert_raises_rpc_error(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.", noprivs_wallet.walletpassphrase, "pass", 1)
155+
noprivs_wallet.unloadwallet()
156+
157+
# Make a new dump and check that there are no mkeys
158+
dumpfile_path = self.nodes[0].datadir_path / "noprivs_enc.dump"
159+
do_wallet_tool("-wallet=noprivs_enc", f"-dumpfile={dumpfile_path}", "dump")
160+
with open(dumpfile_path, "r", encoding="utf-8") as f:
161+
# Check theres nothing with an 'mkey' prefix
162+
assert_equal(all([not line.startswith("046d6b6579") for line in f]), True)
163+
103164

104165
if __name__ == '__main__':
105166
WalletEncryptionTest(__file__).main()

0 commit comments

Comments
 (0)