achow101
commented at 2:01 am on August 2, 2019:
member
Introducing the wallet of the glorious future (again): native descriptor wallets. With native descriptor wallets, addresses are generated from descriptors. Instead of generating keys and deriving addresses from keys, addresses come from the scriptPubKeys produced by a descriptor. Native descriptor wallets will be optional for now and can only be created by using createwallet.
Descriptor wallets will store descriptors, master keys from the descriptor, and descriptor cache entries. Keys are derived from descriptors on the fly. In order to allow choosing different address types, 6 descriptors are needed for normal use. There is a pair of primary and change descriptors for each of the 3 address types. With the default keypool size of 1000, each descriptor has 1000 scriptPubKeys and descriptor cache entries pregenerated. This has a side effect of making wallets large since 6000 pubkeys are written to the wallet by default, instead of the current 2000. scriptPubKeys are kept only in memory and are generated every time a descriptor is loaded. By default, we use the standard BIP 44, 49, 84 derivation paths with an external and internal derivation chain for each.
Descriptors can also be imported with a new importdescriptors RPC.
Native descriptor wallets use the ScriptPubKeyMan interface introduced in #16341 to add a DescriptorScriptPubKeyMan. This defines a different IsMine which uses the simpler model of “does this scriptPubKey exist in this wallet”. Furthermore, DescriptorScriptPubKeyMan does not have watchonly, so with native descriptor wallets, it is not possible to have a wallet with both watchonly and non-watchonly things. Rather a wallet with disable_private_keys needs to be used for watchonly things.
A --descriptor option was added to some tests (wallet_basic.py, wallet_encryption.py, wallet_keypool.py, wallet_keypool_topup.py, and wallet_labels.py) to allow for these tests to use descriptor wallets. Additionally, several RPCs are disabled for descriptor wallets (importprivkey, importpubkey, importaddress, importmulti, addmultisigaddress, dumpprivkey, dumpwallet, importwallet, and sethdseed).
fanquake added the label
Wallet
on Aug 2, 2019
fanquake added this to the "PRs" column in a project
achow101 force-pushed
on Aug 2, 2019
DrahtBot
commented at 3:23 am on August 2, 2019:
member
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Conflicts
Reviewers, this pull request conflicts with the following ones:
#18774 (test: added test for upgradewallet RPC by brakmic)
#18727 (test: Add CreateWalletFromFile test by ryanofsky)
#18699 (wallet: Avoid translating RPC errors by MarcoFalke)
#18654 (rpc: separate bumpfee’s psbt creation function into psbtbumpfee by achow101)
#18618 (gui: Drop RecentRequestsTableModel dependency to WalletModel by promag)
#18617 (test: add factor option to adjust test timeouts by brakmic)
#18608 (refactor: Remove CAddressBookData::destdata by ryanofsky)
#18570 (rpc: return block hash & height in getbalances, gettransaction & getwalletinfo JSONs by brakmic)
#18479 (RPC: Show fee in results for signrawtransaction* for segwit inputs by luke-jr)
#18244 (rpc: fundrawtransaction and walletcreatefundedpsbt respect locks even with manual coin selection by Sjors)
#17977 ([WIP] Implement BIP 340-342 validation (Schnorr/taproot/tapscript) by sipa)
#17877 (qt, refactor: Make enums in BitcoinUnits class scoped by hebasto)
#16463 ([BIP 174] Implement serialization support for GLOBAL_XPUB field. by achow101)
#16432 (qt: Add privacy to the Overview page by hebasto)
#16224 (gui: Bilingual GUI error messages by hebasto)
#11413 ([wallet] [rpc] sendtoaddress/sendmany: Add explicit feerate option by kallewoof)
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.
meshcollider
commented at 3:25 am on August 2, 2019:
contributor
Strong Concept ACK, this should be much nicer now it is preceded by the rework.
achow101 force-pushed
on Aug 2, 2019
achow101 force-pushed
on Aug 2, 2019
achow101 force-pushed
on Aug 2, 2019
achow101 force-pushed
on Aug 2, 2019
Sjors
commented at 8:23 am on August 3, 2019:
member
Approach ACK. It looks pretty straight forward and thanks to The Box feels cleaner than the previous attempt.
I suggest that, after a bit more progress on #16341, we review this in parallel. That ensures that we don’t mess up the division of labour between ScriptPubKeyMan and its subclasses.
Also concept ACK on using BIP44/49/84 for new descriptor wallets. That may need some discussion, because it means individual addresses are no longer hardened.
Some issues I found perusing the commits:
when you restart bitcoind and load a (watch-only) wallet the keypool is empty and getnewaddress no longer works
importdescriptors should no longer require a range argument (~also I would prefer a singular RPC~ nvm that makes rescan slow)
when calling getnewaddress with an address type for which the wallet misses a descriptor, it returns a blank error message:
can you give each ScriptPubKeyManager it’s own file?
deleting GetMetadata and Upgrade inside Introduce WalletDescriptor; should have its own commit?
ScriptPubKeyMap could be introduced in Implement IsMine instead of the earlier commit. Would it make sense to move this into the Descriptor class? How are infinite range descriptors handled, expanded and cached keypoolsize items at a time?
Am I reading this wrong or is SetupGeneration creating a fresh seed for each descriptor?
I’m not a fan of determining the output type in an indirect manner by expanding the descriptor Optional<OutputType> out_script_type = DetermineOutputType(scripts_temp[0], out_keys);; that information is already encoded in the descriptor. Shameless plug for #15590.
fanquake added the label
Descriptors
on Aug 4, 2019
hugohn
commented at 2:57 pm on August 12, 2019:
contributor
Concept ACK.
@achow101 I just noticed that both the existing deriveaddressses & importmulti commands treat descriptor [range_start, range_end] as inclusive, which is consistent with the common understanding of the notation []. Should we update the new importdescriptors command to do the same (make range_end inclusive)?
achow101
commented at 4:20 pm on August 12, 2019:
member
Should we update the new importdescriptors command to do the same (make range_end inclusive)?
I guess so. Latest pushed should do that.
achow101 force-pushed
on Aug 14, 2019
achow101 force-pushed
on Sep 4, 2019
achow101 force-pushed
on Sep 4, 2019
achow101 force-pushed
on Sep 4, 2019
achow101 force-pushed
on Sep 4, 2019
achow101 force-pushed
on Sep 17, 2019
achow101 force-pushed
on Sep 18, 2019
achow101 force-pushed
on Oct 10, 2019
Sjors
commented at 4:50 pm on October 10, 2019:
member
Due to your latest change upstream, this now complains: scriptpubkeyman.h:516:10: warning: 'CheckDecryptionKey' overrides a member function but is not marked 'override'
achow101 force-pushed
on Oct 10, 2019
achow101
commented at 7:13 pm on October 10, 2019:
member
Fixed the warning.
achow101 force-pushed
on Oct 14, 2019
achow101 force-pushed
on Oct 15, 2019
achow101 force-pushed
on Oct 25, 2019
achow101 force-pushed
on Oct 26, 2019
achow101 force-pushed
on Oct 29, 2019
achow101 force-pushed
on Oct 29, 2019
achow101 force-pushed
on Nov 4, 2019
Sjors
commented at 11:18 am on November 5, 2019:
member
Rescanning is broken for (some?) descriptor wallets, both when importing descriptors and when calling rescanblockchain. It does detect new transactions. Example:
Also note the address is incorrectly marked as ischange (but the derivation is correct). Might be unrelated though; the wallet determines IsChange() by checking if it’s in the address book (yuck).
Update: I can no longer reproduce this (d7ca9abe4b817913852abbd48082bde64df8e9d9)
achow101 force-pushed
on Nov 6, 2019
achow101 force-pushed
on Nov 7, 2019
achow101 force-pushed
on Nov 7, 2019
achow101 force-pushed
on Nov 7, 2019
achow101 force-pushed
on Dec 6, 2019
achow101 force-pushed
on Dec 13, 2019
achow101 force-pushed
on Jan 6, 2020
achow101 force-pushed
on Jan 6, 2020
achow101 force-pushed
on Jan 17, 2020
Sjors
commented at 6:53 pm on January 20, 2020:
member
Unfortunately this test passes, so I can’t reproduce the bug I was chasing just yet. I was seeing an origin-pubkey mismatch for sortedmulti() descriptors.
Update: fairly certain I was chasing a ghost. Hopefully the tests are useful.
achow101
commented at 6:29 pm on January 21, 2020:
member
I could not replicate any bugs with sortedmulti descriptors.
achow101 force-pushed
on Jan 30, 2020
achow101 marked this as ready for review
on Jan 30, 2020
achow101 renamed this:
[WIP] Native Descriptor Wallets (take 2)
Native Descriptor Wallets using DescriptorScriptPubKeyMan
on Jan 30, 2020
achow101
commented at 5:00 am on January 30, 2020:
member
Rebased onto master, now ready for review.
achow101 force-pushed
on Jan 30, 2020
Sjors
commented at 12:38 pm on January 30, 2020:
member
Concept and approach re-ACK
I rebased my HWI support PRs on top of this: #16546 (RPC) and #16549 (GUI).
The first commit could go to an independent PR:
Output a descriptor in createmultisig
And a few that could be done before this PR, just to get the number of commits down a bit :-)
~Introduce SetType function to tell ScriptPubKeyMans the type and internal-ness of it~ (empty for legacy)
Change wallet_encryption.py to use signmessage instead of dumpprivkey
d3819b681f766a17deef4650bd6297178924f04b Change GetMetadata to use unique_ptr<CKeyMetadata>
c80e25d8743d7e5bfe866d32bf394cf55166bde5 Add a function to determine the OutputType of a scriptPubKey (DetermineOutputType should be contrasted to getting this information directly from the descriptor like in #15590)
0fc666f32ff8472215e05e85dd609901a02dc3ae Add IsLegacy to CWallet so that the GUI knows whether to show watchonly
I’m ambivalent about using BIP44/49/84 for new descriptor wallets. Downside is that individual addresses are no longer hardened. Upside is that this many other wallets use this standard and we could even consider BIP39 backup at some point (#17748). Perhaps createwallet (and the GUI) could make the use of BIP44/49/84 optional.
importdescriptors should no longer require a range argument
importdescriptor could be a singular RPC (less tedious when used manually). It could drop the timestamp argument in favor of just calling rescanblockchain after import.
I think we should split scriptpubkeyman.{h,cpp} into scriptpubkeyman.{h,cpp} , legacy_scriptpubkeyman.{h,cpp} and descriptor_scriptpubkeyman.{h,cpp} (although meh because it creates large diff, but maybe better to get it over with).
in
src/rpc/misc.cpp:86
in
dd92677895outdated
82@@ -83,6 +83,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
83 "{\n"
84 " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n"
85 " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n"
86+ " \"descriptor\":\"descriptor\" (string) The descriptor for the P2SH address for this multisig\n"
The new dummy class in 3f6cbc5bb16f8a021bbe88d998b12698205aaeac introduces several compiler problems (rebase slippage?), e.g:
0./wallet/scriptpubkeyman.h:483:41: error: non-virtual member function marked 'override' hides virtual member function
1 void KeepDestination(int64_t index) override;
achow101
commented at 10:33 pm on January 30, 2020:
Since this gets dropped later, dropped this at the dummy class definition.
Sjors
commented at 2:42 pm on January 30, 2020:
member
I reviewed the first ~5 commits until e40a833c3bffb0cf723357238eb809398e3e5237Create LegacyScriptPubKeyMan when not a descriptor wallet, but started to run into compiler issues, so will continue later. Found one nit too:
laanwj added this to the "Blockers" column in a project
achow101 force-pushed
on Jan 30, 2020
achow101
commented at 10:34 pm on January 30, 2020:
member
Moved Output a descriptor in createmultisig to it’s own PR and dropped it from here since it isn’t actually used in descriptor wallets.
I will investigate breaking out the other commits.
* I'm ambivalent about using BIP44/49/84 for new descriptor wallets. Downside is that individual addresses are no longer hardened. Upside is that this many other wallets use this standard and we could even consider BIP39 backup at some point (#17748). Perhaps `createwallet` (and the GUI) could make the use of BIP44/49/84 optional.
AFAICT, the reasoning for using exclusively hardened derivation is because we could export private keys with dumpprivkey and that has issues. But since that is disabled for descriptor wallets, I think it’s fine.
* `importdescriptors` should no longer require a range argument
Why?
* `importdescriptor` could be a singular RPC (less tedious when used manually). It could drop the `timestamp` argument in favor of just calling `rescanblockchain` after import.
I suppose, but I don’t really want there to be multiple commands that do fundamentally the same thing.
I think we should split scriptpubkeyman.{h,cpp} into scriptpubkeyman.{h,cpp} , legacy_scriptpubkeyman.{h,cpp} and descriptor_scriptpubkeyman.{h,cpp} (although meh because it creates large diff, but maybe better to get it over with).
Meh.
achow101 closed this
on Jan 30, 2020
achow101 reopened this
on Jan 30, 2020
Sjors
commented at 10:35 am on January 31, 2020:
member
Descriptors can be expanded indefinitely, so why specify a (mandatory) range on import? It might make sense as an optional field to set a boundary, e.g. if you know the external signing device has a range limit.
AFAICT, the reasoning for using exclusively hardened derivation is because we could export private keys with dumpprivkey and that has issues. But since that is disabled for descriptor wallets, I think it’s fine.
I’m not familiar with the history. Another reason could be that if one private key is compromised in a side-channel attack, the other keys are still safe. But that assumes such a side-channel attack, couldn’t also compromise the master key. Or perhaps a (horrible) bug in the signing code causes a private key to leak on chain. Or someone wants to reenable dumpprivkey again to redeem some fork coin. Using m/{0,1,2}'/{0,1}'/k by default and making BIP44/49/84 opt-in (in a followup) seems less of change from status quo.
I don’t really want there to be multiple commands that do fundamentally the same thing.
Do you mean having similar commands (importmulti and importdescriptors) with different options? I like consistency, as do developers who want a simple path to switch their integration over to descriptor wallets. But I also like the rare opportunity to clean up the RPC. And when people have to copy-paste some magic to import keys from a hardware wallet, this new syntax looks less intimidating. Though ideally that shouldn’t be needed at all.
The AppVeyor failure might be legit wallet_importdescriptors.py encounters JSONRPCException: bad-txns-inputs-missingorspent (-25), albeit odd.
In f83fde4e8e13a254083e9433dfa770a8b52b1053Create LegacyScriptPubKeyMan when not a descriptor wallet you’re also introducing class WalletDescriptor; that was supposed to be a separate commit?
You’re serialising the descriptor as a string (in WalletDescriptor). That’s fine with me, I’d rather not delay this work, and we can change the serialisation later. However there’s been some mailinglist discussion about serialising descriptors into something that’s easier to copy/paste and put in a QR code. Maybe that’s worth working out in a separate PR and then to use that for the wallet as well. Added a an issue for it #18043.
in
src/wallet/scriptpubkeyman.h:474
in
ee6980be43outdated
467@@ -467,4 +468,49 @@ class LegacySigningProvider : public SigningProvider
468 bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override { return m_spk_man.GetKeyOrigin(keyid, info); }
469 };
470471+class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
472+{
473+public:
474+ using ScriptPubKeyMan::ScriptPubKeyMan;
In ee6980be437c09df6044a5ced7caa91410453027Introduce DescriptorScriptPubKeyMan as a dummy class: this line (using ScriptPubKeyMan::ScriptPubKeyMan;) is dropped in 7af1a569567a595dc26c33075632f406c4ff0904.
achow101
commented at 6:47 pm on February 11, 2020:
That’s intentional. The using ScriptPubKeyMan::ScriptPubKeyMan; is needed to use the default ScriptPubKeyMan constructor until we add a custom one for DescriptorScriptPubKeyMan.
nit: these are added, but not used, in 7af1a569567a595dc26c33075632f406c4ff0904Store WalletDescriptor in DescriptorScriptPubKeyMan which seems out of the blue. b784a150b23e10ee794d1d2616a0df5c0f317fef seems a better place.
achow101
commented at 7:35 pm on February 11, 2020:
Moved
instagibbs
commented at 8:16 pm on March 20, 2020:
in
src/wallet/scriptpubkeyman.h:476
in
7af1a56956outdated
469@@ -470,8 +470,17 @@ class LegacySigningProvider : public SigningProvider
470471 class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
472 {
473+private:
474+ WalletDescriptor descriptor GUARDED_BY(cs_desc_man);
475+
476+ using ScriptPubKeyMap = std::map<CScript, uint32_t>; // Map of scripts to descriptor range index
ee6980be437c09df6044a5ced7caa91410453027: void RewriteDB() override; is dropped in fd995af379b73b63b78b276b5b8f31d39b2d2b67
achow101
commented at 7:36 pm on February 11, 2020:
Dropped
Sjors
commented at 6:11 pm on January 31, 2020:
member
Code reviewed the first 15 commits up to fd995af379b73b63b78b276b5b8f31d39b2d2b67, looks pretty good.
Regarding private key serialisation:
In d6f0c337f5bbf935cd93ed3884f5c10bcaa5d493Implement loading of keys for DescriptorScriptPubKeyMan the comment // hash pubkey/privkey to accelerate wallet load, is explained elsewhere:
0 // Old wallets store keys as DBKeys::KEY [pubkey] => [privkey]
1 // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
2 // using EC operations as a checksum.
3 // Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
4 // remaining backwards-compatible.
There’s no need to be backwards-compatible. I haven’t thought about this scheme deeply, but can we improve it?
Do we want to use a descriptor cache for all private keys, like we do public keys? And then checksum the entire cache instead of individual keys?
For descriptors without hardened derivation, should we refrain from storing private keys at all? (including for encrypted wallets, since we can derive from seed after decryption)
Update: see IRC wallet meeting log of today, I tripped over the name here; these are apparently master keys.
jonatack
commented at 7:59 pm on January 31, 2020:
member
DrahtBot added the label
Needs rebase
on Feb 4, 2020
MarcoFalke referenced this in commit
75fb37ce68
on Feb 9, 2020
achow101 force-pushed
on Feb 11, 2020
DrahtBot removed the label
Needs rebase
on Feb 11, 2020
achow101 force-pushed
on Feb 11, 2020
achow101 force-pushed
on Feb 11, 2020
achow101 force-pushed
on Feb 12, 2020
achow101
commented at 0:37 am on February 12, 2020:
member
I’ve rebased this on top of #18115 and #18034. I’ll start addressing other issues tomorrow.
I believe I’ve fixed the random failure of wallet_importdescriptors.py as well.
achow101 force-pushed
on Feb 12, 2020
laanwj removed this from the "Blockers" column in a project
achow101 force-pushed
on Feb 14, 2020
achow101 force-pushed
on Feb 14, 2020
achow101 force-pushed
on Feb 14, 2020
achow101
commented at 8:16 pm on February 14, 2020:
member
I’ve removed the requirement for range in importdescriptors. It will give a warning and use the default keypool range.
sidhujag referenced this in commit
c71c448328
on Feb 18, 2020
in
src/wallet/scriptpubkeyman.cpp:2004
in
91d4efc842outdated
1999+ SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
2000+ }
2001+
2002+ // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
2003+ for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
2004+ std::unique_ptr<FlatSigningProvider> keys = GetSigningProvider(psbtx.tx->vout.at(i).scriptPubKey, true);
I figured out what confused me. I was trying to call the Descriptor SKPMan version of FillPSBT() but that doesn’t include the UTXOs, which are normally added in CWallet. I overhauled my design to avoid that problem.
achow101 force-pushed
on Feb 20, 2020
achow101 force-pushed
on Feb 21, 2020
instagibbs
commented at 2:57 pm on February 24, 2020:
member
We should make IsChange check for derivation path for descriptor wallets and not the ad-hoc “is this address in my address book”, which we have to support for legacy wallets forever.
edit: nevermind, not derivation path, just whether or not it’s in m_internal_spk_mans or not
DrahtBot added the label
Needs rebase
on Feb 25, 2020
achow101 force-pushed
on Feb 25, 2020
DrahtBot removed the label
Needs rebase
on Feb 25, 2020
achow101 force-pushed
on Feb 25, 2020
achow101 force-pushed
on Feb 27, 2020
achow101
commented at 1:46 am on February 27, 2020:
member
We still cache individual keys for /*h descriptors, but they’re indexed rather than sequential, so the question remains here.
achow101
commented at 8:35 pm on February 28, 2020:
Yes, it is no longer needed. I’ve removed it.
in
src/wallet/scriptpubkeyman.cpp:1691
in
1b4fd1424foutdated
1685+ // Add all of the scriptPubKeys to the scriptPubKey set
1686+ for (const CScript& script : scripts_temp) {
1687+ m_map_script_pub_keys[script] = i;
1688+ }
1689+ // Write the cache
1690+ std::map<KeyOriginInfo, CExtPubKey> newly_cached = descriptor.cache.GetNotCached(cache.GetCachedExtPubKeys());
Now that you’re no longer caching individual keys, can there still be a situation where you have a non-empty cache that’s incomplete? If not, then you might be able to drop GetNotCached.
instagibbs
commented at 6:20 pm on February 28, 2020:
I don’t think these changes are related to the commit message?
achow101
commented at 0:20 am on February 29, 2020:
Hmm. I think the commit they were part of was accidentally squashed. Moved back into their own commit.
in
src/wallet/wallet.cpp:3872
in
4d7998695foutdated
3867@@ -3868,8 +3868,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
38683869 walletInstance->SetWalletFlags(wallet_creation_flags, false);
38703871- // Always create LegacyScriptPubKeyMan for now
3872- walletInstance->SetupLegacyScriptPubKeyMan();
3873+ // Only create LegacyScriptPubKeyMan when not descriptor wallet
3874+ if (!(wallet_creation_flags & WALLET_FLAG_DESCRIPTORS)) {
instagibbs
commented at 6:23 pm on February 28, 2020:
Just a thought you can nack: You can check for the wallet flags since you just set them above. Easier pattern matching for reviewers who are expecting IsWalletFlagSet.
achow101
commented at 0:20 am on February 29, 2020:
Done
in
src/wallet/walletdb.cpp:529
in
e6af0ba62boutdated
523@@ -495,6 +524,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
524 result = DBErrors::CORRUPT;
525 }
526527+ // Set the active ScriptPubKeyMans
528+ for (auto spk_man_pair : wss.m_external_spk_managers) {
529+ pwallet->SetActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, false, true);
instagibbs
commented at 6:53 pm on February 28, 2020:
please annotate bool args
achow101
commented at 0:21 am on February 29, 2020:
Done
in
src/wallet/walletdb.cpp:532
in
e6af0ba62boutdated
523@@ -495,6 +524,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
524 result = DBErrors::CORRUPT;
525 }
526527+ // Set the active ScriptPubKeyMans
528+ for (auto spk_man_pair : wss.m_external_spk_managers) {
529+ pwallet->SetActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, false, true);
530+ }
531+ for (auto spk_man_pair : wss.m_internal_spk_managers) {
532+ pwallet->SetActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, true, true);
instagibbs
commented at 6:53 pm on February 28, 2020:
please annotate bool args
achow101
commented at 0:21 am on February 29, 2020:
Done
in
src/wallet/scriptpubkeyman.cpp:1614
in
630a5308c8outdated
1609+ break;
1610+ }
1611+ default: assert(false);
1612+ }
1613+
1614+ // Mainnet derives at 0',testnet and regtest derive at 1'
instagibbs
commented at 8:30 pm on February 28, 2020:
0',testnet missing space
achow101
commented at 0:21 am on February 29, 2020:
Done
in
src/wallet/scriptpubkeyman.cpp:1594
in
630a5308c8outdated
1589+
1590+ int64_t creation_time = GetTime();
1591+
1592+ std::string xpub = EncodeExtPubKey(master_key.Neuter());
1593+
1594+ // Add the seed to the wallet
instagibbs
commented at 8:31 pm on February 28, 2020:
Seems like Build descriptor string goes here?
achow101
commented at 0:21 am on February 29, 2020:
Moved
in
src/wallet/scriptpubkeyman.cpp:1632
in
630a5308c8outdated
instagibbs
commented at 8:34 pm on February 28, 2020:
I don’t see intermediate key being written?
achow101
commented at 0:21 am on February 29, 2020:
Removed. The change that did that was reverted given that we now use the descriptor xpub cache.
achow101 force-pushed
on Feb 28, 2020
instagibbs
commented at 8:47 pm on February 28, 2020:
member
Reviewed up to 51012bb297c589bbca59024acb64a59cf9283918 “Add IsSingleType to Descriptors”
will keep going later
achow101 force-pushed
on Feb 29, 2020
in
src/wallet/scriptpubkeyman.cpp:1683
in
8f282ebaf0outdated
1678+ FlatSigningProvider out_keys;
1679+ std::vector<CScript> scripts_temp;
1680+ DescriptorCache cache;
1681+ if (!descriptor.descriptor->Expand(i, provider, scripts_temp, out_keys, &cache)) {
1682+ // Maybe we have a cached xpub and we can expand from cache
1683+ if (!descriptor.descriptor->ExpandFromCache(i, descriptor.cache, scripts_temp, out_keys)) return false;
It may also be worth clarifying that although CanGetAddresses() considers descriptor.next_index < descriptor.range_end, each topup bumps range_end, except for wallets with hardened derivation, with encrypted or without private keys. Those wallets need to call keypoolrefill.
in
src/wallet/scriptpubkeyman.cpp:1520
in
8f282ebaf0outdated
1514+ TopUp();
1515+
1516+ // Get the scriptPubKey from the descriptor
1517+ FlatSigningProvider out_keys;
1518+ std::vector<CScript> scripts_temp;
1519+ if (descriptor.range_end <= m_max_desc_index && !TopUp(1)) {
instagibbs
commented at 8:07 pm on March 20, 2020:
Placeholder for this thought: As I mentioned elsewhere, I’d really like something smarter for detecting IsChange. We could instead store the internall-ness via record with the spkm themselves, then the active record needs one less thing as well, and know what is change even on wallet restore.
It would be nice to avoid this ACTIVE[INTERNAL/EXTERNAL]SPK record altogether and just add two fields to WALLETDESCRIPTOR. I vaguely recall there was a problem with loading order, as the reason we have separate records?
These records are generic spkman records, not descriptor wallet specific records. It does not make sense to make these part of WALLETDESCRIPTOR when not all spkmans are descriptors.
in
src/wallet/walletdb.cpp:247
in
6fa9c482c5outdated
instagibbs
commented at 8:24 pm on March 20, 2020:
Just noting that this is strictly a “softfork” over LegacySPKM: previously any detected pubkey in involved in a script would boot an entry.
The new behavior is much simpler and easier to reason about. Just noting it’s different in case somebody does some sort of idiotic key-sharing wallet between chains.
nit: you do an early return here
@instagibbs IIUC earlier behaviour can still be mimicked with a combo() descriptor.
DrahtBot added the label
Needs rebase
on Mar 23, 2020
achow101 force-pushed
on Mar 24, 2020
DrahtBot removed the label
Needs rebase
on Mar 24, 2020
in
src/wallet/scriptpubkeyman.cpp:2096
in
4a0b2986f4outdated
1639+ descriptor.cache = cache;
1640+ for (int32_t i = descriptor.range_start; i < descriptor.range_end; ++i) {
1641+ FlatSigningProvider out_keys;
1642+ std::vector<CScript> scripts_temp;
1643+ descriptor.descriptor->ExpandFromCache(i, descriptor.cache, scripts_temp, out_keys);
1644+ // Add all of the scriptPubKeys to the scriptPubKey set
instagibbs
commented at 1:11 pm on March 25, 2020:
under what circumstances would multiple scriptpubkeys be generated for a descriptor wallet? Isn’t this just for combo which is inapplicable?
Just combo which can be imported. TopUp is called for all imports to generate the scriptPubKeys, scripts, etc.
instagibbs
commented at 2:44 pm on March 26, 2020:
Ah right I got confused about active arg. In that case the importdescriptors help should be noting that combo cannot be active. The error is helpful enough but I think it warrants note.
in
src/wallet/walletdb.cpp:668
in
4a0b2986f4outdated
559@@ -532,6 +560,12 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
560 pwallet->SetActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ true, /* memonly */ true);
561 }
562563+ // Set the descriptor caches
564+ for (auto desc_cache_pair : wss.m_descriptor_caches) {
565+ auto spk_man = pwallet->GetScriptPubKeyMan(desc_cache_pair.first);
instagibbs
commented at 1:19 pm on March 25, 2020:
should be asserting or aborting the next line when spkm cannot be found(nullptr) for whatever reason
It isn’t. CanGetAddresses should return false for non-ranged descriptors because you cannot get new addresses from such a descriptor. HavePrivateKeys here is for when the cache runs on hardened derivation. If we have private keys, then we can continue to derive those hardened keys.
instagibbs
commented at 2:41 pm on March 26, 2020:
CanGetAddresses should return false for non-ranged descriptors because you cannot get new addresses from such a descriptor
Hm? If you import a non-ranged descriptor that includes a private key, CanGetAddress will return true here.
Whatever the result of this discussion is, I think this code section requires a comment.
instagibbs
commented at 3:52 pm on March 26, 2020:
But we may still be unable to get addresses, these conditions are caught later.
from GetNewDestination. Might want to move that comment to where I’m asking and explain exactly what additional checks are required?
I’ve tightened up the checks that CanGetAddresses does. It will do a IsSingleType and IsRange check now to disallow non-ranged descriptor and combo descriptors.
in
src/wallet/scriptpubkeyman.cpp:1556
in
915da4fabaoutdated
1552 bool DescriptorScriptPubKeyMan::TopUp(unsigned int size)
1553 {
1554- return false;
1555+ LOCK(cs_desc_man);
1556+ unsigned int target_size;
1557+ if (size > 0)
instagibbs
commented at 2:34 pm on March 25, 2020:
in
src/wallet/walletutil.h:97
in
8da5fdc34aoutdated
92+{
93+public:
94+ std::shared_ptr<Descriptor> descriptor;
95+ uint64_t creation_time;
96+ int32_t range_start; // First item in range; start of range, inclusive, i.e. [range_start, range_end)
97+ int32_t range_end; // Item after the last; end of range, exclusive, i.e. [range_start, range_end)
instagibbs
commented at 4:10 pm on March 26, 2020:
Note if and when this value is set and can change e.g., TopUp?
in
src/wallet/scriptpubkeyman.cpp:1522
in
0b4b742dd5outdated
1516+ TopUp();
1517+
1518+ // Get the scriptPubKey from the descriptor
1519+ FlatSigningProvider out_keys;
1520+ std::vector<CScript> scripts_temp;
1521+ if (m_wallet_descriptor.range_end <= m_max_cached_index && !TopUp(1)) {
instagibbs
commented at 4:25 pm on March 26, 2020:
Can you explain why we do a TopUp(1) here then a few lines later do the ExpandFromCache?
TopUp(1) generates the next cache item if we have run out of cached things. ExpandFromCache can then use that cached thing. Otherwise TopUp(1) is a no-op.
in
src/wallet/scriptpubkeyman.cpp:1986
in
40960f9316outdated
instagibbs
commented at 4:59 pm on March 26, 2020:
future work: looks like a lot of duplicated code with a couple differences from legacy.
in
src/wallet/wallet.h:1227
in
339cc0e6b7outdated
1219@@ -1220,6 +1220,9 @@ class CWallet final : public WalletStorage, private interfaces::Chain::Notificat
12201221 //! Sets the active ScriptPubKeyMan for the specified type and internal
1222 void SetActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal, bool memonly = false);
1223+
1224+ //! Create new DescriptoScriptPubKeyMans and add them to the wallet
instagibbs
commented at 5:04 pm on March 26, 2020:
instagibbs
commented at 5:16 pm on March 26, 2020:
member
reviewed through:
0commit d40617bf4c69d10bf9714ab1f653fc5b740dbf631Author: Andrew Chow <achow101-github@achow101.com>
2Date: Wed Aug 14 14:25:53 2019 -0400
34 Add IsLegacy to CWallet so that the GUI knows whether to show watchonly
achow101 force-pushed
on Mar 26, 2020
achow101 force-pushed
on Mar 26, 2020
instagibbs
commented at 7:00 pm on March 26, 2020:
member
1225@@ -1226,6 +1226,12 @@ class CWallet final : public WalletStorage, private interfaces::Chain::Notificat
12261227 //! Create new DescriptorScriptPubKeyMans and add them to the wallet
1228 void SetupDescriptorScriptPubKeyMans();
1229+
1230+ //! Check if the wallet already has a descriptor
1231+ DescriptorScriptPubKeyMan* HasWalletDescriptor(const WalletDescriptor& desc) const;
instagibbs
commented at 1:48 pm on March 27, 2020:
in
src/wallet/rpcdump.cpp:1517
in
7d1c6bbeb0outdated
1513+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Active descriptor must be ranged");
1514+ }
1515+
1516+ // Ranged descriptors should not have a label
1517+ if (data.exists("range") && data.exists("label")) {
1518+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptors should not have a label");
instagibbs
commented at 2:51 pm on March 27, 2020:
add importdescriptors RPC and tests for native descriptor wallets:
in
src/wallet/rpcdump.cpp:1522
in
7d1c6bbeb0outdated
1518+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptors should not have a label");
1519+ }
1520+
1521+ // Internal addresses should not have a label either
1522+ if (internal && data.exists("label")) {
1523+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
instagibbs
commented at 2:52 pm on March 27, 2020:
add importdescriptors RPC and tests for native descriptor wallets
in
src/wallet/rpcdump.cpp:1527
in
7d1c6bbeb0outdated
1523+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
1524+ }
1525+
1526+ // Combo descriptor check
1527+ if (active && !parsed_desc->IsSingleType()) {
1528+ throw JSONRPCError(RPC_WALLET_ERROR, "Combo descriptors cannot be set to active");
instagibbs
commented at 2:52 pm on March 27, 2020:
add importdescriptors RPC and tests for native descriptor wallets
in
src/wallet/rpcdump.cpp:1538
in
7d1c6bbeb0outdated
1543+ }
1544+ }
1545+
1546+ // If private keys are enabled, abort if private keys are not provided
1547+ if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !have_privkeys) {
1548+ throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import descriptor without private keys to a wallet with private keys enabled");
instagibbs
commented at 2:53 pm on March 27, 2020:
add importdescriptors RPC and tests for native descriptor wallets
instagibbs
commented at 5:14 pm on March 28, 2020:
In other words, make sure that importing legacy doesn’t accidentally replace p2sh somehow, and p2sh doesn’t accidentally also replace bech32, and so on. Just thinking about what could have gone wrong under the hood.
instagibbs
commented at 3:16 pm on March 27, 2020:
member
Finished review through:
0ommit a03a8cbd1f42636834263135e904900cc808d346 (HEAD -> wallet-of-the-glor)
1Author: Andrew Chow <achow101-github@achow101.com>
2Date: Thu Feb 13 21:06:29 2020 -0500
34 Return error when no ScriptPubKeyMan is available for specified type
56 When a CWallet doesn't have a ScriptPubKeyMan for the requested type
7 in GetNewDestination, give a meaningful error. Also handle this in
8 Qt which did not do anything with errors.
Looking pretty good so far. My least confident part of review is the encrypted wallet portions, so I don’t vouch for those parts.
I still think we should fix IsChange() detection by recording the internal-ness directly in each spkm wallet record to allow for better detection of change on wallet restoration without the user just having to getnewaddress a bunch before it sees on-chain funds. Seems really confusing for a user to import an existing HWW-based BIP44/49/84 descriptor, sync, then have all of its funds in “change”.
in
src/wallet/rpcdump.cpp:1582
in
a03a8cbd1foutdated
1566+ }
1567+
1568+ // Set descriptor as active if necessary
1569+ if (active) {
1570+ if (!w_desc.descriptor->GetOutputType()) {
1571+ warnings.push_back("Unknown output type, cannot set descriptor to active.");
instagibbs
commented at 3:32 pm on March 27, 2020:
note: not sure how this can be hit. Has to be a ranged descriptor that is “null”
in
src/wallet/rpcdump.cpp:1608
in
a03a8cbd1foutdated
1603+ {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
1604+ {
1605+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
1606+ {
1607+ {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "Descriptor to import."},
1608+ {"active", RPCArg::Type::BOOL, /* default */ "true", "Set this descriptor to be the active descriptor for the corresponding output type/externality"},
instagibbs
commented at 3:47 pm on March 27, 2020:
in
test/functional/wallet_importdescriptors.py:6
in
a03a8cbd1foutdated
0@@ -0,0 +1,293 @@
1+#!/usr/bin/env python3
2+# Copyright (c) 2019 The Bitcoin Core developers
3+# Distributed under the MIT software license, see the accompanying
4+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+"""Test the importdescriptors RPC.
6+
instagibbs
commented at 3:50 pm on March 27, 2020:
this test needs to check default arguments like active
I’m still not very excited about string serialisation, because it sets descriptors in stone. On the other hand, we can also write a straight-forward upgrade script, since descriptors are not encrypted.
It doesn’t have to be that way. We describe “wrapped segwit script” with “sh(wsh(script))”, but it might just as well have been “p2sh[wsh[script]]”. There’s already talk of tweaking descriptors to make them more compatible with miniscript. We use base58 encoded xpubs, but we might as well switch to something bech32 encoded in the future. It makes sense to have a more computer-friendly representation, that’s mapped 1 to 1 with these strings. See also #18043.
It’s just that I’d rather not delay this PR for that.
instagibbs
commented at 5:17 pm on March 28, 2020:
We’re not going to get this right on the first shot likely no matter what we decide. As long as it’s not a supreme burden to upgrade I think current is fine.
in
src/wallet/wallet.h:1244
in
ed75ce9649outdated
1213@@ -1214,6 +1214,12 @@ class CWallet final : public WalletStorage, private interfaces::Chain::Notificat
12141215 //! Connect the signals from ScriptPubKeyMans to the signals in CWallet
1216 void ConnectScriptPubKeyManNotifiers();
1217+
1218+ //! Instantiate a descriptor ScriptPubKeyMan from the WalletDescriptor and load it
1219+ void LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc);
1220+
1221+ //! Sets the active ScriptPubKeyMan for the specified type and internal
memonly could use some documentation (IIUC it depends on if you’re loading an existing wallet, or inserting a new descriptor; it’s not some test suite hack)
in
src/wallet/scriptpubkeyman.cpp:1546
in
68cef400b9outdated
1538@@ -1539,6 +1539,17 @@ bool DescriptorScriptPubKeyMan::TopUp(unsigned int size)
15391540 void DescriptorScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
1541 {
1542+ LOCK(cs_desc_man);
1543+ if (IsMine(script)) {
1544+ int32_t index = m_map_script_pub_keys[script];
1545+ if (index >= m_wallet_descriptor.next_index) {
1546+ WalletLogPrintf("%s: Detected a used keypool item, mark all keypool items up to this item as used\n", __func__);
in
src/wallet/scriptpubkeyman.cpp:2101
in
0abf7b960coutdated
1641+ FlatSigningProvider out_keys;
1642+ std::vector<CScript> scripts_temp;
1643+ m_wallet_descriptor.descriptor->ExpandFromCache(i, m_wallet_descriptor.cache, scripts_temp, out_keys);
1644+ // Add all of the scriptPubKeys to the scriptPubKey set
1645+ for (const CScript& script : scripts_temp) {
1646+ m_map_script_pub_keys[script] = i;
I was thinking the other way around: have different record for different things, so it’s easy to add new things and wipe / ignore stuff we don’t need anymore.
More specifically, I’ve been thinking about a way to unify the descriptor cache structure so that we don’t have to have separate records like that. In particular, I’ve considered setting a bogus derivation index for the parent xpubs so that a single record can be used, as well as a single map within DescriptorCache too.
in
src/wallet/walletdb.cpp:670
in
0abf7b960coutdated
That’s inherently done by not being able to expand from cache during SetCache.
in
src/wallet/scriptpubkeyman.cpp:1858
in
a03a8cbd1foutdated
1847+}
1848+
1849+bool DescriptorScriptPubKeyMan::CanGetAddresses(bool internal) const
1850+{
1851+ // We can only give out addresses from descriptors that are single type (not combo), ranged,
1852+ // and either has cached keys or can generate more keys (ignoring encryption)
No. CanGetAddresses implies that more addresses can be fetched. You can’t do that for an unranged descriptor, we consider their address already fetched and used.
in
src/wallet/scriptpubkeyman.cpp:1558
in
8ae9bf1239outdated
1559+ return false;
1560+
1561+ bool keyPass = m_map_crypted_keys.empty(); // Always pass when there are no encrypted keys
1562+ bool keyFail = false;
1563+ CryptedKeyMap::const_iterator mi = m_map_crypted_keys.begin();
1564+ for (; mi != m_map_crypted_keys.end(); ++mi)