Skip to content

Commit 47fe744

Browse files
author
MarcoFalke
committed
Merge bitcoin/bitcoin#22364: wallet: Make a tr() descriptor by default
4868c9f Extract Taproot internal keyid with GetKeyFromDestination (Andrew Chow) d8abbe1 Mention bech32m in -addresstype and -changetype help (Andrew Chow) 8fb5784 Create a tr() descriptor bech32m DescriptorScriptPubKeyMan by default (Andrew Chow) 54b3699 Store pubkeys in TRDescriptor::MakeScripts (Andrew Chow) Pull request description: Make a `tr()` descriptor by default in descriptor wallets so that users will be able to make and use segwit v1 bech32m addresses. ACKs for top commit: MarcoFalke: Concept ACK 4868c9f Sjors: re-utACK 4868c9f gruve-p: ACK bitcoin/bitcoin@4868c9f meshcollider: Concept + code review ACK 4868c9f Tree-SHA512: e5896e665b8d559f1d759b6582d1bb24f70d4698a57307684339d9fdcdac28ae9bc17bc946a7efec9cb35c130a95ffc36e3961a335124ec4535d77b8d00e9631
2 parents a2ed33b + 4868c9f commit 47fe744

16 files changed

+95
-58
lines changed

src/script/descriptor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,7 @@ class TRDescriptor final : public DescriptorImpl
851851
builder.Finalize(xpk);
852852
WitnessV1Taproot output = builder.GetOutput();
853853
out.tr_spenddata[output].Merge(builder.GetSpendData());
854+
out.pubkeys.emplace(keys[0].GetID(), keys[0]);
854855
return Vector(GetScriptForDestination(output));
855856
}
856857
bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, const StringType type, const DescriptorCache* cache = nullptr) const override

src/script/signingprovider.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ bool FillableSigningProvider::GetCScript(const CScriptID &hash, CScript& redeemS
190190

191191
CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination& dest)
192192
{
193-
// Only supports destinations which map to single public keys, i.e. P2PKH,
194-
// P2WPKH, and P2SH-P2WPKH.
193+
// Only supports destinations which map to single public keys:
194+
// P2PKH, P2WPKH, P2SH-P2WPKH, P2TR
195195
if (auto id = std::get_if<PKHash>(&dest)) {
196196
return ToKeyID(*id);
197197
}
@@ -208,5 +208,15 @@ CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination&
208208
}
209209
}
210210
}
211+
if (auto output_key = std::get_if<WitnessV1Taproot>(&dest)) {
212+
TaprootSpendData spenddata;
213+
CPubKey pub;
214+
if (store.GetTaprootSpendData(*output_key, spenddata)
215+
&& !spenddata.internal_key.IsNull()
216+
&& spenddata.merkle_root.IsNull()
217+
&& store.GetPubKeyByXOnly(spenddata.internal_key, pub)) {
218+
return pub.GetID();
219+
}
220+
}
211221
return CKeyID();
212222
}

src/wallet/init.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ const WalletInitInterface& g_wallet_init_interface = WalletInit();
4343

