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
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--174a7506f384e20aa4161008e828411d-->
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:
./wallet/scriptpubkeyman.h:483:41: error: non-virtual member function marked 'override' hides virtual member function
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
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:
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:
// Old wallets store keys as DBKeys::KEY [pubkey] => [privkey]
// ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
// using EC operations as a checksum.
// Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
// 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 12: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 change2003 | + 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
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 12: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,
3868 |
3869 | walletInstance->SetWalletFlags(wallet_creation_flags, false);
3870 |
3871 | - // Always create LegacyScriptPubKeyMan for now3872 | - walletInstance->SetupLegacyScriptPubKeyMan();3873 | + // Only create LegacyScriptPubKeyMan when not descriptor wallet3874 | + 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 12: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 | }
526 |
527 | + // 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 12:21 AM on February 29, 2020:
Done
in
src/wallet/walletdb.cpp:532
in
e6af0ba62boutdated
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.
This tops up the descriptor cache (and `m_map_script_pub_keys`).
The cache is stored in the wallet payload
and used to expand the descriptor upon wallet load. A descriptor
ScriptPubKeyMan may rely more on ephemeral data than its legacy keypool
counterpart. For wallets without private keys and with unhardened derivation, the
keypool is aved as a single xpub, and therefore Topup() does not increase storage
size.
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
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:
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
1220 |
1221 | //! 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:
commit d40617bf4c69d10bf9714ab1f653fc5b740dbf63
Author: Andrew Chow <achow101-github@achow101.com>
Date: Wed Aug 14 14:25:53 2019 -0400
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
1226 |
1227 | //! Create new DescriptorScriptPubKeyMans and add them to the wallet
1228 | void SetupDescriptorScriptPubKeyMans();
1229 | +1230 | + //! Check if the wallet already has a descriptor1231 | + DescriptorScriptPubKeyMan* HasWalletDescriptor(const WalletDescriptor& desc) const;
instagibbs
commented at 1:48 PM on March 27, 2020:
in
src/wallet/rpcdump.cpp:1511
in
7d1c6bbeb0outdated
1506 | + throw JSONRPCError(RPC_INVALID_PARAMETER, "next_index is out of range");1507 | + }1508 | + }1509 | + }1510 | +1511 | + // Active descriptor must be ranged
instagibbs
commented at 2:28 PM on March 27, 2020:
add importdescriptors RPC and tests for native descriptor wallets:
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 label1517 | + 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 either1522 | + 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:1538
in
7d1c6bbeb0outdated
1543 | + }1544 | + }1545 | +1546 | + // If private keys are enabled, abort if private keys are not provided1547 | + 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:
ommit a03a8cbd1f42636834263135e904900cc808d346 (HEAD -> wallet-of-the-glor)
Author: Andrew Chow <achow101-github@achow101.com>
Date: Thu Feb 13 21:06:29 2020 -0500
Return error when no ScriptPubKeyMan is available for specified type
When a CWallet doesn't have a ScriptPubKeyMan for the requested type
in GetNewDestination, give a meaningful error. Also handle this in
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 necessary1569 | + 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"
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
1214 |
1215 | //! Connect the signals from ScriptPubKeyMans to the signals in CWallet
1216 | void ConnectScriptPubKeyManNotifiers();
1217 | +1218 | + //! Instantiate a descriptor ScriptPubKeyMan from the WalletDescriptor and load it1219 | + 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)
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) const1850 | +{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 keys1562 | + bool keyFail = false;1563 | + CryptedKeyMap::const_iterator mi = m_map_crypted_keys.begin();1564 | + for (; mi != m_map_crypted_keys.end(); ++mi)