-
Notifications
You must be signed in to change notification settings - Fork 37.7k
rpc: add rpc to get mempool txs spending specific prevouts #24408
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
Conversation
a7966e4
to
2a7db52
Compare
For this usecase, why not using |
Because that wouldn't work for confirmed utxos! My funding transaction (or any L2 protocol initial state) has been confirmed on-chain and I want to check who spends it in the mempool. I can't use Maybe the RPC name is misleading, because its inputs ( |
Right, sorry, i shouldn't have inferred the behaviour from the name. I see it's in the context of |
It's much weaker than |
2a7db52
to
cc038b6
Compare
Is it possible to add it in existing RPC |
I'm not sure it would be very ergonomic, because
Correctly defining the arguments lists (and which are required) to support both use-cases without creating confusion seems hard to be honest... |
Not being able to walk the chain forward is certainly the source of huge friction and hacky code while working with Bitcoin RPC. I'd even go that far to claim that this is the single largest missing feature that if we'd have, it would have saved the most time for me personally. Certainly cACK this approach, but I'd like to note a general version of it would be more useful, somehow we should be able to determine which transaction spends a UTXO if any, regardless if it's from mempool or not. |
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.
Concept ACK, seems very reasonable to me especially since we already have a mempool function to fetch the transaction. Also in general, it seems useful to be able to fetch the transaction that conflicts with yours.
src/rpc/blockchain.cpp
Outdated
"\nScans the mempool to find a transaction spending the given output\n", | ||
{ | ||
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "transaction id"}, | ||
{"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"}, | ||
}, |
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'm wondering if, in the future, you might want to pass in a list of prevouts? (e.g. if you have transaction(s) with multiple inputs and want to see if anything in the mempool conflicts)
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.
That's a good idea, it's worth adding this flexibility right now to avoid running in backwards-compatibility issues if we add it later, I'll try that. I'll take a list of prevouts
and return a map of prevout -> spending tx (or null if not found)
.
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 in 0015ceb
I chose to return only the spendingtxid
instead of the mempool tx data, because otherwise if a transaction appears multiple times (because it spends many of the provided prevouts) it would be duplicated, which is a (small) waste.
Callers can chain this with getmempoolentry
to get more data about the conflicting transaction.
If you think it makes more sense to directly include the mempool tx let me know, I don't have strong feelings about that.
This might be tricky (the non-mempool case) since we would have deleted the UTXO from our chainstate. And not possible sometimes on a pruned node. |
Concept ACK - rationale in PR description seems strong and other projects (e.g. Wasabi) have experienced same issue.
I think the use case from @nopara73's comment above would be you don't know whether the transaction spending the UTXO is in your mempool or confirmed onchain and you want to query your mempool first and then your transaction index? I think generally you'd want one RPC to query your mempool and one RPC to query your transaction index. Combining both into a single RPC seems to me against separation of concerns (and is easily resolved by just running two RPC commands). |
FTR there's |
@nopara73 do you mean that instead of adding a new RPC like I'm doing, we could update In the case where the spending tx is not in the mempool, I think the only solution would be to walk the chain backwards and analyze each block to see if the spending tx is inside it. However, as @glozow says, if you're running a pruned node, you will likely analyze every block you have and won't find anything, which is very inefficient. Even worse, if the provided utxo never existed, you will walk the whole chain without finding anything... It looks like a much more debatable change which would involve serious trade-offs, that's why I think restricting this PR to mempool txs has a better chance of being accepted. I could still reuse |
0b6cdaa
to
4ef5a16
Compare
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. |
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.
Concept ACK.
Maybe the two commits can be merged. Apparently the first commit is in an invalid state (since the outputs
parameter is not converted).
263b0a7
to
0015ceb
Compare
Definitely, done. I was waiting for the builds to complete to check that I hadn't missed something else that would need fixing. |
I think without this RPC, you can create a tx that spends the input and only fetch the whole mempool if the input was spent. Moreover, I wonder what happens if the tx is included in a block right away? So the input will be gone, but there isn't a tx in the mempool that spends it. While the RPC makes sense on its own, it doesn't fix your problem completely? |
Thanks for the review @MarcoFalke!
I probably tried to be a bit too general in my description, let me try to explain in more details my current scenario. In the specific case where I want to apply this in the short term, I'm trying to spend lightning htlcs. I have a pre-signed transaction that spends the outpoint I'm interested in but doesn't provide enough fees, so I'll want to add more inputs to reach the right feerate. The complete flow looks like this:
Without this RPC, I would have to optimistically fund and broadcast, and if that fails fetch the whole mempool to find the conflicting transaction. I expect to regularly have to go through the RBF flow, so it feels inefficient to have to dump the mempool when that happens :( You're right that if the current mempool transaction confirms while I'm funding its replacement, I'll need to correctly handle it, but that doesn't seem to be too much of a problem for my use-case. Does that make sense with this more detailed scenario? |
Thanks for the review @glozow!
I am more and more convinced that it's better to only have the We're regularly seeing big coinjoin transactions in our systems, so if we include the whole tx in the RPC response we can easily have responses that span several Mb, which is a waste for |
re ACK 8c9152c |
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.
Code Review ACK 8c9152c
A caveat : the usage of this RPC you're describing sounds unsafe to me. If you're desirous to monitor the spend of a shared-utxo such as LN funding output, your counterparty might be able to broadcast multiple conflicting transactions. If this counterparty knows your full-node, your mempool can even be partitioned from the rest of the network, with a junk feerate, potentially wrecking your fee-bump. Of course, this assumes a bit of cross-layer mapping work on your malicious counterparty. So better to reconciliate from multiple mempools or opt for blind fee-bump after a while imho.
Thanks for the comments! But that shouldn't be an issue in our case (at least not related to this RPC). This RPC is helpful to react instantly once a shared output is spent. It doesn't matter if in the end it's another transaction that gets confirmed, we will learn this when we receive blocks and will then adapt. It then becomes a different problem of ensuring you're receiving blocks (avoiding complete eclipse attacks), which we solve by a combination of blockchain watchdogs and a good Protecting against commit tx pinning is an entirely separate issue as well, which exists regardless of whether this RPC exists or not. |
8c9152c
to
8f9335f
Compare
You would add the txout you need to watch to the wallet (maybe we need to add a way to do that), then the wallet would scan every new block/tx internally, letting you know via listtransactions.
At least legacy wallets support both normal and watch-only in the same wallet... |
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.
tACK 8f9335f
I confirmed that the new RPC correctly returns the ids of transactions that spend the outpoints passed as a parameter.
@luke-jr thanks, I understand what you mean, I didn't know a wallet could be both normal and watch-only. This is something that could make sense, we are currently doing this scan outside of Does it completely replace this PR though? I can see value in having both options (a dedicated RPC and a watch-only feature), the watch-only mode could be added afterwards (especially if we see people starting to rely on this new RPC). |
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.
Conceptually, this feature needs a transaction indexer and that's what a wallet is. I think ideally this feature should be part of the wallet as a more general "get spending tx by prevout" feature, rather than a very usecase-specific feature leveraging the mempool index.
However, it would be a bit convoluted to use at the moment. It would require them to import a new descriptor for each output they would need to claim, without (currently) the possibility to delete it. The number of descriptors under watch would therefore grow continuously as the software would be used.
Accounting for the immediate benefit to users, and the low cost of this RPC (it only calls into a single mempool function), i agree it can be merged. I believe longer term we should rather improve the wallet instead of providing (what i think are) specialized wallet functionalities at the node level.
light code review ACK 8f9335f -- the code makes sense to me and i didn't spot any issue. But i only quickly glanced at the diff.
(For reference, we have a similar issue with revaultd
: in order to fetch a spending transaction from the watchonly wallet whose txid is unknown we need to iterate on the list of the wallet transactions. https://github.com/revault/revaultd/blob/b310208a8b3b420f9b0cddcc03588e264a99538b/src/bitcoind/interface.rs#L777-L864 (fortunately listsinceblock
allows us to reduce the cost by only iterating on transactions since the last pool).)
I agree, and you're one step ahead, see #24539 :) |
8f9335f
to
ad14372
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.
reACK ad14372
We add an RPC to fetch the mempool transactions spending given outpoints. Without this RPC, application developers would need to first call `getrawmempool` which returns a long list of `txid`, then fetch each of these txs individually to check whether they spend the given outpoint(s). This RPC can later be enriched to also find confirmed transactions instead of being restricted to mempool transactions.
ad14372
to
4185570
Compare
Simple rebase on |
I don't have any pending issues on that PR, if @ariard @darosior @glozow @MarcoFalke you have time to give this another pass whenever possible it would be great 🙏 |
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.
re-utACK 4185570
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.
re-ACK 4185570
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.
re-tACK 4185570
We add an RPC to fetch the mempool transactions spending given outpoints. Without this RPC, application developers would need to first call `getrawmempool` which returns a long list of `txid`, then fetch each of these txs individually to check whether they spend the given outpoint(s). This RPC can later be enriched to also find confirmed transactions instead of being restricted to mempool transactions. Github-Pull: bitcoin#24408 Rebased-From: ad14372
cc @glozow |
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.
reACK 4185570
…c prevouts 4185570 Add RPC to get mempool txs spending outputs (t-bast) Pull request description: We add an RPC to fetch mempool transactions spending any of the given outpoints. Without this RPC, application developers need to first call `getrawmempool` which returns a long list of `txid`, then fetch each of these transactions individually (`getrawtransaction`) to check whether they spend the given outpoints, which wastes a lot of bandwidth (in the worst case we need to transfer the whole mempool). For example in lightning, when we discover that one of our channel funding transactions has been spent, we need to find the spending transaction to claim our outputs from it. We are currently forced to fetch the whole mempool to do the analysis ourselves, which is quite costly. I believe that this RPC is also generally useful when doing some introspection on your mempool after one of your transactions failed to broadcast, for example when you implement RBF at the application level. Fetching and analyzing the conflicting transaction gives you more information to successfully replace it. ACKs for top commit: darosior: re-utACK 4185570 vincenzopalazzo: re-ACK bitcoin@4185570 danielabrozzoni: re-tACK 4185570 w0xlt: reACK bitcoin@4185570 Tree-SHA512: 206687efb720308b7e0b6cf16dd0a994006c0b5a290c8eb386917a80130973a6356d0d5cae1c63a01bb29e066dd721594969db106cba7249214fcac90d2c3dbc
We add an RPC to fetch mempool transactions spending any of the given outpoints.
Without this RPC, application developers need to first call
getrawmempool
which returns a long list oftxid
, then fetch each of these transactions individually (getrawtransaction
) to check whether they spend the given outpoints, which wastes a lot of bandwidth (in the worst case we need to transfer the whole mempool).For example in lightning, when we discover that one of our channel funding transactions has been spent, we need to find the spending transaction to claim our outputs from it. We are currently forced to fetch the whole mempool to do the analysis ourselves, which is quite costly.
I believe that this RPC is also generally useful when doing some introspection on your mempool after one of your transactions failed to broadcast, for example when you implement RBF at the application level. Fetching and analyzing the conflicting transaction gives you more information to successfully replace it.