4444
void WalletInit::AddWalletOptions(ArgsManager& argsman) const
4545
{
46-
argsman.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
46+
argsman.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", \"bech32\", or \"bech32m\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
4747
argsman.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting many (possibly all) or none, instead of selecting on a per-output basis. Privacy is improved as addresses are mostly swept with fewer transactions and outputs are aggregated in clean change addresses. It may result in higher fees due to less optimal coin selection caused by this added limitation and possibly a larger-than-necessary number of inputs being used. Always enabled for wallets with \"avoid_reuse\" enabled, otherwise default: %u.", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
48-
argsman.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
48+
argsman.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", \"bech32\", or \"bech32m\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
4949
argsman.AddArg("-consolidatefeerate=<amt>", strprintf("The maximum feerate (in %s/kvB) at which transaction building may use more inputs than strictly necessary so that the wallet's UTXO pool can be reduced (default: %s).", CURRENCY_UNIT, FormatMoney(DEFAULT_CONSOLIDATE_FEERATE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
5050
argsman.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
5151
argsman.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kvB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "

src/wallet/scriptpubkeyman.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,12 +1876,6 @@ bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const
18761876

18771877
bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_key, OutputType addr_type, bool internal)
18781878
{
1879-
if (addr_type == OutputType::BECH32M) {
1880-
// Don't allow setting up taproot descriptors yet
1881-
// TODO: Allow setting up taproot descriptors
1882-
return false;
1883-
}
1884-
18851879
LOCK(cs_desc_man);
18861880
assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
18871881

@@ -1911,7 +1905,10 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_
19111905
desc_prefix = "wpkh(" + xpub + "/84'";
19121906
break;
19131907
}
1914-
case OutputType::BECH32M: assert(false); // TODO: Setup taproot descriptor
1908+
case OutputType::BECH32M: {
1909+
desc_prefix = "tr(" + xpub + "/86'";
1910+
break;
1911+
}
19151912
} // no default case, so the compiler can warn about missing cases
19161913
assert(!desc_prefix.empty());
19171914

src/wallet/test/fuzz/notifications.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ struct FuzzedWallet {
6868
CScript GetScriptPubKey(FuzzedDataProvider& fuzzed_data_provider)
6969
{
7070
auto type{fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES)};
71-
if (type == OutputType::BECH32M) {
72-
type = OutputType::BECH32; // TODO: Setup taproot descriptor and remove this line
73-
}
7471
CTxDestination dest;
7572
bilingual_str error;
7673
if (fuzzed_data_provider.ConsumeBool()) {

src/wallet/wallet.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3168,11 +3168,6 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
31683168

31693169
for (bool internal : {false, true}) {
31703170
for (OutputType t : OUTPUT_TYPES) {
3171-
if (t == OutputType::BECH32M) {
3172-
// Skip taproot (bech32m) for now
3173-
// TODO: Setup taproot (bech32m) descriptors by default
3174-
continue;
3175-
}
31763171
auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this));
31773172
if (IsCrypted()) {
31783173
if (IsLocked()) {

test/functional/rpc_fundrawtransaction.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -571,12 +571,12 @@ def test_locked_wallet(self):
571571
if self.options.descriptors:
572572
self.nodes[1].walletpassphrase('test', 10)
573573
self.nodes[1].importdescriptors([{
574-
'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h)'),
574+
'desc': descsum_create('tr(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h)'),
575575
'timestamp': 'now',
576576
'active': True
577577
},
578578
{
579-
'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/1h/*h)'),
579+
'desc': descsum_create('tr(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/1h/*h)'),
580580
'timestamp': 'now',
581581
'active': True,
582582
'internal': True
@@ -778,11 +778,18 @@ def test_option_feerate(self):
778778
for param, zero_value in product(["fee_rate", "feeRate"], [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]):
779779
assert_equal(self.nodes[3].fundrawtransaction(rawtx, {param: zero_value})["fee"], 0)
780780

781-
# With no arguments passed, expect fee of 141 satoshis.
782-
assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00000141, vspan=0.00000001)
783-
# Expect fee to be 10,000x higher when an explicit fee rate 10,000x greater is specified.
784-
result = node.fundrawtransaction(rawtx, {"fee_rate": 10000})
785-
assert_approx(result["fee"], vexp=0.0141, vspan=0.0001)
781+
if self.options.descriptors:
782+
# With no arguments passed, expect fee of 153 satoshis as descriptor wallets now have a taproot output.
783+
assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00000153, vspan=0.00000001)
784+
# Expect fee to be 10,000x higher when an explicit fee rate 10,000x greater is specified.
785+
result = node.fundrawtransaction(rawtx, {"fee_rate": 10000})
786+
assert_approx(result["fee"], vexp=0.0153, vspan=0.0001)
787+
else:
788+
# With no arguments passed, expect fee of 141 satoshis as legacy wallets only support up to segwit v0.
789+
assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00000141, vspan=0.00000001)
790+
# Expect fee to be 10,000x higher when an explicit fee rate 10,000x greater is specified.
791+
result = node.fundrawtransaction(rawtx, {"fee_rate": 10000})
792+
assert_approx(result["fee"], vexp=0.0141, vspan=0.0001)
786793

787794
self.log.info("Test fundrawtxn with invalid estimate_mode settings")
788795
for k, v in {"number": 42, "object": {"foo": "bar"}}.items():
@@ -1073,7 +1080,7 @@ def test_22670(self):
10731080
# Make sure the default wallet will not be loaded when restarted with a high minrelaytxfee
10741081
self.nodes[0].unloadwallet(self.default_wallet_name, False)
10751082
feerate = Decimal("0.1")
1076-
self.restart_node(0, [f"-minrelaytxfee={feerate}", "-discardfee=0"]) # Set high minrelayfee, set discardfee to 0 for easier calculation
1083+
self.restart_node(0, [f"-minrelaytxfee={feerate}", "-discardfee=0", "-changetype=bech32", "-addresstype=bech32"]) # Set high minrelayfee, set discardfee to 0 for easier calculation
10771084

10781085
self.nodes[0].loadwallet(self.default_wallet_name, True)
10791086
funds = self.nodes[0].get_wallet_rpc(self.default_wallet_name)

test/functional/rpc_psbt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class PSBTTest(BitcoinTestFramework):
3131
def set_test_params(self):
3232
self.num_nodes = 3
3333
self.extra_args = [
34-
["-walletrbf=1"],
34+
["-walletrbf=1", "-addresstype=bech32", "-changetype=bech32"], #TODO: Remove address type restrictions once taproot has psbt extensions
3535
["-walletrbf=0", "-changetype=legacy"],
3636
[]
3737
]

test/functional/tool_wallet.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ def log_wallet_timestamp_comparison(self, old, new):
7070

7171
def get_expected_info_output(self, name="", transactions=0, keypool=2, address=0):
7272
wallet_name = self.default_wallet_name if name == "" else name
73-
output_types = 3 # p2pkh, p2sh, segwit
7473
if self.options.descriptors:
74+
output_types = 4 # p2pkh, p2sh, segwit, bech32m
7575
return textwrap.dedent('''\
7676
Wallet info
7777
===========
@@ -85,6 +85,7 @@ def get_expected_info_output(self, name="", transactions=0, keypool=2, address=0
8585
Address Book: %d
8686
''' % (wallet_name, keypool * output_types, transactions, address))
8787
else:
88+
output_types = 3 # p2pkh, p2sh, segwit. Legacy wallets do not support bech32m.
8889
return textwrap.dedent('''\
8990
Wallet info
9091
===========
@@ -298,8 +299,8 @@ def test_getwalletinfo_on_different_wallet(self):
298299
assert_equal(1000, out['keypoolsize_hd_internal'])
299300
assert_equal(True, 'hdseedid' in out)
300301
else:
301-
assert_equal(3000, out['keypoolsize'])
302-
assert_equal(3000, out['keypoolsize_hd_internal'])
302+
assert_equal(4000, out['keypoolsize'])
303+
assert_equal(4000, out['keypoolsize_hd_internal'])
303304

304305
self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
305306
assert_equal(timestamp_before, timestamp_after)

test/functional/wallet_address_types.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ def test_address(self, node, address, multisig, typ):
121121
assert_equal(info['witness_version'], 0)
122122
assert_equal(len(info['witness_program']), 40)
123123
assert 'pubkey' in info
124+
elif not multisig and typ == "bech32m":
125+
# P2TR single sig
126+
assert info["isscript"]
127+
assert info["iswitness"]
128+
assert_equal(info["witness_version"], 1)
129+
assert_equal(len(info["witness_program"]), 64)
124130
elif typ == 'legacy':
125131
# P2SH-multisig
126132
assert info['isscript']
@@ -339,19 +345,31 @@ def run_test(self):
339345
self.log.info("Nodes with addresstype=legacy never use a P2WPKH change output (unless changetype is set otherwise):")
340346
self.test_change_output_type(0, [to_address_bech32_1], 'legacy')
341347

342-
self.log.info("Nodes with addresstype=p2sh-segwit only use a P2WPKH change output if any destination address is bech32:")
343-
self.test_change_output_type(1, [to_address_p2sh], 'p2sh-segwit')
344-
self.test_change_output_type(1, [to_address_bech32_1], 'bech32')
345-
self.test_change_output_type(1, [to_address_p2sh, to_address_bech32_1], 'bech32')
346-
self.test_change_output_type(1, [to_address_bech32_1, to_address_bech32_2], 'bech32')
348+
if self.options.descriptors:
349+
self.log.info("Nodes with addresstype=p2sh-segwit only use a bech32m change output if any destination address is bech32:")
350+
self.test_change_output_type(1, [to_address_p2sh], 'p2sh-segwit')
351+
self.test_change_output_type(1, [to_address_bech32_1], 'bech32m')
352+
self.test_change_output_type(1, [to_address_p2sh, to_address_bech32_1], 'bech32m')
353+
self.test_change_output_type(1, [to_address_bech32_1, to_address_bech32_2], 'bech32m')
354+
else:
355+
self.log.info("Nodes with addresstype=p2sh-segwit only use a P2WPKH change output if any destination address is bech32:")
356+
self.test_change_output_type(1, [to_address_p2sh], 'p2sh-segwit')
357+
self.test_change_output_type(1, [to_address_bech32_1], 'bech32')
358+
self.test_change_output_type(1, [to_address_p2sh, to_address_bech32_1], 'bech32')
359+
self.test_change_output_type(1, [to_address_bech32_1, to_address_bech32_2], 'bech32')
347360

348361
self.log.info("Nodes with change_type=bech32 always use a P2WPKH change output:")
349362
self.test_change_output_type(2, [to_address_bech32_1], 'bech32')
350363
self.test_change_output_type(2, [to_address_p2sh], 'bech32')
351364

352-
self.log.info("Nodes with addresstype=bech32 always use a P2WPKH change output (unless changetype is set otherwise):")
353-
self.test_change_output_type(3, [to_address_bech32_1], 'bech32')
354-
self.test_change_output_type(3, [to_address_p2sh], 'bech32')
365+
if self.options.descriptors:
366+
self.log.info("Nodes with addresstype=bech32 always use either a bech32 or bech32m change output (unless changetype is set otherwise):")
367+
self.test_change_output_type(3, [to_address_bech32_1], 'bech32m')
368+
self.test_change_output_type(3, [to_address_p2sh], 'bech32')
369+
else:
370+
self.log.info("Nodes with addresstype=bech32 always use a P2WPKH change output (unless changetype is set otherwise):")
371+
self.test_change_output_type(3, [to_address_bech32_1], 'bech32')
372+
self.test_change_output_type(3, [to_address_p2sh], 'bech32')
355373

356374
self.log.info('getrawchangeaddress defaults to addresstype if -changetype is not set and argument is absent')
357375
self.test_address(3, self.nodes[3].getrawchangeaddress(), multisig=False, typ='bech32')
@@ -370,10 +388,9 @@ def run_test(self):
370388
self.test_address(4, self.nodes[4].getrawchangeaddress('bech32'), multisig=False, typ='bech32')
371389

372390
if self.options.descriptors:
373-
self.log.info("Descriptor wallets do not have bech32m addresses by default yet")
374-
# TODO: Remove this when they do
375-
assert_raises_rpc_error(-12, "Error: No bech32m addresses available", self.nodes[0].getnewaddress, "", "bech32m")
376-
assert_raises_rpc_error(-12, "Error: No bech32m addresses available", self.nodes[0].getrawchangeaddress, "bech32m")
391+
self.log.info("Descriptor wallets have bech32m addresses")
392+
self.test_address(4, self.nodes[4].getnewaddress("", "bech32m"), multisig=False, typ="bech32m")
393+
self.test_address(4, self.nodes[4].getrawchangeaddress("bech32m"), multisig=False, typ="bech32m")
377394
else:
378395
self.log.info("Legacy wallets cannot make bech32m addresses")
379396
assert_raises_rpc_error(-8, "Legacy wallets cannot provide bech32m addresses", self.nodes[0].getnewaddress, "", "bech32m")

0 commit comments

Comments
 (0)