New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RPC: allow to track coins by parent descriptors #25504
RPC: allow to track coins by parent descriptors #25504
Conversation
{ | ||
std::vector<WalletDescriptor> descs; | ||
for (const auto spk_man: GetScriptPubKeyMans(script)) { | ||
if (const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this cast be avoided, or a static cast used instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point here is to filter only descriptor spkmans, which can only be done dynamically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approach ACK
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
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. |
a3f89df
to
8976cd8
Compare
Concept ACK. JoinMarket currently imports each address into watchonly manually and assigns same label (JM wallet id) to them. So this is needed for JoinMarket to support ranged descriptors in future instead. |
To be clear, this affects more than just imported descriptors. It outputs the parent descriptor for each scriptPubKey, and so it will as well for the automatically generated descriptors. |
Concept ACK |
Good to see others have an interest in this. :) Note however that there is another barrier to tracking coins by descriptor on the RPC interface, if you want to track multiple descriptors on the same wallet. |
8976cd8
to
053f19a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As ListTransactions
receives the wtx
, instead of adding a copy of the scriptPubKey
inside COutputEntry
to later use it to call PushWalletDescriptors(wallet, r.script_pubkey, entry)
, what if you just get the output directly?
In other words, drop 9bfd9a9 and change d8b1e37 (line 398) in this way:
PushWalletDescriptors(wallet, wtx.tx->vout.at(r.vout).scriptPubKey, entry);
053f19a
to
f7a0398
Compare
Heh, indeed it's much cleaner. Thanks, done. (Force pushed again to re-kick CI which was blocked on "Agent is not responding!") |
f7a0398
to
83b8bdd
Compare
83b8bdd
to
3447b80
Compare
We currently expose a method to get the signing providers, which allows to infer a descriptor from the scriptPubKey. But in order to identify "on" what descriptor a coin was received, we need access to the descriptors that were imported to the wallet.
3447b80
to
dc148f3
Compare
I added a new commit to make |
dc148f3
to
02cc302
Compare
The descriptor wallets allow an application to track coins of multiple descriptors in a single wallet. However, such an application would not previously be able to (easily) tell what received coin "belongs" to what descriptor. This commit tackles this issues by adding a "wallet_desc" entry to the entries for received coins in 'listsinceblock'.
02cc302
to
9e78836
Compare
Friendly ping to reviewers, as i'd love to have this in the next release. :) |
ACK 9e78836 |
self.generate(self.nodes[2], 1) | ||
coins = self.nodes[2].listsinceblock()["transactions"] | ||
assert_equal(len([c for c in coins if c["address"] == addr]), 0) | ||
coins = self.nodes[2].listsinceblock(include_change=True)["transactions"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would this have two entries since you have the send(to change), and the normal change output as well?
I think the test should be explicit about it if so
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. What would you suggest? Just asserting there are two entries?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extract both outputs, assert they are change addresses, I suspect is the best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. This needed a couple re-arrangements since listsinceblock
lists also the "sent" coins, and we only want coins since the last block.
I couldn't resist a few drive-by cosmetic changes to the tests, including removing the unneeded block generation.
It's useful for an external application tracking coins to not be limited by our change detection. For instance, for a watchonly wallet with two descriptors a transaction from one to the other would be considered a change output and not be included in the result (if the address was not generated by this wallet).
9e78836
to
a6b0c1f
Compare
ACK a6b0c1f |
re-ACK a6b0c1f |
@@ -623,6 +632,7 @@ RPCHelpMan listsinceblock() | |||
} | |||
|
|||
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool()); | |||
bool include_change = (!request.params[4].isNull() && request.params[4].get_bool()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it ugly to encode default argument values in the code syntax, as opposed to simply name them with a literal. If the default were to change, one would have to change the code syntax as opposed to simply changing a literal.
const bool include_change{p.isNull()?false:p.get_bool()}; // should be clearer now and in the future if the default was changed or refactored out
@darosior I thought I fixed that in #22929. The test you added also specifically generates change address and not receiving address. Could you elaborate on the use-case here since it differs from the test? |
Sure, the use case is using Bitcoin Core to track coins for multiple descriptors, and any descriptor. There exists a number of applications doing that (and in my opinion, there should be more :p). #22929 was a good fix for users of the wallet as such, but it was limited to a single active descriptor per output type. It's not a viable solutions for applications that want "just an index of transactions per descriptors". Such an application should not care about the wallet internals: whether the descriptor is active or not, the spkman internal or not, the address in the book or not. No, they just want to know the transactions related to a set of descriptor in a given block range. We can provide this information, and that's what this PR is permitting. Regarding the test, i generated a change address to simulate an address that was externally generated (not in the address book), and not necessarily from a descriptor that was imported as "active" in the watchonly wallet (therefore may be considered internal, ie change). |
Wallet descriptors are useful for applications using the Bitcoin Core wallet as a backend for tracking coins, as they allow to track coins for multiple descriptors in a single wallet. However there is no information currently given for such applications to link a coin with an imported descriptor, severely limiting the possibilities for such applications of using multiple descriptors in a single wallet. This PR outputs the matching imported descriptor(s) for a given received coin in
listsinceblock
(and friends).It comes from a need for an application i'm working on, but i think it's something any software using
bitcoind
to track multiple descriptors in a single wallet would have eventually. For instance i'm thinking about the BDK project. Currently, the way to achieve this is to import raw addresses with labels and to have your application be responsible for wallet things like the gap limit.I'll add this to the output of
listunspent
too if this gets a few Concept ACKs.