|
5 | 5 | """Test Wallet encryption"""
|
6 | 6 |
|
7 | 7 | import time
|
| 8 | +import subprocess |
8 | 9 |
|
| 10 | +from test_framework.messages import hash256 |
9 | 11 | from test_framework.test_framework import BitcoinTestFramework
|
10 | 12 | from test_framework.util import (
|
11 | 13 | assert_raises_rpc_error,
|
@@ -100,6 +102,65 @@ def run_test(self):
|
100 | 102 | sig = self.nodes[0].signmessage(address, msg)
|
101 | 103 | assert self.nodes[0].verifymessage(address, sig, msg)
|
102 | 104 |
|
| 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 | + |
103 | 164 |
|
104 | 165 | if __name__ == '__main__':
|
105 | 166 | WalletEncryptionTest(__file__).main()
|
0 commit comments