Skip to content

Make ScriptPubKeyMan an actual interface and the wallet to have multiple#17261

Merged
meshcollider merged 13 commits intobitcoin:masterfrom
achow101:wallet-box-pr-2
Jan 30, 2020
Merged

Make ScriptPubKeyMan an actual interface and the wallet to have multiple#17261
meshcollider merged 13 commits intobitcoin:masterfrom
achow101:wallet-box-pr-2

Conversation

@achow101
Copy link
Member

@achow101 achow101 commented Oct 26, 2019

Continuation of wallet boxes project.

Actually makes ScriptPubKeyMan an interface which LegacyScriptPubkeyMan. Moves around functions and things from CWallet into LegacyScriptPubKeyMan so that they are actually separate things without circular dependencies.


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.

This PR is the last step in the Wallet Structure Changes.

@achow101
Copy link
Member Author

The final diff is very similar to #16341 just with some things dropped as they were deemed unnecessary.

@Sjors
Copy link
Member

Sjors commented Oct 26, 2019

For comparison, my last ACK on the original was for c37be15. That code was identical to @ryanofsky's branch at 299296e, just using a different set of commits to get there. This PR rebases that on master @25d7e2e78137d07eb612c44d19b0d496050c947a, with the last two commits (952ba99 & 299296e) dropped. The result is f47ffe0a88111a63e1d7d98c25fbe2184215dbbe here (minus two linter changes).

@DrahtBot
Copy link
Contributor

DrahtBot commented Oct 26, 2019

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #17954 (wallet: Remove calls to Chain::Lock methods by ryanofsky)
  • #17878 (wip: zmq: Support -zmqpubwallettx by promag)
  • #17786 (refactor: Nuke policy/fees->mempool circular dependencies by hebasto)
  • #17681 (wallet: Keep inactive seeds after sethdseed and derive keys from them as needed by achow101)
  • #17484 (wallet: add cached m_is_ibd to remove Chain::isInitialBlockDownload by ariard)
  • #17443 (Drop checkFinalTx and use Median Time Past to check finality of wallet transactions by ariard)
  • #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)
  • #16698 (Mempool: rework rebroadcast logic to improve privacy by amitiuttarwar)
  • #16528 ([WIP] Native Descriptor Wallets (take 2) by achow101)
  • #16440 (BIP-322: Generic signed message format by kallewoof)
  • #16224 (gui: Bilingual GUI error messages by hebasto)
  • #15761 (Replace -upgradewallet startup option with upgradewallet RPC by achow101)
  • #15719 (Drop Chain::requestMempoolTransactions method by ryanofsky)
  • #10443 (Add fee_est tool for debugging fee estimation code by ryanofsky)

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.

ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Oct 29, 2019
Suggested by MarcoFalke <[email protected]>
bitcoin#17260 (comment)

The same check was also added in the followup commit "Refactor: Move
SetupGeneration code out of CWallet" 9727a4d
from bitcoin#17261, but it's safer to start checking now without waiting for that
followup.
@ryanofsky
Copy link
Contributor

ryanofsky commented Oct 29, 2019

Suggestion: there's a cluster of fairly trivial refactoring commits in this PR that I think could be pulled out into a separate PR and merged pretty quickly. Doing this would reduce the number of commits here by almost half:

  • b318219c51e316cfb75639f0820cc1b8017bfdfd MOVEONLY: Reorder LegacyScriptPubKeyMan methods (4/36)
  • 8c7eac4de3e94adcf3ec8a0358c410a198b8b844 Refactor: Declare LegacyScriptPubKeyMan methods as virtual (5/36)
  • f0339ba0b3a9d54f04b0e5f0ec80a9f791567caa Refactor: Add new ScriptPubKeyMan virtual methods (6/36)
  • 5321cf7eb565834a7e69331d690543375890d516 Refactor: Move SetAddressBook call out of LegacyScriptPubKeyMan::GetNewDestination (7/36)
  • 44f524e6c2116a4c57e9e47c4b130efe751cf5a7 Refactor: Move SetWalletFlag out of LegacyScriptPubKeyMan::UpgradeKeyMetadata (8/36)
  • 2b92e87104868e11be9a0e2129d1340677ebc81c Refactor: Remove UnsetWalletFlag call from LegacyScriptPubKeyMan::SetHDSeed (9/36)
  • 802dfb95abf9465efe34d16ece4f7e6dc8349177 Refactor: Move SetAddressBookWithDB call out of LegacyScriptPubKeyMan::ImportScriptPubKeys (10/36)
  • d8d2d31e55a53bfeb3860f278e47692970bdcd84 Refactor: Move LoadKey LegacyScriptPubKeyMan method definition (11/36)
  • 9f20667b5bb4f7939efa2cf16e3f12abf34ebbe5 Refactor: Move GetMetadata code out of getaddressinfo (12/36)
  • 309ee44cc663c8d753729d94100bbbe694c2e091 Refactor: Move MarkUnusedAddresses code out of CWallet::AddToWalletIfInvolvingMe (13/36)
  • 1ffce090c62ca340e09ace89d859843b995b0ee9 Refactor: Move Upgrade code out of CWallet::CreateWalletFromFile (14/36)
  • f09be4ff1cc00390fc50147c63260983a2d98d71 Refactor: Move HavePrivateKeys code out of CWallet::CreateWalletFromFile (15/36)
  • 25d904ce890f2989cbd3f677972be59d1381e98c Refactor: Move SetupGeneration code out of CWallet (16/36)
  • a207dbc464a647342c55cfd3c81e9866c063cdce Refactor: Move RewriteDB code out of CWallet (17/36)
  • 1cca908e4d1f855e961eb513ff87deda6a615323 Refactor: Move GetKeypoolSize code out of CWallet (18/36)
  • 23008f4c36d3bd982dcdb70698b6dc05b673c4e0 Refactor: Move nTimeFirstKey accesses out of CWallet (19/36)

