Skip to content

Commit 69f1ee1

Browse files
author
MarcoFalke
committed
Merge #20365: wallettool: add parameter to create descriptors wallet
173cc9b test: walettool create descriptors (Ivan Metlushko) 345e88e wallettool: add param to create descriptors wallet (Ivan Metlushko) 6d3af3a wallettool: pass in DatabaseOptions into MakeWallet (Ivan Metlushko) Pull request description: Rationale: expose and promote descriptor wallets in more places; make cli tool more consistent with `createwallet` rpc. Add `-descriptors` parameter which is off by default. When specified it will create a new descriptors wallet with sqlite backend, which is consistent with `createwallet` rpc. This PR is based on a suggestion from **ryanofsky** bitcoin/bitcoin#19137 (comment) Example: ``` $ ./src/bitcoin-wallet -wallet=fewty -descriptors create Topping up keypool... Wallet info =========== Name: fewty Format: sqlite Descriptors: yes Encrypted: no HD (hd seed available): yes Keypool Size: 6000 Transactions: 0 Address Book: 0 ``` ``` $ ./src/bitcoin-wallet -wallet=fewty create Topping up keypool... Wallet info =========== Name: fewty Format: bdb Descriptors: no Encrypted: no HD (hd seed available): yes Keypool Size: 2000 Transactions: 0 Address Book: 0 ``` ACKs for top commit: achow101: ACK 173cc9b ryanofsky: Code review ACK 173cc9b. This seems pretty nicely implemented now, with opportunities to clean up more and dedup later MarcoFalke: Concept ACK 173cc9b 🌠 Tree-SHA512: cc32ba336ff709de2707ee15f495b4617908e8700ede8401a58e894f44cda485c544d644023c9a6604d88a62db9d92152383ee2e8abf691688c25cf6e222c622
2 parents 3f20580 + 173cc9b commit 69f1ee1

File tree

3 files changed

+69
-90
lines changed

3 files changed

+69
-90
lines changed

src/bitcoin-wallet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ static void SetupWalletToolArgs(ArgsManager& argsman)
2828
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
2929
argsman.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
3030
argsman.AddArg("-debug=<category>", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
31+
argsman.AddArg("-descriptors", "Create descriptors wallet. Only for create", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
3132
argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
3233

3334
argsman.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);

src/wallet/wallettool.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,27 @@ static void WalletToolReleaseWallet(CWallet* wallet)
2121
delete wallet;
2222
}
2323

24-
static void WalletCreate(CWallet* wallet_instance)
24+
static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flags)
2525
{
2626
LOCK(wallet_instance->cs_wallet);
2727

2828
wallet_instance->SetMinVersion(FEATURE_HD_SPLIT);
29+
wallet_instance->AddWalletFlags(wallet_creation_flags);
2930

30-
// generate a new HD seed
31-
auto spk_man = wallet_instance->GetOrCreateLegacyScriptPubKeyMan();
32-
CPubKey seed = spk_man->GenerateNewSeed();
33-
spk_man->SetHDSeed(seed);
31+
if (!wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
32+
auto spk_man = wallet_instance->GetOrCreateLegacyScriptPubKeyMan();
33+
spk_man->SetupGeneration(false);
34+
} else {
35+
wallet_instance->SetupDescriptorScriptPubKeyMans();
36+
}
3437

3538
tfm::format(std::cout, "Topping up keypool...\n");
3639
wallet_instance->TopUpKeyPool();
3740
}
3841

39-
static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, bool create)
42+
static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options)
4043
{
41-
DatabaseOptions options;
4244
DatabaseStatus status;
43-
if (create) {
44-
options.require_create = true;
45-
} else {
46-
options.require_existing = true;
47-
}
4845
bilingual_str error;
4946
std::unique_ptr<WalletDatabase> database = MakeDatabase(path, options, status, error);
5047
if (!database) {
@@ -85,7 +82,7 @@ static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::pa
8582
}
8683
}
8784

88-
if (create) WalletCreate(wallet_instance.get());
85+
if (options.require_create) WalletCreate(wallet_instance.get(), options.create_flags);
8986

