Skip to content

Latest commit

 

History

History
47 lines (35 loc) · 5.55 KB

2022-03-09-#19602.md

File metadata and controls

47 lines (35 loc) · 5.55 KB

A PR by Andrew Chow that implements an RPC which migrates Legacy wallets to Descriptor wallets as step in the roadmap for the eventual removal of the Legacy wallet and the Berkeley DB dependency. Participants discussed what goes into the migration process and differences between Legacy and Descriptor wallets.

Note: This one also includes some of my own notes and other contributors comments from the PR that I believe give a further inside into the changes.

Questions

In your own words, (briefly) describe the migration process. 🔗

  1. First creates a backup of the wallet, in case of incorrect migration.
  2. Moves all records from the BDB database to a new SQLite database for storage. At this step we end up with a legacy-sqlite format.
    • SQLiteDatabase (introduced with #19077) is used strictly as a key-value store (a table main with two columns, key and value both with the type blob) to keep compatibility with BDB.
  3. Creates a set of all the scriptPubKeys to step-by-step remove everything from it as the migration process creates each wallet.
  4. Creates the spendable wallet (the main descriptor wallet with the same name as the one we are migrating from) by creating:
    • a combo descriptor for each non-HD key. It includes birthdate and origin, if exists.
      • We recognize those because (in contrast to HD derived keys) they don't have the hd_seed_id in their metadata.
    • a combo descriptor for each one of our HD chains.
  5. If necessary, creates the <name>_solvables wallet.
    • solvable: anything we have any (public) keys for and we know how to spend (e.g. a multisig for which we have one public key, and we know the other two because we have the full redeemscript). It basically backups the redeemscript.
    • A solvable address is not automatically watch-only, but all import* methods except importmulti ensure it is.
      • importmulti adds a redeemscript so it's solvable, but it's not marked watch-only. The net-effect of that is that we see the full script with getaddressinfothe wallet ignores transactions to it, but we can sign them. This may be useful if you're a co-signer and you don't want those transactions to show up. src
    • After the migration, the solvables wallet can't sign things the legacy wallet could sign.
    • The solvables wallet can update a PSBT with the UTXO, scripts, and pubkeys, for inputs that spend any of the scripts it contains. Because the user had not watched those scripts when the wallet is a legacy wallet, it does not make sense to have them be in the watchonly wallet. (src)
  6. If necessary, creates the <name>_watchonly wallet.
    • watch-only: may or may not be solvable, e.g. an unsolvable multisig is one for which we only know the address (scriptPubKey), not the full script, i.e. we don't know which keys it needs.
  7. Loads the new wallet and deletes all the legacy stuff.

Why is LegacyScriptPubKeyMan::GetScriptPubKeys() needed?

  • At the end of the migration process every scriptPubKey from the Legacy wallet must be associated with a descriptor. LegacyScriptPubKeyMan::GetScriptPubKeys() creates a set of all the scriptPubKeys of the Legacy wallet in order to ensure that the resulting Descriptor Wallets can compute all the original scripts.
  • This set is calculated from:
    • mapKeys and mapCryptedKeys by calculating their respective P2PK & P2PKH scriptPubKeys
    • mapScripts which are ISMINE_SPENDABLE
    • multisigs for which we own ALL the keys involved
    • setWatchOnly which contains all the watch-only scripts

Why isn’t the HD seed ignored when looking at all of the keys in a Legacy wallet? 🔗

  • We don't ignore the seeds themselves because they are considered IsMine therefore it is a valid key that could receive Bitcoin to it even though it's corresponding addresses would never be given out.
  • This is because the seed is also a CKey (as the rest of the keys) but with metadata.hdKeypath = "s" to be identified as seed. This seed is hashed according to the BIP 32 specification to become the BIP 32 master key which everything else is then derived from.
bonus: Descriptor wallets get rid of the concept of mixed watchonly and spendable. Why were watchonly and owned mixed together before? 🔗
  • Legacy wallets allow for watch-only and owned to mix together because it used to not be possible to have different wallet files for different purposes, multiwallet is relatively recent.
  • This change simplifies IsMine and it's also a separation of funds/duties/etc.