@achow101, would you want to pull out the commits above into a separate PR? I'd also be happy to make the PR if it would be more convenient.

@achow101
Copy link
Member Author

@ryanofsky Done as #17304

@maflcko
Copy link
Member

maflcko commented Oct 29, 2019

fanquake added this to Blockers in High-priority for review 5 hours ago

Why? I thought that high-prio could only be used once per week per contributor.

@fanquake
Copy link
Member

fanquake commented Oct 29, 2019 via email

@maflcko
Copy link
Member

maflcko commented Oct 30, 2019

#17260 (comment) was in for this week, but fair enough

@Sjors
Copy link
Member

Sjors commented Jan 19, 2020

re-utACK 126d6945e38cdc47b1227cdacfaa456714efcdf5

src/outputtype.h Outdated
Copy link
Member

@laanwj laanwj Jan 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't defining the value of non-primitive constants in the header file potentially cause linker issues in C++ (or duplication, as this literal data will be included in multiple files)?. I think it's better to only declare the variable here then initialize the value in the cpp.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this would cause any issues since it's const. I also haven't seen any linker warnings or errors about this. I think there would only be some duplication, but isn't that the same for any constant global variable that is defined in a header?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are fine trusting the word of Microsoft,

In C, constant values default to external linkage, so they can appear only in source files. In C++, constant values default to internal linkage, which allows them to appear in header files.

Another source said C++ does not normally create storage for consts, which I assume means they work like #defines, in which case there shouldn't be wasted duplicate data entries either.

Copy link
Member

@sipa sipa Jan 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you replace the {...} initializer with a function call, and then have the constant in multiple translation units, the function will get called once for each.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the initialization to the cpp file

promag and others added 13 commits January 23, 2020 16:34
This commit only affects locking behavior and doesn't have other changes.
In CWallet::LoadWallet, use this to detect and empty wallet with no keys

This commit does not change behavior.
This avoids repeaded upgrades when support for more multiple keyman references
is added in the next commit:
bitcoin#16341 (comment)
Instead of having a uint256 representations of one scattered throughout
where it is used, define it globally in uint256.h
Add wallet logic for dealing with multiple ScriptPubKeyMan instances. This
doesn't change current behavior because there is still only a single
LegacyScriptPubKeyMan. But in the future the new logic will be used to support
descriptor wallets.
This commit does not change behavior.
Needed for future ScriptPubKeyMans which may need to create
SigningProviders dynamically and thus a normal pointer is not enough

This commit does not change behavior.
@instagibbs
Copy link
Member

re-utACK 3f37365

@Sjors
Copy link
Member

Sjors commented Jan 24, 2020

re-utACK 3f37365 (it still compiles on macOS after #17261 (comment))

@meshcollider
Copy link
Contributor

Tested re-ACK 3f37365

I think this is ready to go in

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post-merge code review ACK 3f37365. I did post two questions in review comments below that I think would be good to resolve.

Changes since last review (72df6f1):

  • Dropped commit daf6d548de3efdfdb974bb52948f6086fb0e2ecc "Set state to WATCH_ONLY if we can get the pubkey"
  • Added EnsureLegacyScriptPubKeyMan create argument to make RPCs like sethdseed work on empty wallets
  • coinselector_tests setup fix
  • OUTPUT_TYPES definition move
  • AddAndGetDestinationForScript formatting tweaks
  • Split commit 01b4511 "Make UpgradeKeyMetadata work only on LegacyScriptPubKeyMan" out of commit 264917ff4343b678007d89373aba6e99d521001c "Add multiple keyman maps and loops"
  • LegacyScriptPubKeyMan::Upgrade hd split version check no longer added
  • Replacement hd seed generation skipped in CWallet::EncryptWallet for nonlegacy wallets
  • CWallet::GetAllScriptPubKeyMans method added and used by CreateWalletFromFile to not skip inactive keyman instances when figuring out first key time
  • CWallet::GetScriptPubKeyMan internal true/false code deduped
  • CWallet::SetupLegacyScriptPubKeyMan asserts removed
  • LegacySigningProvider m_spk_man member rename and comment tweak

}
// Upgrade to HD chain split if necessary
if (m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) {
if (m_storage.CanSupportFeature(FEATURE_HD_SPLIT) && CHDChain::VERSION_HD_CHAIN_SPLIT) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "HD Split: Avoid redundant upgrades" (415afcc):

This seems buggy and the change looks like it has no effect. Is this supposed to say && hdChain.nVersion < CHDChain::VERSION_HD_CHAIN_SPLIT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: #17261 (comment)

No, descriptor wallets have a different way of doing it.

In commit "Box the wallet: Add multiple keyman maps and loops" (c729afd):

It seems bad to do nothing at all and keep using unencrypted seeds for non-legacy wallets, and the comment here doesn't here doesn't explain any legacy/nonlegacy distinction. It would seem safer to trigger some kind of error for non-legacy wallets if planned behavior hasn't been implemented yet, than to just do nothing.

If there is supposed to be legacy / non-legacy distinction, it would seem better to move legacy code out of CWallet into LegacyScriptPubKeyMan in a PostEncrypt or similar ScriptPubKeyMan virtual method

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.