This PR expands the wallet RPC gethdkey introduced in #26728. It takes a BIP32 path as argument and returns the xpub, along with the master key fingerprint.
To test this PR:
create a regular descriptor wallet
call getxpub m/86h/0h/0h
call listdescriptors, compare the xpub in the tr() descriptor with the previous step
Bigger picture
This paves the way for using Bitcoin Core as one signer in a multisig setup. It simplifies the proposed flow in #22067.
The eventual flow would be:
Create a blank wallet with a seed (either with no descriptors, or where its single key descriptors are not active, so they don’t get used when calling getnewaddress).
(Manually, with Specter or with a simple Python utility - TBD): craft a multisig descriptor containing this xpub
Call importdescriptors which will allow the import if its own fingerprint is recognized (and after checking the xpub)
The usual flow with getnewaddress, send and walletprocesspsbt (and their GUI equivalents)
This PR makes step (2) possible.
Step (1) would require a followup, because blank wallets currently can’t have a seed. Step 4 would also require a followup, so that importdescriptors treats an xpub as if it was an xpriv, after checking that it can derive the xpub from the seed (that matches the fingerprint)
On the GUI side a wizard could perform the above steps and guide the user, where they either:
export their own xpub and then import a descriptor from somewhere else (performing some sanity checks); or
import xpub(s) from other wallets, compose a multisig and get the descriptors for export; or
get xpub(s) from connected hardware devices and configure a multisig (with or without a key on their machine)
in
test/functional/wallet_getxpub.py:61
in
75d09830caoutdated
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.
Conflicts
Reviewers, this pull request conflicts with the following ones:
#29154 (tests: improve wallet multisig descriptor test and docs by mjdietzx)
#29136 (wallet: addhdkey RPC to add just keys to wallets via new void(KEY) descriptor by achow101)
#29130 (wallet: Add createwalletdescriptor and gethdkeys RPCs for adding new automatically generated descriptors by achow101)
#29124 (wallet: Automatically repair corrupted metadata with doubled derivation path by achow101)
#29016 (RPC: add new listmempooltransactions by niftynei)
#28976 (wallet: Fix migration of blank wallets by achow101)
#28868 (wallet: Fix migration of wallets with txs that have both spendable and watchonly outputs by achow101)
#28710 (Remove the legacy wallet and BDB dependency by achow101)
#28610 (wallet: Migrate entire address book entries to watchonly and solvables too by achow101)
#28574 (wallet: optimize migration process, batch db transactions by furszy)
#28333 (wallet: Construct ScriptPubKeyMans with all data rather than loaded progressively by achow101)
#28192 (ParseHDKeypath: support h as hardened marker by Sjors)
#27865 (wallet: Track no-longer-spendable TXOs separately by achow101)
#27286 (wallet: Keep track of the wallet’s own transaction outputs in memory by achow101)
#26008 (wallet: cache IsMine scriptPubKeys to improve performance of descriptor wallets by achow101)
#24748 (test/BIP324: functional tests for v2 P2P encryption by stratospher)
#22838 (descriptors: Be able to specify change and receiving in a single descriptor string by achow101)
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
jonatack
commented at 5:19 pm on June 26, 2021:
contributor
Thanks for working on this!
in
src/wallet/wallet.cpp:3277
in
1ff7a25d5aoutdated
3272+ auto spk_man = GetScriptPubKeyMan(OutputType::BECH32, true);
3273+ if (!spk_man) return false;
3274+ auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
3275+ CKey seed = desc_spk_man->GetKeys().begin()->second;
3276+ CExtKey master_key;
3277+ master_key.SetSeed(seed.begin(), seed.size());
In addition to being an ugly hack, this is also incorrect and does not actually produce the correct master key.
Rspigler
commented at 6:01 am on June 30, 2021:
contributor
Thanks so much for continuing to work on this! Love the use of BIP 87 :)
Call importdescriptors which will allow the import if its own fingerprint is recognized (and after checking the xpub)
Yes, important to check the xpub also, not just the fingerprint, which is easy to spoof
export their own xpub and then import a descriptor…
import xpub(s) from other wallets, compose a multisig…
get xpub(s) from connected hardware devices…
I agree that the above 3 encompasses all use cases/needs for Bitcoin Core being a coordinator and a signer in a multisig setup.
importdescriptors should treat an xpub as if it was an xpriv, after checking that it can derive the xpub from the seed (that matches the fingerprint); it needs to copy the seed from a sibling descriptor??
Can you explain this to me more?
As for the blank wallets, why do we need to be working from blank wallets, and if xprivs are derived from seeds, how can a blank wallet have a seed?
Sjors
commented at 1:39 pm on July 1, 2021:
member
It depends on how we want to define blank. It would be empty except for a seed, so without any descriptors in it.
If a wallet has a seed and you call importdescriptors on it with some multisig descriptor, it should check the fingerprint(s) and path(s) mentioned in that descriptor. It should then derive the xpriv & xpub for that path, from the wallet seed, and check if it matches the descriptor xpub. In that case, the wallet should replace that xpub with the xpriv, as if the user provided it upon import. This avoid the dangerous act of exporting and re-importing an xpriv. That way, if you want to sign a transaction, the wallet knows it can provide one of the signatures.
Rspigler
commented at 6:46 pm on July 6, 2021:
contributor
Thank you, makes a lot of sense!
And what about sibling descriptors?
in
src/wallet/wallet.cpp:3354
in
1ff7a25d5aoutdated
3269+ }
3270+
3271+ // TODO: avoid needing a pre-existing ScriptPubKeyMan for obtaining the HD seed
3272+ auto spk_man = GetScriptPubKeyMan(OutputType::BECH32, true);
3273+ if (!spk_man) return false;
3274+ auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
I received the following error while executing ./src/bitcoin-cli -rpcwallet="descwallet" getxpub "m/87h/0h/0h":
Assertion failed: lock cs_desc_man not held in wallet/scriptpubkeyman.cpp:1747; locks held:
'pwallet->cs_wallet' in wallet/rpcdump.cpp:1868 (in thread 'httpworker.0')
I changed the code below (adding LOCK(desc_spk_man->cs_desc_man);) and then it worked.
With a wallet different from the default one, I receive the error when sending to w2 address.
test_framework.authproxy.JSONRPCException: Insufficient funds (-6)
in
test/functional/wallet_getxpub.py:57
in
1ff7a25d5aoutdated
52+ # TODO: if createwallet supports setting a seed without descriptors,
53+ # use that here instead. This allows replacing deriveaddresses
54+ # with getnewaddress.
55+ w2_address = self.nodes[1].deriveaddresses(descsum_create(desc_receive), 0)[0]
56+ w1.sendtoaddress(w2_address, 1)
57+ self.nodes[0].generate(1)
mjdietzx
commented at 9:30 pm on July 14, 2021:
contributor
Concept ACK
I’ll be sure to update the tests/docs in #22067 to use this when this PR gets merged
fjahr
commented at 7:11 pm on August 1, 2021:
contributor
Concept ACK
Zero-1729
commented at 6:29 am on August 2, 2021:
contributor
Concept ACK
Sjors force-pushed
on Oct 21, 2021
Sjors
commented at 6:24 pm on October 21, 2021:
member
I managed to reconstruct the master extended private key, by combining the key part of the descriptor CExtKey with the chaincode part of the CExtPubKey stored inside that descriptor. This code is absolute trash though. I’ll try to reuse some of @achow101’s work in https://github.com/achow101/bitcoin/commits/upgrade-to-tr-1 which tries to solve a similar problem, for adding a taproot descriptor to an existing descriptor wallet.
Sjors force-pushed
on Nov 5, 2021
Sjors force-pushed
on Nov 14, 2021
Sjors force-pushed
on Nov 17, 2021
Sjors force-pushed
on Nov 17, 2021
Sjors force-pushed
on Nov 17, 2021
Sjors
commented at 6:59 pm on November 17, 2021:
member
Ok, this is now ready for review.
I’ll be sure to update the tests/docs in #22067 to use this when this PR gets merged
I might also take a stab at this, once this PR (and its ancestor) is rebased to include it.
Sjors marked this as ready for review
on Nov 17, 2021
DrahtBot added the label
Needs rebase
on Nov 22, 2021
Sjors force-pushed
on Nov 29, 2021
Sjors
commented at 8:19 am on November 29, 2021:
member
Rebased and updated the multisig descriptor test and example documentation to use getxpub. Unfortunately we still have to reuse existing wallet keys, e.g. m/44h/1h/0h, because importdescriptors can’t derive private keys yet.
DrahtBot removed the label
Needs rebase
on Nov 29, 2021
DrahtBot added the label
Needs rebase
on Dec 7, 2021
Sjors force-pushed
on Dec 10, 2021
DrahtBot removed the label
Needs rebase
on Dec 10, 2021
Sjors force-pushed
on Dec 30, 2021
Sjors force-pushed
on Dec 30, 2021
DrahtBot added the label
Needs rebase
on Jan 11, 2022
Sjors force-pushed
on Jan 12, 2022
DrahtBot removed the label
Needs rebase
on Jan 12, 2022
JeremyRubin
commented at 3:44 pm on March 29, 2022:
contributor
concept ACK this + using core as a signer like this.
One also cool idea would be is if, during PSBT signing code, we temporarily treat all descriptors there as “ours”?
Sjors
commented at 4:00 pm on March 29, 2022:
member
@JeremyRubin afaik descriptors are always treated as “ours”, both in private key wallets and watch-only wallets. The distinction between watch-only and regular addresses was dropped with the introduction descriptor wallets.
mjdietzx
commented at 8:49 pm on April 2, 2022:
contributor
@Sjors in the description of this PR you say “The current draft implementation is not usable.” what specifically are you referring to by that?
Sjors
commented at 8:35 pm on April 5, 2022:
member
@mjdietzx I think that refers to an older version of this PR that used a hideous hack, but the current code should be fine to review. I deleted that remark for the description.
mjdietzx
commented at 9:01 pm on April 6, 2022:
contributor
154@@ -155,8 +155,8 @@ The basic steps are:
155156 1. Every participant generates an xpub. The most straightforward way is to create a new descriptor wallet which we will refer to as
157 the participant's signer wallet. Avoid reusing this wallet for any purpose other than signing transactions from the
158- corresponding multisig we are about to create. Hint: extract the wallet's xpubs using `listdescriptors` and pick the one from the
159- `pkh` descriptor since it's least likely to be accidentally reused (legacy addresses)
160+ corresponding multisig we are about to create. Hint: extract the wallet's xpubs using `getxpub m/44h/0h/0h` (`m/44h/1h/0h` for testnet and signet). This reuses keys from the
nit: ParseHDKeypath won’t parse paths where the hardened derivation at a particular level is indicated by an h. So we probably want to update these docs, ie getxpub m/44'/0'/0'
Is there any reason that ParseHDKeypath shouldn’t allow paths like this though?
CXX wallet/libbitcoin_wallet_a-keyman.o
In file included from ./script/signingprovider.h:11,
from ./wallet/crypter.h:10,
from wallet/keyman.cpp:9:
./script/keyorigin.h: In member function ‘std::optional<std::pair<CExtPubKey, KeyOriginInfo> > wallet::KeyManager::GetExtPubKey(std::vector) const’:
./script/keyorigin.h:11:8: warning: ‘origin’ may be used uninitialized in this function [-Wmaybe-uninitialized]
11 | struct KeyOriginInfo
| ^~~~~~~~~~~~~
wallet/keyman.cpp:90:19: note: ‘origin’ was declared here
90 | KeyOriginInfo origin;
| ^~~~~~
CXX wallet/libbitcoin_wallet_a-load.o
Still was able to compile
Tests all pass
Create a wallet with private keys disabled
Create a blank wallet with a seed (single key descriptor that is not active) as described in OP.
[
{
“success”: false,
“error”: {
“code”: -4,
“message”: “Cannot import descriptor without private keys to a wallet with private keys enabled”
},
“warnings”: [
“Range not given, using default keypool range”
]
}
]
Not sure how to proceed from here
Sjors
commented at 1:28 pm on July 13, 2022:
member
Got this error when running make:
@Rspigler are you sure that warning is not related to #23417?
I clarified the description a bit: the enumerated steps there would require more followups. I added a simpler procedure to manually test this PR.
Rspigler
commented at 4:52 pm on July 13, 2022:
contributor
I am able to compile and run #23417 without issue.
But still get this error when compiling this PR:
CXX wallet/libbitcoin_wallet_a-keyman.o
In file included from ./script/signingprovider.h:11,
from ./wallet/crypter.h:10,
from wallet/keyman.cpp:9:
./script/keyorigin.h: In member function ‘std::optional<std::pair<CExtPubKey, KeyOriginInfo> > wallet::KeyManager::GetExtPubKey(std::vector) const’:
./script/keyorigin.h:11:8: warning: ‘origin’ may be used uninitialized in this function [-Wmaybe-uninitialized]
11 | struct KeyOriginInfo
| ^~~~~~~~~~~~~
wallet/keyman.cpp:90:19: note: ‘origin’ was declared here
90 | KeyOriginInfo origin;
| ^~~~~~
CXX wallet/libbitcoin_wallet_a-load.o
I understand the PR better now, thank you for clarifying the description.
create a regular descriptor wallet
listdescriptors:
…(listing taproot descriptors only):
jonatack
commented at 6:38 pm on January 10, 2023:
contributor
Re-concept ACK (not sure if it’s ready for review).
Sjors
commented at 7:17 pm on January 10, 2023:
member
@jonatack it was a somewhat sloppy rebase. I also need to review the new base PR. Will mark as draft.
Sjors marked this as a draft
on Jan 10, 2023
Sjors force-pushed
on Jan 27, 2023
Sjors force-pushed
on Jan 27, 2023
Sjors marked this as ready for review
on Jan 27, 2023
Sjors
commented at 4:10 pm on January 27, 2023:
member
Alright, cleaned up a few things, this is ready for review again.
Sjors force-pushed
on Jan 27, 2023
DrahtBot added the label
Needs rebase
on Feb 27, 2023
Sjors force-pushed
on Mar 1, 2023
DrahtBot removed the label
Needs rebase
on Mar 1, 2023
DrahtBot added the label
CI failed
on May 14, 2023
maflcko
commented at 9:59 am on June 5, 2023:
member
Maybe mark as draft if this depends on something?
Sjors marked this as a draft
on Jun 5, 2023
Sjors force-pushed
on Jun 5, 2023
Sjors force-pushed
on Jun 22, 2023
DrahtBot added the label
Needs rebase
on Jun 28, 2023
Sjors force-pushed
on Jul 2, 2023
DrahtBot removed the label
Needs rebase
on Jul 2, 2023
Sjors force-pushed
on Jul 31, 2023
DrahtBot removed the label
CI failed
on Jul 31, 2023
DrahtBot added the label
Needs rebase
on Sep 6, 2023
Sjors force-pushed
on Sep 7, 2023
DrahtBot removed the label
Needs rebase
on Sep 7, 2023
S3RK
commented at 7:36 am on September 11, 2023:
contributor
The description now says the PR depends on itself :-)
DrahtBot added the label
Needs rebase
on Oct 3, 2023
Sjors force-pushed
on Oct 6, 2023
DrahtBot added the label
CI failed
on Oct 6, 2023
DrahtBot removed the label
Needs rebase
on Oct 8, 2023
Sjors force-pushed
on Oct 26, 2023
DrahtBot removed the label
CI failed
on Oct 26, 2023
Sjors force-pushed
on Nov 6, 2023
DrahtBot added the label
Needs rebase
on Nov 7, 2023
Sjors force-pushed
on Nov 9, 2023
Sjors renamed this:
rpc: add getxpub
rpc: add path to gethdkey
on Nov 14, 2023
Sjors force-pushed
on Nov 14, 2023
Sjors force-pushed
on Dec 7, 2023
Sjors force-pushed
on Dec 7, 2023
DrahtBot added the label
CI failed
on Dec 7, 2023
DrahtBot removed the label
Needs rebase
on Dec 7, 2023
Sjors force-pushed
on Dec 7, 2023
DrahtBot removed the label
CI failed
on Dec 7, 2023
walletdb: Read and write activehdkey record8bb7a8b06f
wallet: Load activehdkey and its private keys
The activehdkey record will now be loaded into the wallet as its own
member variable. Since the private keys will always be in existing
descriptors in the wallet, the key for the active hd key is also pulled
out of those descriptors on loading.
2e840bcfc0
wallet: Store master key used for automatically generated descriptors2e2545c3a2
wallet: Retrieve active hd pubkey4569cb1666
key: Add constructor for CExtKey that takes CExtPubKey and CKey
We often need to construct a CExtKey given an CExtPubKey and CKey, so
implement a constructor that does that for us.
116a4252d6
wallet: Implement GetActiveHDPrivkey77408c0bdb
wallet, rpc: Add gethdkey RPC9b760c1049
descriptor: Be able to get the pubkeys involved in a descriptor5800b75a54
wallet: Always set WALLET_FLAG_GLOBAL_HD_KEY for new wallets4a82d27872
wallet, rpc: Check WALLET_FLAG_GLOBAL_HD_KEY in gethdkeybb02b9eb94
wallet: Automatically upgrade a wallet to have global hd keyce5495a4b2
tests: Test for gethdkey9ac7fe7da8
test: Test automatic upgrade of descriptor wallets to have hd key5679b4a885
wallet: Set global hd key for migrated wallets18879984d1
test: Also do backwards compat test with encrypted wallets
Best reviewed with `git show -w`
af3667a13c
walletdb: Check that unencrypted and crypted keys are mutually exclusive1bbbb732b5
test: Check that no unencrypted records persist after encrypting291cb32237
test: add coverage for re-opening a downgraded encrypted wallet on master
The test creates a wallet on master, downgrades and encrypts the wallet.
Then, it tries to open it again on master.
059d847810
ParseHDKeypath: support h as hardened markerc9f7127013
wallet: GetExtKey()20a522cf85
rpc: ParsePathBIP32 helper24d2240d92
rpc: add path argument to gethdkey259d52f964
test: use getxpub in wallet_multisig_descriptor_psbt.pya4330bac48
doc: use gethdkey in basic multisig descriptor exampleaebbcd4e77
Sjors force-pushed
on Dec 14, 2023
Sjors
commented at 10:09 am on December 22, 2023:
member
I plan to re-work this on top of #29130 with a similar interface to createwalletdescriptor. That is, if you have a regular wallet with normal descriptors, it will Just Work(tm). Otherwise you need to specify which master key to use.
Initially I’ll make a separate PR. If #26728 is closed in favor of the new approach in #29130 then this PR can be closed too.
ryanofsky
commented at 3:01 pm on December 23, 2023:
contributor
Initially I’ll make a separate PR. If #26728 is closed in favor of the new approach in #29130 then this PR can be closed too.
It seems like you could just rebase this on top of #29130, since the description of the PR and new commits aren’t going to change that much, they will just be based on #29130 instead of #26728. Either way seems fine, though.
Sjors
commented at 8:29 am on December 27, 2023:
member
@ryanofsky building on #29130 introduces a new argument to specify which master key you intend to use.
DrahtBot added the label
Needs rebase
on Jan 8, 2024
DrahtBot
commented at 2:56 pm on January 8, 2024:
contributor
🐙 This pull request conflicts with the target branch and needs rebase.
Sjors
commented at 6:19 pm on January 12, 2024:
member
Given that #26728 was closed, I’ll just modify this PR to build on top of #29130.
(bit busy with stratum v2 stuff, but it will happen)
Sjors
commented at 9:49 am on February 13, 2024:
member
This seems more involved than a simple rebase. #29130 changes gethdkey to gethdkeys making it a less obvious choice to add a path argument to. We probably need a new RPC that’s more similar to createwalletdescriptor in how it’s called.
I probably won’t have time for this in the near future, so I’m closing this as up for grabs.
This is a metadata mirror of the GitHub repository
bitcoin/bitcoin.
This site is not affiliated with GitHub.
Content is generated from a GitHub metadata backup.
generated: 2024-11-17 03:12 UTC
This site is hosted by @0xB10C More mirrored repositories can be found on mirror.b10c.me