9087
return wallet_instance;
9188
}
@@ -110,14 +107,23 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
110107
fs::path path = fs::absolute(name, GetWalletDir());
111108

112109
if (command == "create") {
113-
std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ true);
110+
DatabaseOptions options;
111+
options.require_create = true;
112+
if (gArgs.GetBoolArg("-descriptors", false)) {
113+
options.create_flags |= WALLET_FLAG_DESCRIPTORS;
114+
options.require_format = DatabaseFormat::SQLITE;
115+
}
116+
117+
std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
114118
if (wallet_instance) {
115119
WalletShowInfo(wallet_instance.get());
116120
wallet_instance->Close();
117121
}
118122
} else if (command == "info" || command == "salvage") {
119123
if (command == "info") {
120-
std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ false);
124+
DatabaseOptions options;
125+
options.require_existing = true;
126+
std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
121127
if (!wallet_instance) return false;
122128
WalletShowInfo(wallet_instance.get());
123129
wallet_instance->Close();

test/functional/tool_wallet.py

Lines changed: 47 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ def skip_test_if_missing_module(self):
2828

2929
def bitcoin_wallet_process(self, *args):
3030
binary = self.config["environment"]["BUILDDIR"] + '/src/bitcoin-wallet' + self.config["environment"]["EXEEXT"]
31-
args = ['-datadir={}'.format(self.nodes[0].datadir), '-chain=%s' % self.chain] + list(args)
32-
return subprocess.Popen([binary] + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
31+
default_args = ['-datadir={}'.format(self.nodes[0].datadir), '-chain=%s' % self.chain]
32+
if self.options.descriptors:
33+
default_args.append('-descriptors')
34+
35+
return subprocess.Popen([binary] + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
3336

3437
def assert_raises_tool_error(self, error, *args):
3538
p = self.bitcoin_wallet_process(*args)
@@ -63,6 +66,36 @@ def log_wallet_timestamp_comparison(self, old, new):
6366
result = 'unchanged' if new == old else 'increased!'
6467
self.log.debug('Wallet file timestamp {}'.format(result))
6568

69+
def get_expected_info_output(self, name="", transactions=0, keypool=2, address=0):
70+
wallet_name = self.default_wallet_name if name == "" else name
71+
output_types = 3 # p2pkh, p2sh, segwit
72+
if self.options.descriptors:
73+
return textwrap.dedent('''\
74+
Wallet info
75+
===========
76+
Name: %s
77+
Format: sqlite
78+
Descriptors: yes
79+
Encrypted: no
80+
HD (hd seed available): yes
81+
Keypool Size: %d
82+
Transactions: %d
83+
Address Book: %d
84+
''' % (wallet_name, keypool * output_types, transactions, address))
85+
else:
86+
return textwrap.dedent('''\
87+
Wallet info
88+
===========
89+
Name: %s
90+
Format: bdb
91+
Descriptors: no
92+
Encrypted: no
93+
HD (hd seed available): yes
94+
Keypool Size: %d
95+
Transactions: %d
96+
Address Book: %d
97+
''' % (wallet_name, keypool, transactions, address * output_types))
98+
6699
def test_invalid_tool_commands_and_args(self):
67100
self.log.info('Testing that various invalid commands raise with specific error messages')
68101
self.assert_raises_tool_error('Invalid command: foo', 'foo')
@@ -98,33 +131,7 @@ def test_tool_wallet_info(self):
98131
# shasum_before = self.wallet_shasum()
99132
timestamp_before = self.wallet_timestamp()
100133
self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before))
101-
if self.options.descriptors:
102-
out = textwrap.dedent('''\
103-
Wallet info
104-
===========
105-
Name: default_wallet
106-
Format: sqlite
107-
Descriptors: yes
108-
Encrypted: no
109-
HD (hd seed available): yes
110-
Keypool Size: 6
111-
Transactions: 0
112-
Address Book: 1
113-
''')
114-
else:
115-
out = textwrap.dedent('''\
116-
Wallet info
117-
===========
118-
Name: \
119-
120-
Format: bdb
121-
Descriptors: no
122-
Encrypted: no
123-
HD (hd seed available): yes
124-
Keypool Size: 2
125-
Transactions: 0
126-
Address Book: 3
127-
''')
134+
out = self.get_expected_info_output(address=1)
128135
self.assert_tool_output(out, '-wallet=' + self.default_wallet_name, 'info')
129136
timestamp_after = self.wallet_timestamp()
130137
self.log.debug('Wallet file timestamp after calling info: {}'.format(timestamp_after))
@@ -155,33 +162,7 @@ def test_tool_wallet_info_after_transaction(self):
155162
shasum_before = self.wallet_shasum()
156163
timestamp_before = self.wallet_timestamp()
157164
self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before))
158-
if self.options.descriptors:
159-
out = textwrap.dedent('''\
160-
Wallet info
161-
===========
162-
Name: default_wallet
163-
Format: sqlite
164-
Descriptors: yes
165-
Encrypted: no
166-
HD (hd seed available): yes
167-
Keypool Size: 6
168-
Transactions: 1
169-
Address Book: 1
170-
''')
171-
else:
172-
out = textwrap.dedent('''\
173-
Wallet info
174-
===========
175-
Name: \
176-
177-
Format: bdb
178-
Descriptors: no
179-
Encrypted: no
180-
HD (hd seed available): yes
181-
Keypool Size: 2
182-
Transactions: 1
183-
Address Book: 3
184-
''')
165+
out = self.get_expected_info_output(transactions=1, address=1)
185166
self.assert_tool_output(out, '-wallet=' + self.default_wallet_name, 'info')
186167
shasum_after = self.wallet_shasum()
187168
timestamp_after = self.wallet_timestamp()
@@ -199,19 +180,7 @@ def test_tool_wallet_create_on_existing_wallet(self):
199180
shasum_before = self.wallet_shasum()
200181
timestamp_before = self.wallet_timestamp()
201182
self.log.debug('Wallet file timestamp before calling create: {}'.format(timestamp_before))
202-
out = textwrap.dedent('''\
203-
Topping up keypool...
204-
Wallet info
205-
===========
206-
Name: foo
207-
Format: bdb
208-
Descriptors: no
209-
Encrypted: no
210-
HD (hd seed available): yes
211-
Keypool Size: 2000
212-
Transactions: 0
213-
Address Book: 0
214-
''')
183+
out = "Topping up keypool...\n" + self.get_expected_info_output(name="foo", keypool=2000)
215184
self.assert_tool_output(out, '-wallet=foo', 'create')
216185
shasum_after = self.wallet_shasum()
217186
timestamp_after = self.wallet_timestamp()
@@ -237,9 +206,13 @@ def test_getwalletinfo_on_different_wallet(self):
237206
self.log.debug('Wallet file timestamp after calling getwalletinfo: {}'.format(timestamp_after))
238207

