achow101
commented at 3:01 am on July 5, 2019:
member
Introducing the ScriptPubKeyMan (short for ScriptPubKeyManager) for managing scriptPubKeys and their associated scripts and keys. This functionality is moved over from CWallet. Instead, CWallet will have a pointer to a ScriptPubKeyMan for every possible address type, internal and external. It will fetch the correct ScriptPubKeyMan as necessary. When fetching new addresses, it chooses the ScriptPubKeyMan based on address type and whether it is change. For signing, it takes the script and asks each ScriptPubKeyMan for whether that ScriptPubKeyMan considers that script IsMine, whether it has that script, or whether it is able to produce a signature for it. If so, the ScriptPubKeyMan will provide a SigningProvider to the caller which will use that in order to sign.
There is currently one ScriptPubKeyMan - the LegacyScriptPubKeyMan. Each CWallet will have only one LegacyScriptPubKeyMan with the pointers for all of the address types and change pointing to this LegacyScriptPubKeyMan. It is created when the wallet is loaded and all keys and metadata are loaded into it instead of CWallet. The LegacyScriptPubKeyMan is primarily made up of all of the key and script management that used to be in CWallet. For convenience, CWallet has a GetLegacyScriptPubKeyMan which will return the LegacyScriptPubKeyMan or a nullptr if it does not have one (not yet implemented, but callers will check for the nullptr). For purposes of signing, LegacyScriptPubKeyMan’s GetSigningProvider will return itself rather than a separate SigningProvider. This will be different for future ScriptPubKeyMans.
The LegacyScriptPubKeyMan will also handle the importing and exporting of keys and scripts instead of CWallet. As such, a number of RPCs have been limited to work only if a LegacyScriptPubKeyMan can be retrieved from the wallet. These RPCs are sethdseed, addmultisigaddress, importaddress, importprivkey, importpubkey, importmulti, dumpprivkey, and dumpwallet. Other RPCs which relied on the wallet for scripts and keys have been modified in order to take the SigningProvider retrieved from the ScriptPubKeyMan for a given script.
Overall, these changes should not effect how everything actually works and the user should experience no difference between having this change and not having it. As such, no functional tests were changed, and the only unit tests changed were those that were directly accessing CWallet functions that have been removed.
Miscellaneous changes that don’t necessarily make sense outside of this PR
Move wallet enums to walletutil.h
List output types in an array in order to be iterated over
Always try to sign for all pubkeys in multisig
Interface definitions and miscellaneous changes in preparation for ScriptPubKeyMan integration
Introduce both ScriptPubKeyMan as an interface and LegacyScriptPubKeyMan as a dummy class
Add LegacyScriptPubKeyMan to CWallet
Add function callbacks for wallet flags and versions and wallet database
Fetch the SigningProvider for a script from the wallet
Fetch the ScriptPubKeyMan for given output type and internal-ness, or a given script, or ScriptPubKeyMan id
Implementation of LegacyScriptPubKeyMan by copying existing code from CWallet. These will pass all tests and do not affect CWallet
Implement GetSigningProvider in LegacyScriptPubKeyMan
Implement function to connect ScriptPubKeyMan’s NotifyCanGetAddessesChanged and NotifyWatchOnlyChanged to CWallet’s
Implement IsLocked and IsCrypted in LegacyScriptPubKeyMan
Implement LoadCryptedKey and AddCryptedKey in LegacyScriptPubKeyMan
Implement UpdateTimeFirstKey, and GetTimeFirstKey in LegacyScriptPubKeyMan
Implement AddWatchOnly, RemoveWatchOnly, HaveWatchOnly, and LoadWatchOnly in LegacyScriptPubKeyMan
Implement AddKeyPubKey and LoadKey in LegacyScriptPubKeyMan
Implement WalletLogPrintf in LegacyScriptPubKeyMan
Implement SetHDCHain, and IsHDEnabled in LegacyScriptPubKeyMan
Implement LoadCScript in LegacyScriptPubKeyMan
Implement LoadKeyMetadata and LoadScriptMetadata in LegacyScriptPubKeyMan
Implement GetKey, HaveKey, and GetPubKey in LegacyScriptPubKeyMan
Implement GenerateNewKey in LegacyScriptPubKeyMan
Implement LoadKeyPool in LegacyScriptPubKeyMan
Implement GetOldestKeyPoolTime, KeypoolCountExternalKeys, and GetKeypoolSize in LegacyScriptPubKeyMan
Implement CanGetAddresses, CanGenerateKeys, and HavePrivateKeys in LegacyScriptPubKeyMan
Implement GenerateNewSeed, DeriveNewSeed, and SetHDSeed for LegacyScriptPubKeyMan
Implement TopUpKeypool, TopUp, and NewKeyPool in LegacyScriptPubKeyMan
Implement ReturnAddress, and KeepKey in LegacyScriptPubKeyMan
Implement GetNewAddress, and GetReservedAddress in LegacyScriptPubKeyMan
Implement MarkUnusedAddresses in LegacyScriptPubKeyMan
Implement IsMine in LegacyScriptPubKeyMan
Implement UpgradeKeyMetaData, SetupGeneration, IsFirstRun, Upgrade, RewriteDB in LegacyScriptPubKeyMan
Implement Unlock, Lock, and Encrypt and LegacyScriptPubKeyMan
Implement ImporScripts, ImportPrivKeys, ImportPubKeys, and ImportScriptPubKeys in LegacyScriptPubKeyMan
Implement GetMetadata in LegacyScriptPubKeyMan
Implement GetKeyOrigin in LegacyScriptPubKeyMan
Implement actually loading everything into LegacyScriptPubKeyMan
Implement CanProvide in LegacyScriptPubKeyMan
Replacing CWallet functions and RPC things with calls to ScriptPubKeyMan or LegacyScriptPubKeyMan. These will compile but are not expected to pass tests hence the [ci skip].
[ci skip] Remove CWallet from IsMine and have CWallet always use ScriptPubKeyMan’s IsMine
[ci skip] moveonly: move ismine stuff to be a module of LegacyScriptPubKeyMan
[ci skip] Have GetNewAddress, GetNewChangeAddress, and ReserveAddress use ScriptPubKeyMan
[ci skip] Mark used addresses in ScriptPubKeyMan
[ci skip] Call UpgradeKeyMetaData for each ScriptPubKeyMan
[ci skip] Sign using SigningProvider from ScriptPubKeyMan when signing within CWallet
[ci skip] Do not allow import*, dump*, and addmultisigaddress RPCs when wallet is not backed by LegacyScriptPubKeyMan
[ci skip] Change Imports to use LegacyScriptPubKeyMan Imports
[ci skip] Use SigningProviders and ScriptPubKeyMans in listunspent, signmessage, signrawtransactionwithwallet, and getaddressinfo
[ci skip] Use LegacyScriptPubKeyMan in addmultisigaddress and sethdseed
[ci skip] Use LegacyScriptPubKeyMan for hdseedid in getwalletinfo
[ci skip] Change KeypoolCountExternal and GetKeypoolSize to get aggregate sizes from ScriptPubKeyMans
[ci skip] Have IsHDEnabled fetch from ScriptPubKeyMans
[ci skip] Fetch oldest keypool time from ScriptPubKeyMans
[ci skip] have TopUpKeyPool call TopUp in each ScriptPubKeyMan
[ci skip] Have EncryptWallet, Lock, and Unlock call their respective functions in ScriptPubKeyMans
[ci skip] Use LegacyScriptPubKeyMan throughout psbt_wallet_tests
[ci skip] Use LegacyScriptPubKeyMan throughout wallettool
[ci skip] Use ScriptPubKeyMans’ Setup and Upgrade functions when loading or creating a wallet
[ci skip] Define first run as having no ScriptPubKeyMans
[ci skip] Use RewriteDB action when DB needs rewrite
[ci skip] Use GetTimeFirstKey instead of nTimeFirstKey
[ci skip] Use LegacyScriptPubKeyMan for in wallet_tests
[ci skip] Use LegacyScriptPubKeyMan in dumpprivkey and dumpwallet
[ci skip] Change CanGetAddresses to fetch from ScriptPubKeyMan
[ci skip] Fetch the correct SigningProvider for signing PSBTs
[ci skip] Use LegacyScriptPubKeyMan in test util
[ci skip] Use LegacyScriptPubKeyMan in some parts of getbalances and createwallet
[ci skip] Have getPubKey and getPrivKey use SigningProvider
[ci skip] Use LegacyScriptPubKeyMan in benchmarks involving the wallet
[ci skip] Store p2sh scripts in AddAndGetDestinationForScript
Tying everything together and removing the CWallet functions.
Remove unused functions and switch CWallet to use ScriptPubKeyMan
achow101
commented at 3:01 am on July 5, 2019:
member
In the coming days I will try to squash together some of these commits so there aren’t so many of them. I just wanted to get this open now for people to begin reviewing the changes.
fanquake added the label
Wallet
on Jul 5, 2019
fanquake added the label
Refactoring
on Jul 5, 2019
fanquake added this to the "PRs" column in a project
DrahtBot
commented at 4:41 am on July 5, 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:
#17246 (wallet: avoid knapsack when there’s no change by Sjors)
#17237 (wallet: LearnRelatedScripts only if KeepDestination by promag)
#17219 (wallet: allow transaction without change if keypool is empty by Sjors)
#17211 (Allow fundrawtransaction and walletcreatefundedpsbt to take external inputs by achow101)
#16946 (wallet: include a checksum of encrypted private keys by achow101)
#16910 (wallet: reduce loading time by using unordered maps by achow101)
#16839 (Replace Connman and BanMan globals with Node local by ryanofsky)
#16440 (BIP-322: Generic signed message format by kallewoof)
#16037 (rpc: Enable wallet import on pruned nodes by promag)
#15931 (Remove GetDepthInMainChain dependency on locked chain interface by ariard)
#15761 (Replace -upgradewallet startup option with upgradewallet RPC by achow101)
#15294 ([moveonly] wallet: Extract RipeMd160 by Empact)
#14942 (wallet: Make scan / abort status private to CWallet by Empact)
#14707 ([RPC] Include coinbase transactions in receivedby RPCs by andrewtoth)
#14384 (Fire TransactionRemovedFromMempool callbacks from mempool by l2a5b1)
#14144 (Refactoring: Clarify code using encrypted_batch in CWallet by domob1812)
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.
achow101 force-pushed
on Jul 5, 2019
achow101 force-pushed
on Jul 5, 2019
achow101 force-pushed
on Jul 5, 2019
achow101 force-pushed
on Jul 5, 2019
achow101 force-pushed
on Jul 5, 2019
achow101 force-pushed
on Jul 5, 2019
in
src/interfaces/wallet.cpp:168
in
1404f8ca28outdated
192@@ -170,7 +193,7 @@ class WalletImpl : public Wallet
193 *name = it->second.name;
194 }
195 if (is_mine) {
196- *is_mine = IsMine(*m_wallet, dest);
197+ *is_mine = m_wallet->IsMine(dest);
This line is merely changing how IsMine is called. In order to avoid difficulties for reviewers, the existing code (the assignment itself) is not being changed.
in
src/interfaces/wallet.h:84
in
1404f8ca28outdated
83+ // Get a new address.
84+ virtual bool getNewAddress(const OutputType type, const std::string label, CTxDestination& dest) = 0;
8586 //! Get public key.
87- virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0;
88+ virtual bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) = 0;
The old get...Key returning bool smells: either the pubKey is a boolean, or getPubKey has a side effect of getting the key somewhere else instead of as a result. Could this PR fix that by renaming it to isPubKey()?
I am trying to avoid changing existing code as much as possible in order to avoid additional review burden. Feel free to change this later, but changing it here is out of scope for this PR.
in
src/script/signingprovider.cpp:143
in
1404f8ca28outdated
This is part of a scripted diff. Such a change would both not part of the script and be unrelated to this PR.
in
src/script/keyorigin.h:13
in
1404f8ca28outdated
9+#include <streams.h>
10+#include <vector>
11+
12+struct KeyOriginInfo
13+{
14+ unsigned char fingerprint[4]; //!< First 32 bits of the Hash160 of the public key at the root of the path
This is moved code and I will not be changing it in order to avoid overburdening reviewers with ensuring that behavior hasn’t changed.
fanquake renamed this:
Introduce ScriptPubKeyMan interface and use it for key and script management (aka wallet boxes)
WIP: Introduce ScriptPubKeyMan interface and use it for key and script management (aka wallet boxes)
on Jul 6, 2019
fanquake added the label
Needs Conceptual Review
on Jul 6, 2019
fanquake
commented at 4:28 am on July 6, 2019:
member
Everyone commenting here, note that this PR is a work in progress, and is built on top of multiple open PRs.
For now we should try and keep the discussion here at a Concept ACK/NACK & design level, rather than individual nit-picks and code review.
If you would like to review the code, please do so in the open base PRs. That will not only save the concept level discussion here from being drowned out in inline comments, but will also prevent duplicate review. i.e What you’re commenting on here might have already been addressed/pointed out in the base PRs.
achow101 force-pushed
on Jul 6, 2019
achow101 force-pushed
on Jul 6, 2019
achow101 force-pushed
on Jul 6, 2019
achow101 force-pushed
on Jul 6, 2019
meshcollider
commented at 3:03 am on July 7, 2019:
contributor
Concept ACK, this abstraction is definitely the right path for the wallet to take IMO as we work toward descriptor wallets.
achow101 force-pushed
on Jul 9, 2019
DrahtBot added the label
Needs rebase
on Jul 10, 2019
achow101 force-pushed
on Jul 10, 2019
DrahtBot removed the label
Needs rebase
on Jul 10, 2019
DrahtBot added the label
Needs rebase
on Jul 10, 2019
achow101 force-pushed
on Jul 10, 2019
DrahtBot removed the label
Needs rebase
on Jul 10, 2019
Sjors
commented at 2:56 pm on July 11, 2019:
member
Approach ACK.
To make some of the commits easier to review, instead of having one commit that adds a function to LegacyScriptPubKeyMan (e.g. LegacyScriptPubKeyMan::LoadKey), one commit that deletes it from CWallet (Remove unused functions and switch CWallet to use ScriptPubKeyMan) and one commit that moves the call over (Implement actually loading everything into LegacyScriptPubKeyMan), try to combine those. That lets you get rid of the CI Skip commits, and makes it easier to verify move-only parts with --color-moved=dimmed-zebra.
In other words, start with empty (Legacy)ScriptPubKeyMan and then migrate one function per commit into it.
Are you sure you want to move WalletFeature and WalletFlags into LegacyScriptPubKeyMan? I would prefer to keep these settings global to the wallet. It does make sense for each ScriptPubKeyMan to have its own IsLocked() state, so that brings up the question what to do if a single one fails to unlock. But I’d rather not add RPC methods to unlock specific PubKeyMans in a given wallet.
I brainstormed how to apply this to hardware wallets: #14912 (comment). The unsollicited keypool topup in GetReservedDestination is annoying in that context. Can ScriptPubKeyMan have a flag to opt out of that?
achow101
commented at 5:57 pm on July 11, 2019:
member
To make some of the commits easier to review, instead of having one commit that adds a function to LegacyScriptPubKeyMan (e.g. LegacyScriptPubKeyMan::LoadKey), one commit that deletes it from CWallet (Remove unused functions and switch CWallet to use ScriptPubKeyMan) and one commit that moves the call over (Implement actually loading everything into LegacyScriptPubKeyMan), try to combine those. That lets you get rid of the CI Skip commits, and makes it easier to verify move-only parts with --color-moved=dimmed-zebra.
In other words, start with empty (Legacy)ScriptPubKeyMan and then migrate one function per commit into it.
Doing that will cause the entirety of the PR to fail all tests (so all need ci skip) except for the very last commit. If other reviewers want that, I could reorganize it.
Are you sure you want to move WalletFeature and WalletFlags into LegacyScriptPubKeyMan? I would prefer to keep these settings global to the wallet.
They are global to the wallet. They are moved to walletutil.h. Both LegacyScriptPubKeyMan and CWallet require access to both of these enums, but having scriptpubkeyman including wallet.h would be a circular dependency, so I moved it to walletutil.h.
It does make sense for each ScriptPubKeyMan to have its own IsLocked() state, so that brings up the question what to do if a single one fails to unlock.
I guess it should fail to unlock everything? That would indicate some corruption has occurred.
But I’d rather not add RPC methods to unlock specific PubKeyMans in a given wallet.
Agreed.
The unsollicited keypool topup in GetReservedDestination is annoying in that context. Can ScriptPubKeyMan have a flag to opt out of that?\
I suppose it should actually be in LegacyScriptPubKeyMan::GetReservedDestination rather than CWallet’s. However, TopUp can just do nothing and that won’t effect this behavior. Every ScriptPubKeyMan needs to implement TopUp anyways.
achow101 force-pushed
on Jul 11, 2019
Sjors
commented at 2:18 pm on July 12, 2019:
member
In other words, start with empty (Legacy)ScriptPubKeyMan and then migrate one function per commit into it.
Doing that will cause the entirety of the PR to fail all tests (so all need ci skip) except for the very last commit. If other reviewers want that, I could reorganize it.
Why would any of the test fail if ScriptPubKeyMan is just an empty shell, and/or a bunch of NOOP functions?
The unsollicited keypool topup in GetReservedDestination is annoying in that context. Can ScriptPubKeyMan have a flag to opt out of that?
I suppose it should actually be in LegacyScriptPubKeyMan::GetReservedDestination rather than CWallet’s. However, TopUp can just do nothing and that won’t effect this behavior. Every ScriptPubKeyMan needs to implement TopUp anyways.
In the context of a hardware wallet I want TopUp to fetch keys from the device. But it should only try that if the user explicitly calls topupkeypool (and the UI equivalent). Problem is that TopUp doesn’t know where it’s called from.
achow101 force-pushed
on Jul 16, 2019
achow101 force-pushed
on Jul 16, 2019
achow101 force-pushed
on Jul 16, 2019
achow101 force-pushed
on Jul 17, 2019
achow101 force-pushed
on Jul 17, 2019
achow101 force-pushed
on Jul 17, 2019
achow101 force-pushed
on Jul 18, 2019
achow101 force-pushed
on Jul 18, 2019
achow101 force-pushed
on Jul 19, 2019
achow101 force-pushed
on Jul 19, 2019
achow101 force-pushed
on Jul 19, 2019
achow101 force-pushed
on Jul 19, 2019
achow101 force-pushed
on Jul 22, 2019
achow101 force-pushed
on Jul 22, 2019
achow101 force-pushed
on Jul 22, 2019
DrahtBot added the label
Needs rebase
on Jul 24, 2019
achow101 force-pushed
on Jul 25, 2019
DrahtBot removed the label
Needs rebase
on Jul 25, 2019
achow101 force-pushed
on Jul 25, 2019
achow101 force-pushed
on Jul 25, 2019
laanwj added this to the "Chasing Concept ACK" column in a project
achow101 force-pushed
on Jul 26, 2019
achow101 renamed this:
WIP: Introduce ScriptPubKeyMan interface and use it for key and script management (aka wallet boxes)
Introduce ScriptPubKeyMan interface and use it for key and script management (aka wallet boxes)
on Jul 26, 2019
achow101
commented at 7:51 pm on July 26, 2019:
member
The PRs this depended on have been merged. I don’t foresee making large changes to this anymore, so this is no longer WIP.
Please review!
fanquake removed the label
Needs Conceptual Review
on Jul 26, 2019
achow101 force-pushed
on Jul 29, 2019
DrahtBot added the label
Needs rebase
on Jul 30, 2019
achow101 force-pushed
on Jul 30, 2019
DrahtBot removed the label
Needs rebase
on Jul 30, 2019
achow101 force-pushed
on Jul 30, 2019
achow101 force-pushed
on Jul 30, 2019
achow101 force-pushed
on Jul 30, 2019
DrahtBot added the label
Needs rebase
on Jul 31, 2019
achow101 force-pushed
on Jul 31, 2019
DrahtBot removed the label
Needs rebase
on Jul 31, 2019
DrahtBot added the label
Needs rebase
on Aug 1, 2019
achow101 force-pushed
on Aug 1, 2019
DrahtBot removed the label
Needs rebase
on Aug 1, 2019
laanwj moved this from the "Chasing Concept ACK" to the "Blockers" column in a project
DrahtBot added the label
Needs rebase
on Aug 2, 2019
achow101 force-pushed
on Aug 2, 2019
DrahtBot removed the label
Needs rebase
on Aug 2, 2019
The nice thing about the way it was done before is that if this expr returns false it would get short circuited and CreateSig wouldn’t be called too.
Now it’s the opposite. which I think makes less sense when we know that CreateSig is more work than that simple expr, so it might do CreateSig just to fail that if afterwards.
This is necessary for LegacyScriptPubKeyMan::CanProvide to pass. That uses the DUMMY_SIGNATURE_CREATOR which creates dummy signatures without checking whether the SigningProvider has the private keys (as it should). However, in CanProvide, we want to make sure that we do actually have the private keys, and if the dummy signing returns us dummy signatures that correspond to keys that we don’t have, CanProvide will fail. So we need it to give us all of the signatures (by putting them in the sigdata) so that CanProvide can check for whether it actually could sign.
ryanofsky
commented at 6:33 pm on August 22, 2019:
In commit “Always try to sign for all pubkeys in multisig” (d0b41dc26a74e158bd22259aed50869c9676e3fd)
I’m not deeply familiar with this code, and I found the explanation a little hard to follow, but in case it helps anyone else, the key seems to be that calling CreateSig unconditionally is needed to fill sigdata. I can confirm that rpc_psbt.py test fails without this change.
instagibbs
commented at 3:21 pm on August 27, 2019:
I also found this explanation difficult. Is there any way to succinctly say the desired effect, and put it in a comment in the code?
Can you add the bit about LegacyScriptPubKeyMan::CanProvide as well to the comment?
achow101
commented at 5:30 pm on September 18, 2019:
I don’t think it makes sense to have a comment with such specific information about the wallet in the non-wallet signing code. There’s already a comment there explaining why we try to sign for all keys.
in
src/outputtype.cpp:95
in
ca02ac2532outdated
93 CScript witprog = GetScriptForDestination(witdest);
94 // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
95- if (!IsSolvable(keystore, witprog)) return ScriptHash(script);
96+ if (!IsSolvable(keystore, witprog)) {
97+ keystore.AddCScript(GetScriptForDestination(sh));
98+ return ScriptHash(script);
I think this commit deserves its own PR anyway; as does any refactor that’s a prequisite for the main PR.
achow101 force-pushed
on Aug 5, 2019
Sjors
commented at 1:03 pm on August 13, 2019:
member
Found a bug (try in QT):
create a wallet blank wallet with private keys disabled
see that new address button in receive screen is disabled, as it should
close QT and start again, load the wallet
see that new address button is enabled and creates addresses (panic)
Maybe Always try to sign for all pubkeys in multisig can be its own PR?
I didn’t pay precise attention to which methods are private / public / etc, because that’s easy to change later as required.
Shameless plug for #12134 so we can test for accidental breakage of serialization. The existing test passes on top of this PR, but it only checks that a default “master” wallet can still be opened in earlier versions).
in
src/wallet/scriptpubkeyman.h:119
in
bae5b518ecoutdated
115@@ -85,7 +116,9 @@ class ScriptPubKeyMan
116 class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider
117 {
118 public:
119- LegacyScriptPubKeyMan() {}
120+ LegacyScriptPubKeyMan(FlagSetFunc is_set_func, FlagFunc set_flag_func, FlagFuncWithDB unset_flag_func, VersionFunc feature_sup_func, NameFunc wallet_name_func, SetVersionFunc set_version_func, std::shared_ptr<WalletDatabase> database)
ryanofsky
commented at 6:49 pm on August 22, 2019:
In commit “Add function callbacks for wallet flags and versions and wallet database” (bae5b518ec507d071a54ce4810dd24b016bcfe12)
This could just say using ScriptPubKeyMan::ScriptPubKeyMan to inherit the parent constructor instead of repeating it all. Even at the end of this PR, this constructor is empty except for some member initializations which would be better to do at the points of declaration.
ryanofsky
commented at 7:05 pm on August 22, 2019:
member
Started review (will update list below with progress). Heroic effort here by achow! I’ve started to review a few times, but put it off for too long. Time to seriously jump in.
My initial impression here is in line with Sjors, that it would have been wiser to have sequence of commits gradually moving bits of code and bits of data with all tests passing at every commit, rather than a sequence of commits copying then a sequence of commits deleting. Moving is nicer than copying for review, because you can see the original code right in the diff instead of having to search for it manually. But I know there was an earlier attempt at this, and it didn’t go well for whatever reason, and I don’t think it’s worth putting in more effort just to restructure the PR.
d0b41dc26a74e158bd22259aed50869c9676e3fd Always try to sign for all pubkeys in multisig (1/70)
e892a72f22c3359a12085f0e93d519acaa711bcd Introduce both ScriptPubKeyMan as an interface and LegacyScriptPubKeyMan as a dummy class (2/70)
8a8e141fe93bf887630df9681daa9cd8b2500f55 List output types in an array in order to be iterated over (3/70)
7a27857f91ad585f82bb784faf092e8fe7e30946 Add LegacyScriptPubKeyMan to CWallet (4/70)
16e82f0b9252cddb50b0aeeaedb13a2ebf532c29 Move wallet enums to walletutil.h (5/70)
bae5b518ec507d071a54ce4810dd24b016bcfe12 Add function callbacks for wallet flags and versions and wallet database (6/70)
493486c32ad084099636c8696c10f4e21f56326e Fetch the SigningProvider for a script from the wallet (7/70)
bec9b6c2f00113c4bd789562f1333d30c959ed00 Fetch the ScriptPubKeyMan for given output type and internal-ness, or a given script, or ScriptPubKeyMan id (8/70)
b49bbb939be92a67ff77c3f7bca5bb94dd141906 Refactor rawtransaction_util’s SignTransaction to have previous tx parsing be separate (9/70)
a31374df794d709cf8acfbafc7d5b57f74b1d75a Implement GetSigningProvider in LegacyScriptPubKeyMan (10/70)
7b57deac879499e4b0ee99f1efc320600907bc59 Implement function to connect ScriptPubKeyMan’s NotifyCanGetAddessesChanged and NotifyWatchOnlyChanged to CWallet’s (11/70)
72a44f23a9000765bac8102f53d6606c3f0b0fe0 Implement IsLocked and IsCrypted in LegacyScriptPubKeyMan (12/70)
fce5df284b2fc1b0b7f9582275c11dcce8dded86 Implement LoadCryptedKey and AddCryptedKey in LegacyScriptPubKeyMan (13/70)
6685ff5f9c0265094b97aa5aaa03d6b10ec398bb Implement UpdateTimeFirstKey, and GetTimeFirstKey in LegacyScriptPubKeyMan (14/70)
eeff18e1a8e2240a9e0d0fdd252ae05f36e7f502 Implement AddWatchOnly, RemoveWatchOnly, HaveWatchOnly, and LoadWatchOnly in LegacyScriptPubKeyMan (15/70)
95522e7f84fc311521a5b4246a44a314cdc9a5ed Implement AddKeyPubKey and LoadKey in LegacyScriptPubKeyMan (16/70)
c88536aaab4af1d54b42d8e95d7f9a493d1919f7 Implement WalletLogPrintf in LegacyScriptPubKeyMan (17/70)
977cb6552f033f826bc06198b1855790e61a0e8a Implement SetHDCHain, and IsHDEnabled in LegacyScriptPubKeyMan (18/70)
bf224a9ccc477aa7c20c3bfd6b448b1cdf7f15c0 Implement LoadCScript in LegacyScriptPubKeyMan (19/70)
e205c2c933ebde193cce7b926be240ed3f226e31 Implement LoadKeyMetadata and LoadScriptMetadata in LegacyScriptPubKeyMan (20/70)
7432db927f5990b13539999508e47a8c8d8fec1e Implement GetKey, HaveKey, and GetPubKey in LegacyScriptPubKeyMan (21/70)
0922f3f9370f00feb84690bc7942ecac9737d492 Implement GenerateNewKey in LegacyScriptPubKeyMan (22/70)
c0b6d2b211a39a50e32728e4d8f52c2bf2ad7e79 Implement LoadKeyPool in LegacyScriptPubKeyMan (23/70)
d3e3cc9e9f5ecd9512f4eeb8b51a789f05ac0529 Implement GetOldestKeyPoolTime, KeypoolCountExternalKeys, and GetKeypoolSize in LegacyScriptPubKeyMan (24/70)
acbd549766a89c759b3a1aa435bd001c7082a323 Implement CanGetAddresses, CanGenerateKeys, and HavePrivateKeys in LegacyScriptPubKeyMan (25/70)
5e7d3149895228c4b7b7c7c582f877938b217b23 Implement GenerateNewSeed, DeriveNewSeed, and SetHDSeed for LegacyScriptPubKeyMan (26/70)
d229452d6dbd94331986d267b46d13af745bf98d Implement TopUpKeypool, TopUp, and NewKeyPool in LegacyScriptPubKeyMan (27/70)
4500e0af8f964289acb680bd76031c59ffa8b968 Implement ReturnAddress, and KeepKey in LegacyScriptPubKeyMan (28/70)
a7855dd0cf12314ad4a99ff77de22e60f8a9f435 Implement GetNewAddress, and GetReservedAddress in LegacyScriptPubKeyMan (29/70)
9947416fef270701c9d22fc914830448112e6bb8 Implement MarkUnusedAddresses in LegacyScriptPubKeyMan (30/70)
0ff797c105a3917c4b2fdcf0bb172125fe91349a Implement IsMine in LegacyScriptPubKeyMan (31/70)
eee7b80e8c8a43b7a41bdb6df68735f295f777d2 Implement UpgradeKeyMetaData, SetupGeneration, IsFirstRun, Upgrade, RewriteDB in LegacyScriptPubKeyMan (32/70)
7a5ad98e1733f44f7b71b7247f540dd97acdcb4e Implement Unlock, Lock, and Encrypt and LegacyScriptPubKeyMan (33/70)
f489836fdd5ced869babe04352c473c678d2a9a3 Implement ImporScripts, ImportPrivKeys, ImportPubKeys, and ImportScriptPubKeys in LegacyScriptPubKeyMan (34/70)
cb1519c317ebcf5762567018f881d1faa5957734 Implement GetMetadata in LegacyScriptPubKeyMan (35/70)
b893f0d053f1b5be1229d4fb77ced46b222f9993 Implement GetKeyOrigin in LegacyScriptPubKeyMan (36/70)
93322bf756fd8991fb445e327c878e2695fe9ca9 Implement actually loading everything into LegacyScriptPubKeyMan (37/70)
ae87c89f2e66dc7cb07c541261069c08ad809ddb Implement CanProvide in LegacyScriptPubKeyMan (38/70)
6ac3cf53cca1714f6d6c9fb3ebe16824b9ae49ea [ci skip] Remove CWallet from IsMine and have CWallet always use ScriptPubKeyMan’s IsMine (39/70)
d33724a6ca11134fa4ea1f80cacbc5b873c837e3 [ci skip] moveonly: move ismine stuff to be a module of LegacyScriptPubKeyMan (40/70)
6efeaa2ea30a4c128101ddf4cb9ee1eebb08aa9a [ci skip] Have GetNewAddress, GetNewChangeAddress, and ReserveAddress use ScriptPubKeyMan (41/70)
9603366a7879c225aeef97988323fc482eef5f44 [ci skip] Mark used addresses in ScriptPubKeyMan (42/70)
bc6d7687f9db39577d280c500896493de1525f2f [ci skip] Call UpgradeKeyMetaData for each ScriptPubKeyMan (43/70)
18602887c67b68f141d43f187d52ce32ae3af02b [ci skip] Sign using SigningProvider from ScriptPubKeyMan when signing within CWallet (44/70)
f95622c8db42713e617dea3af79be0531c8c69ec [ci skip] Do not allow import*, dump*, and addmultisigaddress RPCs when wallet is not backed by LegacyScriptPubKeyMan (45/70)
08f509b7d99cf3e5a6e4654c45e411dafd3c94f7 [ci skip] Change Imports to use LegacyScriptPubKeyMan Imports (46/70)
086225731c5bb99438d5cda6174a459332e1abdf [ci skip] Use SigningProviders and ScriptPubKeyMans in listunspent, signmessage, signrawtransactionwithwallet, and getaddressinfo (47/70)
74777d18512f5ff38c0af86cc648fcf60c8d1ea4 [ci skip] Use LegacyScriptPubKeyMan in addmultisigaddress and sethdseed (48/70)
bac9fda14e9bf597d85d51cebfae210a03c33c07 [ci skip] Use LegacyScriptPubKeyMan for hdseedid in getwalletinfo (49/70)
0a23a92785a2bc0eef233ad4c2ac58b7c354ff88 [ci skip] Change KeypoolCountExternal and GetKeypoolSize to get aggregate sizes from ScriptPubKeyMans (50/70)
5db4f1fbf38cf7ad55f358d1fe918cb776e72fc9 [ci skip] Have IsHDEnabled fetch from ScriptPubKeyMans (51/70)
29642268998127a7e46ad4ccf04cbf2bc66e1653 [ci skip] Fetch oldest keypool time from ScriptPubKeyMans (52/70)
662d0002a3bc31be2553800b1ac9d2666df7b680 [ci skip] have TopUpKeyPool call TopUp in each ScriptPubKeyMan (53/70)
e32509e49d5f4301d6dd677d682b929caa3a2494 [ci skip] Have EncryptWallet, Lock, and Unlock call their respective functions in ScriptPubKeyMans (54/70)
cb12ea9cc8f0df83f9e29b0ac8abe48567ce4fa1 [ci skip] Use LegacyScriptPubKeyMan throughout psbt_wallet_tests (55/70)
39e9d613aebfa611b3f599916cd595a66500c595 [ci skip] Use LegacyScriptPubKeyMan throughout wallettool (56/70)
dbb0d8f6af94300dbe84e0dd1595e0a9d45d3d07 [ci skip] Use ScriptPubKeyMans’ Setup and Upgrade functions when loading or creating a wallet (57/70)
c09a7967dab25904218e6b850e871f49b4f9ea78 [ci skip] Define first run as having no ScriptPubKeyMans (58/70)
d188254886cf7ac429c1d6ebcb1e18f09f215a61 [ci skip] Use RewriteDB action when DB needs rewrite (59/70)
5a837cbec18b6e7bf6903816f479b96fc31f0b7e [ci skip] Use GetTimeFirstKey instead of nTimeFirstKey (60/70)
04456084ef2600909a2d162dbfae9197afcb94bb [ci skip] Use LegacyScriptPubKeyMan for in wallet_tests (61/70)
2dd631c391fee7fccaba4d536d08306de867accd [ci skip] Use LegacyScriptPubKeyMan in dumpprivkey and dumpwallet (62/70)
dadcbef6226ed1cddcf18ebbbe12ca8470d56946 [ci skip] Change CanGetAddresses to fetch from ScriptPubKeyMan (63/70)
f353a3ca99600e6e25b92e478cb5592c69a62ed8 [ci skip] Fetch the correct SigningProvider for signing PSBTs (64/70)
fef2ffa7ac0df3373ea3146e11057a1932736336 [ci skip] Use LegacyScriptPubKeyMan in test util (65/70)
6aac37c38c5d8d3aea67209931a7de5a14fe92bb [ci skip] Use LegacyScriptPubKeyMan in some parts of getbalances and createwallet (66/70)
43819a86b248c089bcfc342e7c7ce43060f507c1 [ci skip] Have getPubKey and getPrivKey use SigningProvider (67/70)
4e7c2d8fc7835133c39ad8b8c7a805d49679c0f4 [ci skip] Use LegacyScriptPubKeyMan in benchmarks involving the wallet (68/70)
dc262606b03486af8045b71e78b8292ebf225536 [ci skip] Store p2sh scripts in AddAndGetDestinationForScript (69/70)
c5f900358e582607c8386430ea3a63f02533b64e Remove unused functions and switch CWallet to use ScriptPubKeyMan (70/70)
in
src/wallet/scriptpubkeyman.h:145
in
e892a72f22outdated
13+
14+#include <boost/signals2/signal.hpp>
15+
16+enum class OutputType;
17+
18+class ScriptPubKeyMan
instagibbs
commented at 3:27 pm on August 27, 2019:
Please add a short comment on what this is in charge of and what it’s not.
in
src/wallet/wallet.cpp:4960
in
7a27857f91outdated
4955+ m_spk_managers[spk_manager->GetID()] = spk_manager;
4956+ }
4957+ // These all need to exist and be the same
4958+ assert(m_internal_spk_managers.count(OutputType::LEGACY) > 0);
4959+ std::shared_ptr<ScriptPubKeyMan> spk_man = m_internal_spk_managers.at(OutputType::LEGACY);
4960+ assert(m_internal_spk_managers.at(OutputType::P2SH_SEGWIT) == spk_man);
instagibbs
commented at 3:49 pm on August 27, 2019:
we have the list of output types, let’s just iterate through them here to assert these
in
src/wallet/scriptpubkeyman.h:174
in
a31374df79outdated
166@@ -167,4 +167,20 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
167 uint256 GetID() const override;
168 };
169170+/** Wraps a LegacyScriptPubKeyMan so that it can be returned in a new unique_ptr */
171+class LegacySigningProvider : public SigningProvider
172+{
173+private:
174+ const LegacyScriptPubKeyMan* spk_man;
ryanofsky
commented at 5:01 pm on August 27, 2019:
In commit “Implement GetSigningProvider in LegacyScriptPubKeyMan” (a31374df794d709cf8acfbafc7d5b57f74b1d75a)
Not important, but this should never be null so a reference makes more sense than a pointer in this class
ryanofsky
commented at 5:04 pm on August 27, 2019:
In commit “Implement function to connect ScriptPubKeyMan’s NotifyCanGetAddessesChanged and NotifyWatchOnlyChanged to CWallet’s” (7b57deac879499e4b0ee99f1efc320600907bc59)
const auto& instead of auto would avoid copying and incrementing / decrementing shared_ptr atomic reference counts, would also make it clear that the std::pair is not changing
instagibbs
commented at 6:43 pm on August 30, 2019:
member
Suggestion just to see what others think: Split up this PR into a couple PRs, first of which is the non-test-breaking changes, the 2nd all the breaking changes+test fixes at the end?
ryanofsky
commented at 6:48 pm on August 30, 2019:
member
Suggestion just to see what others think: Split up this PR into a couple PRs, first of which is the non-test-breaking changes, the 2nd all the breaking changes+test fixes at the end?
I definitely would not want this because it would temporarily add a huge amount of duplicate code to the wallet. If this needs to be broken up to be reviewed (and I don’t think it does), I’d either go with sjors approach moving things gradually, or just have one giant move-only commit followed by the actual code changes.
ryanofsky
commented at 6:52 pm on August 30, 2019:
member
Or, in case I misunderstood, if you are just talking about splitting the first 9 commits into their own PR (the commits before the “Implement…” commits and the “ci skip” commits), that seems perfectly fine and would help a little.
achow101 force-pushed
on Aug 30, 2019
achow101 force-pushed
on Aug 30, 2019
achow101 force-pushed
on Aug 30, 2019
achow101
commented at 8:12 pm on August 30, 2019:
member
DrahtBot removed the label
Needs rebase
on Aug 30, 2019
Sjors
commented at 9:03 am on August 31, 2019:
member
The blank wallet bug is indeed fixed.
I like the idea of splitting the first 9 commits out, maybe into two or three PRs. The refactors touch very different spots, so could be multiple PRs. At minimum I would put the pure refactors and new ScriptPubKeyman stuff separate PRs. Right now the commits alternate between refactors and adding new stuff.
d9ea84699fc0c63003b759ec8e41802a3e1a2cec Always try to sign for all pubkeys in multisig (haven’t reviewed yet)
d2d43ab141e7f27b3c965ebbf5e7db10476837f3 Move wallet enums to walletutil.hACK, confirmed move only
In general I suggest splitting as much as possible out into followup, prerequisite and parallel PRs. Then let’s see if we can review the remaining blob as a whole, or keep chipping small bits off.
I’m unsure about splitting the other two “unrelated” commits since they don’t actually do anything useful outside of this PR.
d2d43ab is purely to avoid a circular dependency, and d9ea846 is only to fix a side effect of this PR.
Also reordered the first several commits so the “unrelated” commits come first.
achow101 force-pushed
on Sep 3, 2019
in
src/wallet/wallet.h:1324
in
c4cbc90efeoutdated
1318@@ -1319,6 +1319,16 @@ class CWallet final : public FillableSigningProvider, private interfaces::Chain:
1319 /** Implement lookup of key origin information through wallet key metadata. */
1320 bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
13211322+ std::set<std::shared_ptr<ScriptPubKeyMan>> GetActiveScriptPubKeyMans() const;
1323+
1324+ //! Get the ScriptPubKeyMan for the given OutputType and internal/external chain. If it doesn't exist, instantiate one
instagibbs
commented at 8:06 pm on September 6, 2019:
this function doesn’t create one, just returns null
achow101
commented at 11:17 pm on September 6, 2019:
Edited comment.
in
src/wallet/scriptpubkeyman.h:237
in
f504e8a537outdated
122@@ -123,6 +123,15 @@ class ScriptPubKeyMan
123124 class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider
125 {
126+private:
127+ //! if fUseCrypto is true, mapKeys must be empty
128+ //! if fUseCrypto is false, vMasterKey must be empty
129+ std::atomic<bool> fUseCrypto{false};
instagibbs
commented at 8:10 pm on September 6, 2019:
I know this is from CWallet but I’d rather avoid hungarian notation for new variables: m_use_crypto
instagibbs
commented at 9:03 pm on September 6, 2019:
on second thought, nevermind. for now straight copy is good.
in
src/wallet/scriptpubkeyman.h:244
in
f504e8a537outdated
128+ //! if fUseCrypto is false, vMasterKey must be empty
129+ std::atomic<bool> fUseCrypto{false};
130+
131+ bool SetCrypted();
132+
133+ CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore);
instagibbs
commented at 8:13 pm on September 6, 2019:
I know there’s no documentation where you got this from but could there be an explanation of this field?
achow101
commented at 11:18 pm on September 6, 2019:
Done
in
src/wallet/scriptpubkeyman.cpp:210
in
f504e8a537outdated
instagibbs
commented at 8:17 pm on September 6, 2019:
I know this is cloning but std::make_pair like everywhere else
achow101
commented at 11:18 pm on September 6, 2019:
Done
in
src/wallet/scriptpubkeyman.h:272
in
aba2ac2b7eoutdated
161@@ -161,6 +162,9 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
162 //! Adds a watch-only address to the store, and saves it to disk.
163 bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
164165+ //! Adds a key to the store, and saves it to disk.
instagibbs
commented at 8:28 pm on September 6, 2019:
please change all these “store” references to pubkeyman references
achow101
commented at 11:11 pm on September 6, 2019:
I don’t think it’s necessary to change all of these.
in
src/wallet/scriptpubkeyman.cpp:485
in
4d53fa7f1coutdated
480+ secret.MakeNewKey(fCompressed);
481+ }
482+
483+ // Compressed public keys were introduced in version 0.6.0
484+ if (fCompressed) {
485+ SetMinVersion(FEATURE_COMPRPUBKEY, nullptr, false);
instagibbs
commented at 8:50 pm on September 6, 2019:
this seems to add the optional params for no reason vs master. remove them, or at least annotate what they mean.
achow101
commented at 11:18 pm on September 6, 2019:
Removed the optional params. They still need to be specified in when binding the function though.
in
src/wallet/scriptpubkeyman.h:351
in
c43d7572a2outdated
instagibbs
commented at 8:58 pm on September 6, 2019:
renamed to GetTotalKeypoolSize to make it clear it counts both?
achow101
commented at 11:10 pm on September 6, 2019:
It’s always counted both, and I would prefer to keep this consistent with CWallet::GetKeypoolSize.
instagibbs
commented at 9:03 pm on September 6, 2019:
member
reviewed through d7ff25c778ee1fbafa349b4623b3c5241ca0097c : “Implement CanGetAddresses, CanGenerateKeys, and HavePrivateKeys in LegacyScriptPubKeyMan”
achow101 force-pushed
on Sep 6, 2019
fanquake referenced this in commit
46494b08e2
on Sep 7, 2019
DrahtBot added the label
Needs rebase
on Sep 7, 2019
achow101 force-pushed
on Sep 7, 2019
DrahtBot removed the label
Needs rebase
on Sep 7, 2019
instagibbs
commented at 2:42 pm on September 9, 2019:
member
only changes through “Implement CanGetAddresses, CanGenerateKeys, and HavePrivateKeys in LegacyScriptPubKeyMan” were the suggested ones in above review.
in
src/wallet/scriptpubkeyman.cpp:17
in
a007c8ac72outdated
instagibbs
commented at 3:14 pm on September 9, 2019:
missing error.clear() that CWallet has?
achow101
commented at 6:00 pm on September 9, 2019:
Done
in
src/wallet/scriptpubkeyman.h:295
in
a007c8ac72outdated
283@@ -284,6 +284,25 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
284 std::map<CKeyID, int64_t> m_pool_key_to_index;
285 std::map<int64_t, CKeyID> m_reserved_key_to_index;
instagibbs
commented at 3:16 pm on September 9, 2019:
realized this doesn’t exist in master, please put explanation in comment on what it’s used for
achow101
commented at 6:00 pm on September 9, 2019:
Done.
in
src/wallet/scriptpubkeyman.cpp:154
in
c83bb3c7adoutdated
149+ if (GetPubKey(meta_pair.first, pubkey)) {
150+ batch->WriteKeyMetadata(meta, pubkey, true);
151+ }
152+ }
153+ }
154+ batch.reset(); //write before setting the flag
instagibbs
commented at 3:33 pm on September 9, 2019:
there’s no flag being set here anymore. If intentional, I’d like an explanation and fixup of this comment
achow101
commented at 6:00 pm on September 9, 2019:
Removed the comment.
in
src/wallet/scriptpubkeyman.h:181
in
365879e9bdoutdated
176+ virtual void MarkUnusedAddresses(const CScript& script) {}
177+
178+ //! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo
179+ virtual void UpgradeKeyMetadata() {}
180+
181+ /** Sets up the key generation stuff.
instagibbs
commented at 3:37 pm on September 9, 2019:
please elaborate on stuff :P
it’s important for spotting missing functionality since I don’t think there’s an equiv block in old wallet code
instagibbs
commented at 3:38 pm on September 9, 2019:
seems a bit of a misnomer even based on my reading
achow101
commented at 5:37 pm on September 9, 2019:
The equivalent blocks in old wallet code are the various SetHDSeed(GenerateNewSeed()) lines scattered around in a bunch of places.
How is it a misnomer?
achow101
commented at 6:00 pm on September 9, 2019:
I updated the comment.
in
src/wallet/scriptpubkeyman.cpp:411
in
c83bb3c7adoutdated
203+ // generate a new master key
204+ SetHDSeed(GenerateNewSeed());
205+ hd_upgrade = true;
206+ }
207+ // Upgrade to HD chain split if necessary
208+ if (CanSupportFeature(FEATURE_HD_SPLIT) && hdChain.nVersion < 2 /* VERSION_HD_CHAIN_SPLIT */) {
instagibbs
commented at 3:41 pm on September 9, 2019:
any reason you’re not actually referencing VERSION_HD_CHAIN_SPLIT directly?
instagibbs
commented at 3:42 pm on September 9, 2019:
and why the logic change here? master doesn’t have this version check
achow101
commented at 5:44 pm on September 9, 2019:
Referencing VERSION_HD_CHAIN_SPLIT directly requires including walletdb.h which is a circular dependency.
The logic is slightly different here because without it, it would end up marking the newly generated keys as pre-split on later runs on the same LegacyScriptPubKeyMan. This is an issue because for a LegacyScriptPubKeyMan, Upgrade() will be called 6 times (once for each address type and internalness). If you were to run -upgradewallet multiple times on master, you would see the same problem.
in
src/wallet/scriptpubkeyman.cpp:436
in
365879e9bdoutdated
instagibbs
commented at 3:45 pm on September 9, 2019:
this has nothing to do with the DB, other than it’s called right after database->Rewrite("\x04pool") by the caller.
Please rename it to something sensible e.g., ClearKeypool
achow101
commented at 5:46 pm on September 9, 2019:
It’s supposed to be post-processing that is done by ScriptPubKeyMan when the db had to be rewritten, hence the name. Other ScriptPubKeyMans may do something different.
in
src/wallet/scriptpubkeyman.cpp:638
in
b5cb8f59caoutdated
instagibbs
commented at 3:52 pm on September 9, 2019:
can you explain the diff in code from master with the batch pointer being set in this function, when it’s set to null, etc?
achow101
commented at 5:50 pm on September 9, 2019:
encrypted_batch used to be set from within EncryptWallet before EncryptKeys was called. Since EncryptKeys is now ScriptPubKeyMan::Encrypt, it doesn’t have access to CWallet’s encrypted_batch, so we need to pass it in and set it when the ScriptPubKeyMan is encrypting.
in
src/wallet/scriptpubkeyman.cpp:1427
in
c02e6daf18outdated
1236+ for (const CScript& script : script_pub_keys) {
1237+ if (!have_solving_data || !IsMine(script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
1238+ if (!AddWatchOnlyWithDB(batch, script, timestamp)) {
1239+ return false;
1240+ }
1241+ }
instagibbs
commented at 3:58 pm on September 9, 2019:
Can you explain where the SetAddressBookWithDB call went for reviewers?
achow101
commented at 5:22 pm on September 9, 2019:
It remains in wallet.cpp since the address book isn’t part of ScriptPubKeyMan.
in
src/wallet/scriptpubkeyman.h:211
in
365879e9bdoutdated
instagibbs
commented at 4:02 pm on September 9, 2019:
Brief comment on what this does? (I don’t think similar existed before in CWallet)
achow101
commented at 5:53 pm on September 9, 2019:
It just returns a CKeyMetadata as the function name suggests.
It is needed so that getting metadata no longer requires direct access to mapKeyMetadata which no longer exists in CWallet and is not guaranteed to exist in other ScriptPubKeyMans (e.g. it is generated on the fly in native descriptor wallets).
in
src/script/signingprovider.h:75
in
6806ad09a1outdated
instagibbs
commented at 4:26 pm on September 9, 2019:
Maybe leave a comment in this block that the pubkeyman has no use for this key, it’s just an encryption key. I was tripped up on this. Bad naming! bad!
achow101
commented at 6:02 pm on September 9, 2019:
Done
in
src/wallet/scriptpubkeyman.cpp:502
in
6c7c36276eoutdated
instagibbs
commented at 4:28 pm on September 9, 2019:
please explain in header what it CanProvide
achow101
commented at 6:02 pm on September 9, 2019:
Done
in
src/wallet/scriptpubkeyman.cpp:482
in
6c7c36276eoutdated
302+ } else if (HaveCScript(CScriptID(script))) {
303+ return true;
304+ } else {
305+ ProduceSignature(*this, DUMMY_SIGNATURE_CREATOR, script, sigdata);
306+ if (!sigdata.signatures.empty()) {
307+ // If we could make signatures, make sure we have the private key
instagibbs
commented at 4:30 pm on September 9, 2019:
why?
achow101
commented at 5:26 pm on September 9, 2019:
It could be watch only (as DUMMY_SIGNATURE_CREATOR works for watch only things), which then causes issues elsewhere when a private key is assumed to exist but doesn’t for watch only things. So this prevents those code paths being taken for watch only things.
Should this say “we have any private key” instead of the private key? “DUMMY_SIGNATURE_CREATOR works for watch only things” is useful to add as a comment. Can you also explain under what circumstance DUMMY_SIGNATURE_CREATOR does not produce a signature? It’s also useful to reiterate here that ProduceSignature doesn’t necessarily produce an ECDSA signature, but a script signature, which in turn may contain a (dummy?) ECDSA signature.
achow101
commented at 6:20 pm on September 17, 2019:
I’m not writing an essay. It is completely unnecessary to describe what DUMMY_SIGNATURE_CREATOR does here as you can just look at the sign.cpp/h where it is defined and read the comments and code there yourself. It’s less than 10 lines.
instagibbs
commented at 4:32 pm on September 9, 2019:
member
reviewed through 6c7c36276e26539edc8b281129bb30f1cd4a4fb8 “Implement CanProvide in LegacyScriptPubKeyMan”
in
src/wallet/wallet.cpp:3093
in
6a91646790outdated
3716- CPubKey new_key;
3717- if (!GetKeyFromPool(new_key)) {
3718- error = "Error: Keypool ran out, please call keypoolrefill first";
3719- return false;
3720+ bool result = false;
3721+ auto spk_man = GetScriptPubKeyMan(type, false);
instagibbs
commented at 5:14 pm on September 9, 2019:
nit: annotate the bool arg, or make it an enum
achow101
commented at 11:34 pm on September 9, 2019:
Done
in
src/wallet/wallet.cpp:3283
in
6a91646790outdated
3921+ spk_man = pwallet->GetScriptPubKeyMan(type, internal);
3922+ if (!spk_man) {
3923+ return false;
3924+ }
3925+
3926+ if (!spk_man->IsLocked()) {
instagibbs
commented at 5:18 pm on September 9, 2019:
no need to gate here, TopUp already checks for this in TopUpKeyPool
achow101
commented at 11:34 pm on September 9, 2019:
Done
in
src/wallet/wallet.cpp:3287
in
6a91646790outdated
3925+
3926+ if (!spk_man->IsLocked()) {
3927+ spk_man->TopUp();
3928+ }
3929+
3930+ if (!spk_man->CanGetAddresses(internal)) {
instagibbs
commented at 5:19 pm on September 9, 2019:
LegacyScriptPubKeyMan::GetReservedDestination does this check as well
achow101
commented at 11:35 pm on September 9, 2019:
Done
in
src/wallet/wallet.cpp:3922
in
6a91646790outdated
instagibbs
commented at 5:38 pm on September 9, 2019:
just use spk_man here (if you cannot, please annotate why in comments)
achow101
commented at 11:35 pm on September 9, 2019:
Done
in
src/wallet/wallet.cpp:1415
in
c78b003846outdated
1841+ auto spk_man = GetLegacyScriptPubKeyMan();
1842+ if (!spk_man) {
1843+ return false;
1844+ }
1845+ LOCK(spk_man->cs_KeyStore);
1846+ if (!GetLegacyScriptPubKeyMan()->ImportScriptPubKeys(script_pub_keys, have_solving_data, timestamp)) {
instagibbs
commented at 5:39 pm on September 9, 2019:
just use spk_man here (if you cannot, please annotate why in comments)
achow101
commented at 11:35 pm on September 9, 2019:
Done
achow101 force-pushed
on Sep 9, 2019
in
src/wallet/wallet.cpp:3052
in
f4ff4c5f1aoutdated
3540- WalletBatch batch(*database);
3541- for (int64_t i = missingInternal + missingExternal; i--;)
3542- {
3543- if (i < missingInternal) {
3544- internal = true;
3545+ bool res = true;
instagibbs
commented at 6:03 pm on September 9, 2019:
why not just iterate over GetActiveScriptPubKeyMans result, should be more complete and less brittle to updates?
achow101
commented at 11:35 pm on September 9, 2019:
Done
in
src/wallet/wallet.cpp:4266
in
3e6cd81d3doutdated
4287- if (hd_upgrade) {
4288- if (!walletInstance->TopUpKeyPool()) {
4289- chain.initError(_("Unable to generate keys").translated);
4290- return nullptr;
4291+
4292+ for (bool internal : {false, true}) {
instagibbs
commented at 6:08 pm on September 9, 2019:
GetActiveScriptPubKeyMans?
achow101
commented at 11:35 pm on September 9, 2019:
Done
in
src/wallet/wallet.cpp:4453
in
e3b5a71ae0outdated
4448@@ -4449,8 +4449,17 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
44494450 // No need to read and scan block if block was created before
4451 // our wallet birthday (as adjusted for block time variability)
4452- if (walletInstance->nTimeFirstKey) {
4453- if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height, nullptr)) {
4454+ int64_t time_first_key = std::numeric_limits<int64_t>::max();
4455+ for (bool internal : {false, true}) {
instagibbs
commented at 6:11 pm on September 9, 2019:
GetActiveScriptPubKeyMans?
achow101
commented at 11:35 pm on September 9, 2019:
Done
in
src/wallet/wallet.cpp:219
in
595fb67238outdated
213@@ -214,9 +214,17 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
214 }
215216 // Set a seed for the wallet
217- CPubKey master_pub_key = wallet->GenerateNewSeed();
218- wallet->SetHDSeed(master_pub_key);
219- wallet->NewKeyPool();
220+ {
221+ LOCK(wallet->cs_wallet);
222+ for (bool internal : {false, true}) {
instagibbs
commented at 6:16 pm on September 9, 2019:
GetActiveScriptPubKeyMans?
achow101
commented at 11:35 pm on September 9, 2019:
There was a bit more back and forth, but yeah… :-(
instagibbs
commented at 6:21 pm on September 9, 2019:
member
Briefly reviewed the rest of the commits as well for revision 365879e9bdba93a84bd2ef4a39816a5eb8fd31b0
One concern I have that I’d have to take a harder look at is that some places seem to eagerly try whatever scriptpubkeyman are loaded, and some simply load the Legacy one by name. I’ll more carefully go through the exposed RPC interface later.
achow101 force-pushed
on Sep 9, 2019
achow101 force-pushed
on Sep 9, 2019
instagibbs
commented at 1:51 pm on September 10, 2019:
member
The RPC-based changes seem correct as well. Any import/dump type functionality is locked in to legacy box, and as new boxes are created we can either activate those, or make new RPC calls. For more general info-gathering-on-keys based queries, any box will do, which is what the implementation does.
achow101 force-pushed
on Sep 10, 2019
instagibbs
commented at 4:08 pm on September 10, 2019:
member
sidhujag referenced this in commit
9282d8beb9
on Sep 10, 2019
in
src/wallet/scriptpubkeyman.h:244
in
fb0058ed68outdated
241+ //! keeps track of whether Unlock has run a thorough check before
242+ bool fDecryptionThoroughlyChecked = false;
243+
244+ //! Master encryption key for the wallet. Keys are encrypted with this.
245+ //! Wallet is unlocked if this is not null.
246+ CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore);
ryanofsky
commented at 6:21 pm on September 12, 2019:
The state variables vMasterKey, fUseCrypto, and fDecryptionThoroughlyChecked seem to end up duplicated in both the CWallet and in KeyMan classes:
Is this by design, or could KeyMan instances store references or pointers instead? It seems not ideal for different KeyMan instances to potentially be storing their own copies of encryption state and maybe having variables like fDecryptionThoroughlyChecked set inconsistently.
Also, the SetCrypted and IsLocked functions are duplicated almost but not quite verbatim across both classes:
that would allow ScriptPubKeyMan to get access to database and crypto functionality without creating a circular dependency on CWallet, and without needing the current std::function trampolines and duplication of crypto code.
The idea would be to declare this interface in scriptpubkeyman.h or a standalone header, pass it to scriptpubkeymanager instances, and have CWallet inherit and implement it.
achow101
commented at 9:08 pm on September 13, 2019:
I’ve added WalletStorage as you suggested. However I did not change vMasterKey, fUseCrypto, and fDecryptionThorouglyChecked to be references or pointers as allowing ScriptPubKeyMans to modify these variables in CWallet itself may result in inconsistent states where one ScriptPubKeyMan has finished en/decrypting but another has not. Properly changing them to use references could be done in the future, but doing it now would cause the places that use them to differ even more and thus make review harder.
It makes sense to me to have WalletStorage only cover stuff that’s global to the wallet, rather than unique to each ScriptPubKeyMan. The end result is that this commit is now manageably small.
However, at the PR level, shouldn’t CWallet’s IsCrypted() be looping over all ScriptPubKeyMans, just like SetCrypted() does?
update: IsCrypted() doesn’t loop, I was looking at CWallet::EncryptWallet
achow101
commented at 5:46 pm on September 17, 2019:
However, at the PR level, shouldn’t CWallet’s IsCrypted() be looping over all ScriptPubKeyMans, just like SetCrypted() does?
SetCrypted doesn’t loop.
The thought I had here was that it could be possible that a wallet is considered encrypted (by virtue of having an encryption key set) without any of the ScriptPubKeyMans having any of them actually encrypting anything and thus be IsCrypted() == false.
ryanofsky
commented at 3:46 pm on September 20, 2019:
The encryption stuff here is making my head spin. Wallet encryption was already confusing before, but now with CWallet and ScriptPubKeyMan both having duplicate fUseCrypto, SetCrypted(), IsCrypted(), vMasterKey, IsLocked() states and code, it’s hard to tell which combinations of state are expected, and which are bugs, and whether the code supposed to be keeping states in sync actually works.
I have a few suggestions to try clarify things. These should also simplify the code.
Drop the CWallet::fUseCrypto member and CWallet::SetCrypted() method. Change CWallet::IsCrypted() method to just loop over keyman objects and call IsCrypted on them. As far as I can tell the CWallet::fUseCrypto state is broken in the current PR. CWallet::SetCrypted() used to be called whenever any encrypted data was present, but now that now longer happens, and CWallet::IsCrypted() returns false if the wallet hasn’t been explicitly encrypted or locked since startup, regardless of what encrypted keys or data are present.
If the intent is to consider a wallet encrypted “by virtue of having an encryption key set”, it’d be better to add a new method bool HasEncryptionKey() { return !mapCryptedKeys.empty(); } instead of overloading IsCrypted(). It might also make sense to rename the existing IsCrypted() function HasEncryptedData() in that case so the meaning is not confused.
This last suggestion is less important, but I think it’d be better to lock and unlock the entire wallet, instead of parts of it. The keyman vMasterKey member and IsLocked(), Locked(), and Unlocked() implementations copied from wallet code could all be dropped. Keyman instances should have a fDecryptionThoroughlyChecked member and have CheckDecryption() methods, but otherwise would be simpler if they just called IsLocked() and GetMasterKey() from WalletStorage instead of implementing locking and unlocking themselves.
achow101
commented at 4:42 pm on September 20, 2019:
I’ll take a look at reworking the encryption state stuff.
achow101
commented at 4:10 pm on September 23, 2019:
I’ve changed the encryption stuff to be mostly in line with @ryanofsky’s suggestions.
As far as I can tell the CWallet::fUseCrypto state is broken in the current PR. CWallet::SetCrypted() used to be called whenever any encrypted data was present, but now that now longer happens, and CWallet::IsCrypted() returns false if the wallet hasn’t been explicitly encrypted or locked since startup, regardless of what encrypted keys or data are present.
FWIW it wasn’t broken. SetCrypted was changed to be called during wallet loading so fUseCrypto would be true and thus IsCrypted() returned true. Otherwise tests wouldn’t pass.
achow101 force-pushed
on Sep 13, 2019
achow101 force-pushed
on Sep 13, 2019
in
src/wallet/scriptpubkeyman.h:460
in
5f641a5c8foutdated
460+ */
461+ void MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
462+ const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; }
463+};
464+
465+/** Wraps a LegacyScriptPubKeyMan so that it can be returned in a new unique_ptr */
ryanofsky
commented at 6:29 pm on September 16, 2019:
What’s the reason for using unique_ptr and shared_ptr so many places to begin with? It doesn’t seem like we’d ever want KeyMan instances to outlive their CWallet instance, or to be shared between multiple CWallet instances. So wouldn’t it be simplest to store KeyMan instances in CWalletunique_ptr<> or set<unique_ptr<>> member variables so they get cleaned up when the wallet does, but otherwise just use plain & references or * pointers everywhere else (address types maps, accessors, and higher level code)?
achow101
commented at 8:43 pm on September 16, 2019:
The use of shared_ptr was because ScriptPubKeyMan objects were being passed around a lot. I’ve changed this to store them as a unique_ptr and instead just pass around the ScriptPubKeyMan* as you suggested.
ryanofsky
commented at 9:40 pm on September 16, 2019:
Thanks! If desired, I think you could take it further and drop LegacySigningProvider wrapper class if the CWallet::GetSigningProvider methods are returning plain pointers. Also, I think there’s no need anymore to change CWallet::database from a unique_ptr to a shared_ptr. (It’s a little nicer as a unique_ptr to be sure the database is closed when a wallet’s unloaded and not kept open with a dangling reference.)
This function has no tests, half a dozen different code paths and calls really confusing legacy code like IsMine. There’s probably less than 5 people on the planet who understand it, and even fewer who understand the ways this can break for some weird use cases. The only excuse for not documenting it is if it’s copied from a legacy function.
achow101
commented at 4:44 pm on September 18, 2019:
I’ve added some comments.
in
src/wallet/scriptpubkeyman.h:166
in
1e572c14d8outdated
I suppose we can’t burry IsMine deep inside a private method? :-)
achow101
commented at 5:52 pm on September 17, 2019:
No
in
src/wallet/scriptpubkeyman.h:197
in
1e572c14d8outdated
59+
60+ /* Returns true if the wallet can give out new addresses. This means it has keys in the keypool or can generate new keys */
61+ virtual bool CanGetAddresses(bool internal = false) { return false; }
62+
63+ /** Upgrades the wallet to the specified version */
64+ virtual bool Upgrade(int prev_version, int new_version, std::string& error) { return false; }
Why does this live in scriptpubkeyman.h rather than wallet.h?
Can you add a few comment lines to explain the purpose of WalletStorage and how it relates to CWallet and ScriptPubKeyMan?
In particular I don’t get why (in the next commit) each ScriptPubKeyMan instance needs this.
Maybe move this commit closer to where you first need it.
achow101
commented at 5:53 pm on September 17, 2019:
To avoid circular dependencies.
I am not moving this commit. It belongs right there.
It serves the exact same purpose as the commits it replaces does, to provide access to some functions that require reading and/or modifying some wallet storage things like the database handle, the wallet version number, and wallet flags. It’s there to avoid having callbacks and binding functions.
in
src/wallet/wallet.cpp:4118
in
04aba6b8afoutdated
Maybe point out that having only 1 entry in m_spk_managers here means that functions like GetSigningProvider that iterate over “all” PubKeyMans really only query once.
achow101
commented at 5:55 pm on September 17, 2019:
I don’t think that’s necessary to point out.
in
src/wallet/wallet.cpp:4020
in
15a633b8dcoutdated
Maybe clarify the meaning of “Active”, and how this is different from m_spk_managers. Presumably the latter may, in a descriptor wallet, include PubKeyMan that we don’t draw new address for.
Maybe rename commit to Introduce GetScriptPubKeyMan methods.
achow101
commented at 5:56 pm on September 17, 2019:
Active means that we actively draw addresses from them, i.e. they are in m_internal_spk_managers and m_external_spk_managers.
in
src/wallet/wallet.cpp:4147
in
65b9a1376doutdated
Shouldn’t this only loop over active ScriptPubKeyMans?
achow101
commented at 5:14 pm on September 18, 2019:
Yes. Changed
Sjors
commented at 5:02 pm on September 17, 2019:
member
Review in progress, borrowing @ryanofsky’s fancy tools (see my gist):
eb428d89d09e12b9528518f6a73daeeec4845d12 Move wallet enums to walletutil.h (1/70)
4aade7bceeb5092d6a322daec61c1af07ed9775e List output types in an array in order to be iterated over (2/70)
d8abfae4e93b3023d1d62b4dd10f109eb55f56bd Always try to sign for all pubkeys in multisig (3/70)
9899064017a2864ae0b8eb41c3c7e69822752129 Store p2sh scripts in AddAndGetDestinationForScript (4/70)
fbb6d0142ac98a315fc230d186a4a7b673f5f5cb Introduce both ScriptPubKeyMan as an interface and LegacyScriptPubKeyMan as a dummy class (5/70)
86c2a670d6e169f2b5058946818331d354ed4f3a Add LegacyScriptPubKeyMan to CWallet (6/70)
85b38c74d7642a65af3f447322c31fd77d36780b Add WalletStorage class and have CWallet inherit it (7/70)
d6b6e0bab5552916dcd166e60c5b02d31ba853e7 Add WalletStorage to ScriptPubKeyMan and passthrough some functions to it (8/70)
a8e7461254b5964fc7452e5ceabfbcd303d9fbfb Fetch the SigningProvider for a script from the wallet (9/70)
1127cf6c45c860950a02f85d964b98feb03b1abe Fetch the ScriptPubKeyMan for given output type and internal-ness, or a given script, or ScriptPubKeyMan id (10/70)
501c0830a4bd6bfe3949482170f57d6e137f931c Implement GetSigningProvider in LegacyScriptPubKeyMan (11/70)
e423df81761a83da3f55723f616f90705fe188b5 Implement function to connect ScriptPubKeyMan’s NotifyCanGetAddessesChanged and NotifyWatchOnlyChanged to CWallet’s (12/70)
7af540932d3e405c3886a382d66bda0cac443585 Implement IsLocked and IsCrypted in LegacyScriptPubKeyMan (13/70)
07b616b4b04dc29936cdf5fb87c5b3201bc01445 Implement LoadCryptedKey and AddCryptedKey in LegacyScriptPubKeyMan (14/70)
1c21d4e2ce2c698b9ca06c7c428d7300d33184ab Implement UpdateTimeFirstKey, and GetTimeFirstKey in LegacyScriptPubKeyMan (15/70)
17d32d65c125078063cccf58b72cb6041e6b6828 Implement AddWatchOnly, RemoveWatchOnly, HaveWatchOnly, and LoadWatchOnly in LegacyScriptPubKeyMan (16/70)
7b83f49531be8e22c45b107bcdad879ecd9a834d Implement AddKeyPubKey and LoadKey in LegacyScriptPubKeyMan (17/70)
276adc1ebc3d185d14cfc12abc4f057aaa4f164d Implement WalletLogPrintf in LegacyScriptPubKeyMan (18/70)
6ecc20b2d98f02ad4218f3924c7de88f375fa983 Implement SetHDCHain, and IsHDEnabled in LegacyScriptPubKeyMan (19/70)
8ccc29a594b34c0b6447e58b0732ee9f0840df4c Implement LoadCScript in LegacyScriptPubKeyMan (20/70)
540e60b06265fa3ea52181256ee729ce7a50c3fe Implement LoadKeyMetadata and LoadScriptMetadata in LegacyScriptPubKeyMan (21/70)
251201acb529fc5de68efa13d01b6e5882d2776f Implement GetKey, HaveKey, and GetPubKey in LegacyScriptPubKeyMan (22/70)
2760f2160c2ea437a8c4c67dbb435577cd5e956a Implement GenerateNewKey in LegacyScriptPubKeyMan (23/70)
ce1ed8df731f5770f34a8579a05fa7c5a8effb72 Implement LoadKeyPool in LegacyScriptPubKeyMan (24/70)
ab15835a6bc41946e9821ae68b465fab6bec4a00 Implement GetOldestKeyPoolTime, KeypoolCountExternalKeys, and GetKeypoolSize in LegacyScriptPubKeyMan (25/70)
add7cbd861d29f492d2796325c51ddc8f6e34d5f Implement CanGetAddresses, CanGenerateKeys, and HavePrivateKeys in LegacyScriptPubKeyMan (26/70)
dda4bffd91d569a1a0ddac46d93a99db819e5034 Implement GenerateNewSeed, DeriveNewSeed, and SetHDSeed for LegacyScriptPubKeyMan (27/70)
b8af54b943ff52b259571fd458ed63d20b5a2e95 Implement TopUpKeypool, TopUp, and NewKeyPool in LegacyScriptPubKeyMan (28/70)
8611d0122a8747ba4570afa3113b4b5cca92d721 Implement ReturnAddress, and KeepKey in LegacyScriptPubKeyMan (29/70)
ae9354b7694056cfecc8517e8657f811bf0c7842 Implement GetNewAddress, and GetReservedAddress in LegacyScriptPubKeyMan (30/70)
2ccffda892e4e3c8d7642b6658e5ace84357714b Implement MarkUnusedAddresses in LegacyScriptPubKeyMan (31/70)
5b6f5b926457e2423d6a4fcf0b52e0b454a20676 Implement IsMine in LegacyScriptPubKeyMan (32/70)
205502215e1085b7f94f36d4255b9ee833f0f55a Implement UpgradeKeyMetaData, SetupGeneration, IsFirstRun, Upgrade, RewriteDB in LegacyScriptPubKeyMan (33/70)
84470984430c34f0ad51dc3dc56e4c0d27367bf5 Implement Unlock, Lock, and Encrypt and LegacyScriptPubKeyMan (34/70)
5c03c7e5cba3e7abe00a0eca8889a5b14e9c67b6 Implement ImporScripts, ImportPrivKeys, ImportPubKeys, and ImportScriptPubKeys in LegacyScriptPubKeyMan (35/70)
3099b7c2dbfd31f4a1747f7e38629e44988d6ef7 Implement GetMetadata in LegacyScriptPubKeyMan (36/70)
b1efc0af2845bb57714951613dcdfc70ccf63b47 Implement GetKeyOrigin in LegacyScriptPubKeyMan (37/70)
f3739076feb69c99a6d93d6218e3b628d2e4cc57 Implement actually loading everything into LegacyScriptPubKeyMan (38/70)
c2f6d4b40b8c508aa14f27f5e860cdc271a0b6b4 Implement CanProvide in LegacyScriptPubKeyMan (39/70)
842f501ead54f66143d85c5b44d9defb0f1e22d4 [ci skip] Remove CWallet from IsMine and have CWallet always use ScriptPubKeyMan’s IsMine (40/70)
8a2d51c741b896842e093fa35590ef36abb907c4 [ci skip] moveonly: move ismine stuff to be a module of LegacyScriptPubKeyMan (41/70)
2d4c3de17c140cfc84df45fb9cb5d86f184b004a [ci skip] Have GetNewAddress, GetNewChangeAddress, and ReserveAddress use ScriptPubKeyMan (42/70)
73f7f54b1aad82c26240cffaa1ae7afb7cc602ee [ci skip] Mark used addresses in ScriptPubKeyMan (43/70)
99a802afd08fe4535b4a4bc65ff644186f5a0131 [ci skip] Call UpgradeKeyMetaData for each ScriptPubKeyMan (44/70)
8398e91488779baf282d7d692630f3348cb0b3fa [ci skip] Sign using SigningProvider from ScriptPubKeyMan when signing within CWallet (45/70)
d8dd23fdaed2170ed41c8a702620777115483ae7 [ci skip] Do not allow import*, dump*, and addmultisigaddress RPCs when wallet is not backed by LegacyScriptPubKeyMan (46/70)
5bd936494ed2b6afe5e0cc332ae4e74f0066b061 [ci skip] Change Imports to use LegacyScriptPubKeyMan Imports (47/70)
47e43141262c07a39522361628dab30c7d7625bd [ci skip] Use SigningProviders and ScriptPubKeyMans in listunspent, signmessage, signrawtransactionwithwallet, and getaddressinfo (48/70)
46747500672cf985fd015cd8609e7b39aa175306 [ci skip] Use LegacyScriptPubKeyMan in addmultisigaddress and sethdseed (49/70)
4837bc6fcf8e165dd3ee606f46aa5316e007556c [ci skip] Use LegacyScriptPubKeyMan for hdseedid in getwalletinfo (50/70)
12d51a85b65f1f080cc483b273b43dc7596b9a53 [ci skip] Change KeypoolCountExternal and GetKeypoolSize to get aggregate sizes from ScriptPubKeyMans (51/70)
657e8f7df530ac9c5b70e337e4c2bf2ec062c7fc [ci skip] Have IsHDEnabled fetch from ScriptPubKeyMans (52/70)
d3dde67e4aba051bea38daae60ed80fd4db69d3b [ci skip] Fetch oldest keypool time from ScriptPubKeyMans (53/70)
f3b7ab7a6f443acac0bfc253a380a14f8f17646e [ci skip] have TopUpKeyPool call TopUp in each ScriptPubKeyMan (54/70)
2492a85e988f1ac07655754af2f99ea2b45d9bc3 [ci skip] Have EncryptWallet, Lock, and Unlock call their respective functions in ScriptPubKeyMans (55/70)
0dc9eaffa4a9a306d0b97432ad108be39e4c3184 [ci skip] Use LegacyScriptPubKeyMan throughout psbt_wallet_tests (56/70)
0eab7717e4969a25560e776ee06593710ee0395d [ci skip] Use LegacyScriptPubKeyMan throughout wallettool (57/70)
d027cbb056c1bb2057a0be4a8356f85910768838 [ci skip] Use ScriptPubKeyMans’ Setup and Upgrade functions when loading or creating a wallet (58/70)
ef95b21d408b3b279a8a7c3d5f866f1bccf86c63 [ci skip] Define first run as having no ScriptPubKeyMans (59/70)
dcee75f6dc2a26697a56d5741890acace3f81f06 [ci skip] Use RewriteDB action when DB needs rewrite (60/70)
d1c5d16d0b338e8cdb16ec2d14e23a3c678ece6f [ci skip] Use GetTimeFirstKey instead of nTimeFirstKey (61/70)
9822895bc7cb870c356a77bc6b3763cd37409bee [ci skip] Use LegacyScriptPubKeyMan for in wallet_tests (62/70)
cb56b322890273629de253b12ac4083f046112b3 [ci skip] Use LegacyScriptPubKeyMan in dumpprivkey and dumpwallet (63/70)
f505d6c36e153c0560362aa297cd85bcef62c4de [ci skip] Change CanGetAddresses to fetch from ScriptPubKeyMan (64/70)
6f51d198952a4ae8146fd0947110ba03c1a711ae [ci skip] Fetch the correct SigningProvider for signing PSBTs (65/70)
e2575a1f46d98261c85687956875e0c2a7ef0f31 [ci skip] Use LegacyScriptPubKeyMan in test util (66/70)
fcb35328be8ac5183a216229c8e594e51aadea07 [ci skip] Use LegacyScriptPubKeyMan in some parts of getbalances and createwallet (67/70)
1573967b4aeca99357195546f2903437fa393849 [ci skip] Have getPubKey and getPrivKey use SigningProvider (68/70)
0cca3802d911d5526f96ac7325030fe39e70e3e6 [ci skip] Use LegacyScriptPubKeyMan in benchmarks involving the wallet (69/70)
1c01da74d33033b0aef4a30dc305a03594ff7d33 Remove unused functions and switch CWallet to use ScriptPubKeyMan (70/70)
Nit: maybe split scriptpubkeyman_legacy.h out from scriptpubkeyman.h.
in
src/wallet/wallet.h:146
in
059824a35foutdated
141@@ -283,10 +142,10 @@ class ReserveDestination
142 protected:
143 //! The wallet to reserve from
144 CWallet* pwallet;
145+ //! The ScriptPubKeyMan to reserve from. Based on type when GetReservedDestination is called
146+ ScriptPubKeyMan* spk_man;
ryanofsky
commented at 5:20 pm on September 17, 2019:
Would be good to prefix with m_
achow101
commented at 6:30 pm on September 17, 2019:
Done
achow101 force-pushed
on Sep 17, 2019
in
src/wallet/scriptpubkeyman.cpp:967
in
0d701bd42doutdated
552@@ -553,3 +553,24 @@ void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata&
553 if (!batch.WriteHDChain(hdChain))
554 throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
555 }
556+
ryanofsky
commented at 6:17 pm on September 17, 2019:
In commit “Implement LoadKeyPool in LegacyScriptPubKeyMan” (0d701bd42d0d9db9da858a4e6ae346ae0b1dda90)
Empty src/qt/stqG2SM file in this commit should be dropped
achow101
commented at 6:30 pm on September 17, 2019:
How’d that get there? Removed it.
achow101 force-pushed
on Sep 17, 2019
achow101 force-pushed
on Sep 17, 2019
achow101 force-pushed
on Sep 17, 2019
achow101 force-pushed
on Sep 17, 2019
achow101 force-pushed
on Sep 18, 2019
in
src/wallet/walletdb.cpp:519
in
95aca1f9beoutdated
516@@ -514,10 +517,6 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
517 pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n",
518 wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records);
519520- // nTimeFirstKey is only reliable if all keys have metadata
521- if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta)
522- pwallet->UpdateTimeFirstKey(1);
ryanofsky
commented at 4:40 pm on September 18, 2019:
Did this call move somewhere? Or is it not important and just dropped?
achow101
commented at 4:52 pm on September 18, 2019:
No.. but we should probably keep it, so I’ve added it back in.
achow101 force-pushed
on Sep 18, 2019
achow101 force-pushed
on Sep 18, 2019
achow101 force-pushed
on Sep 18, 2019
achow101 force-pushed
on Sep 18, 2019
in
src/wallet/wallet.cpp:2919
in
df689d5973outdated
2930- LOCK(cs_KeyStore);
2931- // This wallet is in its first run if all of these are empty
2932- fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty()
2933- && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET);
2934+ // This wallet is in its first run if all of these are empty and this isn't blank or no privkeys
2935+ fFirstRunRet = m_spk_managers.empty() && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET);
ryanofsky
commented at 5:56 pm on September 18, 2019:
I wasn’t sure if the new m_spk_managers.empty() condition here is equivalent to the previous mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty() condition. I think it is equivalent because the LoadWallet(*this) call above will only call GetLegacyScriptPubKeyMan() internally if keys are found, and if it does not, SetupLegacyScriptPubKeyMan() will not have been called yet and m_spk_managers will be empty. But correct me if I’m wrong.
I guess it’s a little confusing that GetLegacyScriptPubKeyMan right now will actually create a legacy keyman if one doesn’t exist, and never return null, even though there are checks for it returning null throughout the code. Maybe there should be two different methods for clarity:
Just a suggestion, though, and correct me if I missed anything.
achow101
commented at 8:43 pm on September 19, 2019:
Yes, that is correct. That’s pretty much the only reason GetLegacyScriptPubKeyMan also creates a LegacyScriptPubKeyMan.
The checks for null are for the future when we have more ScriptPubKeyMan classes.
achow101
commented at 11:22 pm on September 19, 2019:
I’ve added GetOrCreateLegacyScriptPubKeyMan
in
src/wallet/wallet.cpp:4791
in
df689d5973outdated
4052+ for (const auto& spk_man_pair : m_spk_managers) {
4053+ if (!spk_man_pair.second->Unlock(vMasterKeyIn, accept_no_keys)) {
4054+ return false;
4055 }
4056- keyPass = true;
4057- if (fDecryptionThoroughlyChecked)
ryanofsky
commented at 6:11 pm on September 19, 2019:
CWallet::fDecryptionThoroughlyChecked variable isn’t actually used anymore, only assigned to. It should probably either be used or removed. Maybe it’s not necessary anymore since keyman has the same variable.
achow101
commented at 11:22 pm on September 19, 2019:
Removed it
in
src/wallet/wallet.cpp:573
in
df689d5973outdated
570@@ -919,10 +571,10 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
571572 // if we are using HD, replace the HD seed with a new one
573 if (IsHDEnabled()) {
ryanofsky
commented at 7:08 pm on September 19, 2019:
It’d make more sense to call spk_man_pair.second->IsHDEnabled on the individual keyman object than IsHDEnabled on the wallet object, which is the intersection of all IsHDEnabled statuses.
achow101
commented at 11:22 pm on September 19, 2019:
Done
achow101 force-pushed
on Sep 19, 2019
in
src/wallet/scriptpubkeyman.cpp:128
in
8a2d51c741outdated
122+ if (keystore.HaveKey(keyID)) {
123+ ret = std::max(ret, IsMineResult::SPENDABLE);
124+ }
125+ break;
126+ }
127+ case TX_SCRIPTHASH:
In 8a2d51c741b896842e093fa35590ef36abb907c4: case TX_PUBKEYHASH contains a bug fix. Maybe move that to a seperate commit so this commit is actually move-only.
achow101
commented at 5:30 pm on September 23, 2019:
Moved into its own commit
in
src/wallet/scriptpubkeyman.cpp:528
in
3099b7c2dboutdated
302@@ -303,6 +303,16 @@ bool LegacyScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sig
303304 const CKeyMetadata* LegacyScriptPubKeyMan::GetMetadata(uint160 id) const
305 {
306+ LOCK(cs_KeyStore);
307+ auto it = mapKeyMetadata.find(CKeyID(id));
Sjors
commented at 10:17 am on September 21, 2019:
In d3dde67e4aba051bea38daae60ed80fd4db69d3b: GetOldestKeyPoolTime was introduced in #10235. It calls GetOldestKeyTimeInPool for the internal and external keypools. IIUC this gets the timestamp from keypool entry at index 0. For legacy wallets those are the same, so std::max is fine. But if we add descriptor wallets, and allow importing them after wallet creation, then std::max would return the newest one. So maybe we should switch to std::min here and initialize oldestKey with Time()?
Alternatively we could drop keypoololdest in the RPC wallet for non-legacy spk_managers, and show it for each individual descriptor instead of the whole wallet.
achow101
commented at 5:30 pm on September 23, 2019:
I’ve changed it to use min. Looking at the original implementation in master, it actually looks like it’s broken as it uses max.
Sjors approved
Sjors
commented at 10:47 am on September 21, 2019:
member
Comparing function bodies side by side was less painful than I feared.
ACK1c01da74d33033b0aef4a30dc305a03594ff7d33, modulo two parts I did not review yet:
wallet encryption stuff, still in flux, see discussion here
I also ran the (very minimal) backwards compatibility & upgrade sanity checks in #12134.
When all reviewers are satisfied, I suggest merging as early possible after 0.19 branch-off.
achow101 force-pushed
on Sep 23, 2019
achow101 force-pushed
on Sep 23, 2019
achow101 force-pushed
on Sep 23, 2019
in
src/wallet/wallet.h:738
in
3b05946857outdated
739@@ -942,8 +740,8 @@ class CWallet final : public FillableSigningProvider, private interfaces::Chain:
740 encrypted_batch = nullptr;
ryanofsky
commented at 7:51 pm on September 24, 2019:
I don’t think there’s any reason for CWallet::encrypted_batch to exist as a member variable anymore. It could just be a local in CWallet::EncryptWallet
achow101
commented at 8:08 pm on September 24, 2019:
Done
in
src/wallet/walletdb.cpp:521
in
3b05946857outdated
516@@ -515,8 +517,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
517 wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records);
518519 // nTimeFirstKey is only reliable if all keys have metadata
520- if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta)
521- pwallet->UpdateTimeFirstKey(1);
522+ if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) {
523+ auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan();
ryanofsky
commented at 8:02 pm on September 24, 2019:
Seems like this should use GetLegacyScriptPubKeyMan not GetOrCreateLegacyScriptPubKeyMan to make more sense and be in line with the previous code. Or if GetOrCreateLegacyScriptPubKeyMan is actually better here, probably should drop if (spk_man) condition which would never be false
achow101
commented at 8:08 pm on September 24, 2019:
Changed to GetLegacyScriptPubKeyMan.
achow101 force-pushed
on Sep 24, 2019
achow101 force-pushed
on Sep 24, 2019
in
src/wallet/wallet.h:146
in
47b342db0doutdated
141@@ -283,10 +142,10 @@ class ReserveDestination
142 protected:
143 //! The wallet to reserve from
144 CWallet* pwallet;
145+ //! The ScriptPubKeyMan to reserve from. Based on type when GetReservedDestination is called
146+ ScriptPubKeyMan* m_spk_man;
ryanofsky
commented at 4:59 pm on September 25, 2019: