17:00:24 <ryanofsky> #startmeeting 
17:00:24 <corebot> ryanofsky: Meeting started at 2025-08-06T17:00+0000
17:00:25 <corebot> ryanofsky: Current chairs: ryanofsky
17:00:26 <corebot> ryanofsky: Useful commands: #action #info #idea #link #topic #motion #vote #close #endmeeting
17:00:27 <corebot> ryanofsky: See also: https://hcoop-meetbot.readthedocs.io/en/stable/
17:00:28 <corebot> ryanofsky: Participants should now identify themselves with '#here' or with an alias like '#here FirstLast'
17:00:35 <ryanofsky> hi
17:00:37 <ryanofsky> Welcome to the bitcoin review club! Today we'll discuss https://bitcoincore.reviews/32489 #32489
17:00:39 <corebot> https://github.com/bitcoin/bitcoin/issues/32489 | wallet: Add `exportwatchonlywallet` RPC to export a watchonly version of a wallet by achow101 · Pull Request #32489 · bitcoin/bitcoin · GitHub
17:00:40 <glozow> hi
17:00:42 <stringintech> Hi
17:00:42 <stickies-v> hi
17:01:06 <pablomartin4btc> hello
17:01:11 <colinc> hi
17:01:17 <kevkevin> hi
17:01:46 <ryanofsky> First question to get started 1. Did you test, concept ACK, approach ACK, or NACK the PR? What aspects did you focus on during review?
17:01:58 <emzy> hi
17:02:26 <monlovesmango> heyy
17:02:41 <stringintech> I was not familiar with this part of the codebase so mainly spent time on getting a basic understanding of the components touched by this PR and then did a light review on the changes. Concept ACK.
17:02:58 <monlovesmango> concept ACK but mostly here just to lurk and learn
17:02:59 <pablomartin4btc> light code review ACK
17:03:28 <effexzi> Hi every1
17:03:29 <ryanofsky> Yeah I feel like the harder part of this PR if you aren't familiar with wallet is probably the concepts
17:03:42 <monlovesmango> yes definitely haha
17:04:04 <ryanofsky> The code is more straightfoward than other prs, because it's new and not interacting with legacy stuff like a lot of wallet prs
17:04:51 <ryanofsky> Feel free to ask if a particular concept is unclear
17:05:08 <ryanofsky> Next up is 2. Why can’t the existing IsRange()/IsSingleType() information tell us whether a descriptor can be expanded on the watch-only side? Explain the logic behind CanSelfExpand() for a) a hardened wpkh(xpub/0h/*) path and b) a pkh(pubkey) descriptor.
17:06:49 <monlovesmango> is it possible that a range can be hardened but also could not be hardened?
17:06:52 <stringintech> About the first part of the question, CanSelfExpand() tells whether we need private keys or cache to get new addresses for a descriptor. A single type range descriptor may contain hardened paths or may not. The latter can self expand but not the former. So only those two methods are not enough.
17:07:34 <stringintech> About the second part
17:07:52 <stickies-v> I think range descriptors with hardened paths can still self expand, but only if they contain the xpriv?
17:08:20 <stringintech> pkh(pubkey) cannot expand because it is not ranged but the other one (which shows the limitation of IsRange()/IsSingleType() checks) cannot expand because it contains hardened paths.
17:08:59 <ryanofsky> stringintech, yeah it's just in the exported wallet there are no private keys, so anything hardened needs to be exported in the cache
17:09:08 <ryanofsky> oops that was in reply to stickies-v
17:09:49 <stringintech> :D
17:09:54 <ryanofsky> stringintech, your answer sounds right
17:10:41 <stickies-v> oh yeah, watchonly exports wouldn't contain any private keys, i was making a more general comment
17:11:17 <ryanofsky> Basically in order to do the export the wallet needs to know if the descriptor uses any hardened paths, so CanSelfExport searched the descriptor recursively for anything hardened and returns true if it finds that
17:11:44 <ryanofsky> If there are any hardened paths caches need to be exported
17:12:23 <monlovesmango> I did have a code question, where is CanSelfExpand defined for OriginPubkeyProvider? since it is just returning m_provider->CanSelfExpand()?
17:13:50 <ryanofsky> I belive OriginPubkeyProvider just wraps another pubkey provider and adds origin information
17:13:59 <stickies-v> I don't really understand what the relevant of the `IsSingleType()` part to the question is,  when is that relevant?
17:14:00 <monlovesmango> wait doesn't it return false if it finds hardened paths?
17:14:11 <ryanofsky> So since it doesn't need to derive anything it can call the wrapped pubkey provider
17:15:03 <ryanofsky> IsSingleType() is really not relevant. The question was just asking why existing methods like IsSingleType were not sufficient and a new CanSelfExpand method needed to be added
17:15:25 <stickies-v> okay, thx
17:15:55 <monlovesmango> so does pkh(pubkey) not use ConstPubkeyProvider? bc I had assumed it did but stringintech said pkh(pubkey) would not be able to expand
17:16:39 <ryanofsky> pkh(pubkey) should self-expand, right?
17:17:03 <ryanofsky> Anything that doesn't include a hardened path should self-expand
17:17:42 <monlovesmango> ryanofsky: that is what I thought
17:18:15 <ryanofsky> Well next question is about application of CanSelfExpand, so related
17:18:19 <ryanofsky> 3. ExportWatchOnlyWallet only copies the descriptor cache if !desc->CanSelfExpand(). What exactly is stored in that cache? How could an incomplete cache affect address derivation on the watch-only wallet?
17:19:13 <monlovesmango> ryanofsky: about OriginPubkeyProvider question, m_provider is cast as std::unique_ptr<PubkeyProvider> which only defines a virtual CanSelfExpand function. how can I see that it is wrapped?
17:20:18 <ryanofsky> Oh I might be using the word "wrapped" in a different way maybe?
17:20:35 <stringintech> monlovesmongo you are right i confused CanSelfExpand() with CanGetAddresses() earlier
17:20:57 <monlovesmango> I might also be asking the question in an unintuitive way haha
17:21:38 <ryanofsky> I just meant to say that OriginPubkeyProvider contains another PubkeyProvider. And since can just call CanSelfExpand on that provider since origin information in a descriptor doesn't affect what the descriptor expands to
17:22:04 <ryanofsky> Origin information is just additional metadata, doesn't affect how addresses are generated or what scriptPubKeys are matched
17:22:44 <monlovesmango> ok that makes sense! thank you!
17:23:00 <stickies-v> The cache stores all the parent xpubs (aren't those in the descriptor already?), a well a a number of derived xpubs (`m_derived_xpubs`), I think according to the size of the keypool?
17:23:11 <ryanofsky> Ok, I'm not 100% sure of this stuff myself so don't take me as authority, but that's my understanding
17:23:14 <stringintech> On question 3: This case we need to copy the cache because it contains the pregenerated addresses from a hardened path where copying them allows us to use those addresses while not having private keys to resolve the hardened paths.
17:23:48 <stickies-v> so an incomplete cache would be similar to someone sending a transaction to a derived address that exceeds the gap limit, I think?
17:24:02 <ryanofsky> +1 stickies-v and stringintech yes that all sounds right
17:24:21 <pablomartin4btc> stringintech +1 (derivation info like last index used, etc)
17:24:22 <ryanofsky> The cache stores pre-derived pubkeys for hardened paths. If it isn’t copied, the watch-only wallet will not be able to see transactions sent to resulting addresses
17:25:45 <ryanofsky> Feel free to ask more about that if not clear, next question is on another topic
17:25:53 <ryanofsky> 4. The exporter sets create_flags = GetWalletFlags() | WALLET_FLAG_DISABLE_PRIVATE_KEYS. Why is it important to preserve the original flags (e.g. AVOID_REUSE) instead of clearing everything and starting fresh?
17:26:23 <stringintech> How can the number of pre-derived pubkeys be configured? (before exporting the watchonly wallet) Is it configurable? Cause eventually we will run out of them I guess.
17:27:02 <monlovesmango> stringintech: great question!
17:27:15 <pablomartin4btc> 4. i think keeping user intent and expected behavior across exported/restored environments
17:27:51 <ryanofsky> yes good question i wonder if the keypool size options affect it
17:28:21 <stickies-v> continuing on the cache: does that mean user should consider calling `keypoolrefill` rpc before exporting the watchonly wallet?
17:28:25 <ryanofsky> or maybe you just topup the keypool before exporting the wallet
17:28:52 <stickies-v> oh we were all thinking the same hah
17:29:12 <ryanofsky> Yes could look into it, good question
17:29:16 <stringintech> Cool :)))
17:29:43 <ryanofsky> Yes for question 4 I had: I think AVOID_REUSE flag is actually not relevant. IIUC point of that flag is to avoid spending from coins previously spent from to improve privacy, and since watch-only wallets can't spend it's not relevant. But not sure about this.
17:29:57 <ryanofsky> In general seems good to preserve flags though
17:30:29 <monlovesmango> yes, any you never know if future flags might have differences in behavior
17:30:43 <ryanofsky> Yep
17:30:44 <glozow> I think AvailableCoins's result depends on AVOID_REUSE, so there is a difference?
17:30:57 <glozow> even though you can't spend those coins
17:31:23 <monlovesmango> true
17:31:55 <ryanofsky> Oh so balance would show up differently, that makes sense if I'm understanding right
17:31:58 <glozow> the user would be confused why the balance shown isn't the same
17:32:01 <glozow> yeah I think so
17:32:01 <monlovesmango> like if you just need to provide an address AVOID_REUSE would affect which address could be returned?
17:32:14 <monlovesmango> oh ok
17:32:54 <monlovesmango> wait AVOID_REUSE would make the wallet not identify balances just bc address is reused?
17:33:21 <glozow> https://github.com/bitcoin/bitcoin/blob/cf15d45192e03ff2b0267842353f5f89541cb3f1/src/wallet/spend.cpp#L420
17:33:35 <ryanofsky> monlovesmango, I'm guessing it would still be in overall balance but not available balance
17:34:33 <ryanofsky> 5. Why does the exporter read the locator from the source wallet and write it verbatim into the new wallet instead of letting the new wallet start from block 0?
17:34:50 <monlovesmango> glozow: thanks :) ryanofsky: gotcha
17:36:21 <monlovesmango> I assume to not have to rescan the wallet?
17:36:26 <pablomartin4btc> monlovesmango: check avoid_reuse description in getbalance RPC
17:36:32 <stringintech> I think it is because we want to know where to resume scanning the blocks for txs we are interested in (and since we have already copied the old txs data, the wallet doesn't need to rescan from genesis). Though I am not confident yet on how locators work in general.
17:37:05 <monlovesmango> pablomartin4btc: will do!
17:37:52 <ryanofsky> feel free to ask about locators! But yeah that all sounds right, it's just to avoid rescanning. It would almost defeat purpose of the RPC if needed to rescan after exporting
17:38:13 <ryanofsky> 6. Consider a multisig descriptor wsh(multi(2,xpub1,xpub2)). If one cosigner exports a watch-only wallet and shares it with a third party, what new information does that third party learn compared to just giving them the descriptor strings?
17:38:17 <pablomartin4btc> monlovesmango +1 plus avoid missing transactions that happened after wallet creation but before export
17:39:31 <stringintech> About locators, what other parts of the codebase use them other than wallet?
17:40:20 <monlovesmango> pablomartin4btc: watch only wallet wouldn't find these? interesting
17:41:03 <ryanofsky> I think just the wallets and indexes use locators
17:41:13 <monlovesmango> is it all scenarios or are there only certain scenarios where watch only wallet wouldn't find these?
17:41:40 <stringintech> ryanofsky Thanks!
17:43:31 <glozow> are these the same locators we use for syncing headers with our peers?
17:45:04 <ryanofsky> Could be? Maybe they are. I'm very ignorant of p2p
17:45:21 <glozow> Seems they could be! `CBlockLocator`
17:45:39 <furszy> yes, they are.
17:45:45 <stringintech> Nice!
17:46:38 <ryanofsky> For answer 6 I had: If the third-party was given the watch-only wallet they would transaction history and wallet metadata along with the descriptor. If they only had the descriptor they might not be able to get full transaction history if uses hardened paths.
17:47:46 <stringintech> And also the addresses copied through the cache I guess (related to hardened paths)?
17:48:16 <monlovesmango> ryanofsky: oh that makes sense
17:48:54 <ryanofsky> stringintech, yes I think that's right
17:50:04 <ryanofsky> IIUC DescriptorCache just contains CExtPubKey objects so not addresses directly, but the thing you would use to derive them and also to match transactions
17:50:25 <monlovesmango> so just to test my understanding, would watch only descriptor of hardened path kinda be useless? or what information could you discern from the descriptor alone?
17:51:19 <stringintech> Oh right, thanks ryanofsky
17:52:46 <ryanofsky> monlovesmango, yes I can't think of any uses if the wallet only had a hardened descriptor and not private key and no cache
17:53:03 <monlovesmango> ryanofsky: cool thanks
17:54:04 <ryanofsky> Since not much time left will just post the last two questions, feel free to respond to either
17:54:08 <ryanofsky> 7. In wallet_exported_watchonly.py, why does the test call wallet.keypoolrefill(100) before checking spendability across the online/offline pair?
17:54:12 <ryanofsky> 8. Prior to this PR, users could achieve a watch-only setup via dumpwallet → importwallet or importdescriptors. Compare those approaches with exportwatchonlywallet in terms of UX, completeness, and long-term maintainability.
17:55:01 <stringintech> On the last one, dumpwallet and importwallet currently do not exist in the codebase, right?
17:55:43 <ryanofsky> Ha yeah, that's a pretty good reason
17:55:47 <monlovesmango> hahaah
17:56:07 <stringintech> :D :D :D
17:56:31 <monlovesmango> for the last one, there is no way to avoid rescan right?
17:57:08 <ryanofsky> I wrote: dumpwallet saves private keys which we don't want to export. importdescriptors would drop metadata, require rescanning, not work with hardened descriptors
17:57:09 <pablomartin4btc> stringintech yeah so at the moment you cant achieve the watch-only setup... unles you run it from the latest release
17:57:23 <pablomartin4btc> but you can do the dump from the wallettool/ bitcoin-wallet bin
17:57:58 <monlovesmango> and if descriptor of hardened path without priv key isn't useful importdescriptors wouldn't really do much?
17:58:07 <ryanofsky> pablomartin4btc, I think the PR doesn't provide a way to do this from wallettool, but it could in the future
17:58:10 <stringintech> ryanofsky: Is a manual copy of the wallet database file be equivalent of dumpwallet?
17:58:36 <stringintech> pablomartin4btc: ah thanks!!
17:58:38 <pablomartin4btc> ryanofsky, to run the dump, not the export
17:58:39 <monlovesmango> ryanofsky: ok you beat me haa
17:59:33 <stringintech> On question 7
18:00:01 <stringintech> In that test the watch-only wallet generates a new address and prepares an incomplete tx and for the offline wallet to be able to sign that tx it has to call keypoolrefill so it can pre-compute addresses which includes that specific address which allows the offline wallet to sign the transaction (since the offline wallet has the corresponding
18:00:02 <stringintech> private key)
18:00:33 <ryanofsky> i think a manual copy of the file had some more information, dumpwallet was like a text representation, but similar
18:00:56 <stringintech> ryanofsky: great thank you
18:01:03 <ryanofsky> stringintech, I think that is correct for question 7
18:01:30 <ryanofsky> Thanks everybody for reviewing and participating!
18:01:48 <stringintech> Thank you and thanks everyone.
18:01:49 <glozow> thanks ryanofsky!
18:01:50 <ryanofsky> Will end the meeting in a minute if no remaining comments
18:02:08 <pablomartin4btc> thank you ryanofsky! thanks for the useful notes!
18:02:09 <monlovesmango> thanks for hosting ryanofsky !! went fast and learned a lot
18:02:26 <ryanofsky> I learned a lot too, this was fun!
18:02:56 <ryanofsky> #endmeeting