239208
assert_equal(0, out['txcount'])
240-
assert_equal(1000, out['keypoolsize'])
241-
assert_equal(1000, out['keypoolsize_hd_internal'])
242-
assert_equal(True, 'hdseedid' in out)
209+
if not self.options.descriptors:
210+
assert_equal(1000, out['keypoolsize'])
211+
assert_equal(1000, out['keypoolsize_hd_internal'])
212+
assert_equal(True, 'hdseedid' in out)
213+
else:
214+
assert_equal(3000, out['keypoolsize'])
215+
assert_equal(3000, out['keypoolsize_hd_internal'])
243216

244217
self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
245218
assert_equal(timestamp_before, timestamp_after)
@@ -261,10 +234,9 @@ def run_test(self):
261234
# Warning: The following tests are order-dependent.
262235
self.test_tool_wallet_info()
263236
self.test_tool_wallet_info_after_transaction()
237+
self.test_tool_wallet_create_on_existing_wallet()
238+
self.test_getwalletinfo_on_different_wallet()
264239
if not self.options.descriptors:
265-
# TODO: Wallet tool needs more create options at which point these can be enabled.
266-
self.test_tool_wallet_create_on_existing_wallet()
267-
self.test_getwalletinfo_on_different_wallet()
268240
# Salvage is a legacy wallet only thing
269241
self.test_salvage()
270242

0 commit comments

Comments
 (0)