rpc: add rpc to get mempool txs spending specific prevouts #24408

pull t-bast wants to merge 1 commits into bitcoin:master from t-bast:get-mempool-spending-tx changing 7 files +200 −0
  1. t-bast commented at 5:24 pm on February 21, 2022: member

    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.

  2. t-bast force-pushed on Feb 21, 2022
  3. in test/functional/mempool_packages.py:103 in 2a7db52cec outdated
     99@@ -100,6 +100,11 @@ def run_test(self):
    100             entry = self.nodes[0].getmempoolentry(x)
    101             assert_equal(entry, mempool[x])
    102 
    103+            # Check that getmempoolspendingtx is consistent with getrawmempool
    


    t-bast commented at 5:27 pm on February 21, 2022:

    It looks like there aren’t any dedicated test files for getmempoolentry, getmempoolancestors and getmempooldescendants (I wanted to insert my tests in such a file).

    Should I create a new rpc_getmempoolpendingtx.py to put more tests (e.g. some failure cases)?


    glozow commented at 11:22 am on February 22, 2022:
    Yeah, that’s probably the move. “rpc_mempool_info.py” would be a good place to put tests for all of these RPCs.

    t-bast commented at 4:31 pm on February 24, 2022:
    Done in https://github.com/bitcoin/bitcoin/pull/24408/commits/0015ceb04cb3545914e2bf7c4b2cde6d66f172ed, I have only added tests for my new RPC for now, future PRs can add tests for getmempoolentry, getmempoolancestors and getmempooldescendants here if that makes sense.
  4. darosior commented at 5:28 pm on February 21, 2022: member
    For this usecase, why not using getmempoolentry’s spentby field?
  5. t-bast commented at 5:30 pm on February 21, 2022: member

    For this usecase, why not using getmempoolentry’s spentby field?

    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 getmempoolentry because I don’t know yet the txid of the spending tx…

    Maybe the RPC name is misleading, because its inputs (txid and vout) aren’t necessarily for a mempool tx (and most of the time will not), but the RPC output will be a mempool tx (or null).

  6. darosior commented at 5:39 pm on February 21, 2022: member
    Right, sorry, i shouldn’t have inferred the behaviour from the name. I see it’s in the context of txindex then, so i don’t have an opinion.
  7. t-bast commented at 5:50 pm on February 21, 2022: member
    It’s much weaker than txindex, it’s exposing a txindex for mempool contents, which is why I think it’s acceptable (I’m only querying existing mempool functions).
  8. t-bast force-pushed on Feb 21, 2022
  9. DrahtBot added the label RPC/REST/ZMQ on Feb 21, 2022
  10. ghost commented at 2:04 am on February 22, 2022: none

    For this usecase, why not using getmempoolentry’s spentby field?

    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 getmempoolentry because I don’t know yet the txid of the spending tx…

    Maybe the RPC name is misleading, because its inputs (txid and vout) aren’t necessarily for a mempool tx (and most of the time will not), but the RPC output will be a mempool tx (or null).

    Is it possible to add it in existing RPC getmempoolentry by adding more arguments (optional)?

  11. t-bast commented at 8:48 am on February 22, 2022: member

    Is it possible to add it in existing RPC getmempoolentry by adding more arguments (optional)?

    I’m not sure it would be very ergonomic, because getmempoolentry requires the txid of the mempool transaction you’re fetching. If we wanted to overload it, it would have the following two distinct sets of behaviors:

    • a txid is provided, so we return a mempool transaction that matches this txid
    • a txid (probably named differently to avoid confusion) and a vout are provided, so we return a mempool transaction spending this given txid:vout

    Correctly defining the arguments lists (and which are required) to support both use-cases without creating confusion seems hard to be honest…

  12. nopara73 commented at 11:15 am on February 22, 2022: none
    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.
  13. in src/rpc/blockchain.cpp:804 in cc038b6b09 outdated
    791+    return RPCHelpMan{"getmempoolspendingtx",
    792+                "\nScans the mempool to find a transaction spending the given output\n",
    793+                {
    794+                    {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "transaction id"},
    795+                    {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
    796+                },
    


    glozow commented at 11:24 am on February 22, 2022:
    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)

    t-bast commented at 12:35 pm on February 22, 2022:
    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).

    t-bast commented at 4:34 pm on February 24, 2022:

    Done in https://github.com/bitcoin/bitcoin/pull/24408/commits/0015ceb04cb3545914e2bf7c4b2cde6d66f172ed 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.

  14. in src/rpc/blockchain.cpp:789 in cc038b6b09 outdated
    785@@ -786,6 +786,49 @@ static RPCHelpMan getmempoolentry()
    786     };
    787 }
    788 
    789+static RPCHelpMan getmempoolspendingtx()
    


    glozow commented at 11:28 am on February 22, 2022:
    Sorry to nitpick but I was confused in the beginning by the name as well (I thought you wanted the child of a mempool transaction). Maybe gettxspendingprevout would be better, but I’m sure there’s someone out there who would find it even more confusing :sweat_smile:

    t-bast commented at 12:33 pm on February 22, 2022:
    I agree, I’m not a fan of the name! Should it be getmempooltxspendingprevout though to highlight that we return a mempool tx (with ancestor / descendant feerates, which are useful in fee-bumping cases)? It’s a long name, but at least it’s quite obvious what it does (hopefully).

    t-bast commented at 4:35 pm on February 24, 2022:
    I updated the name, but now that is accepts multiple prevouts, I’m not sure whether I should make it plural or not… None of the names I find are completely satisfying, so I’ll let reviewers chime in with better proposals!
  15. glozow commented at 11:28 am on February 22, 2022: member
    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.
  16. glozow commented at 11:32 am on February 22, 2022: member

    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.

    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.

  17. michaelfolkson commented at 11:55 am on February 22, 2022: contributor

    Concept ACK - rationale in PR description seems strong and other projects (e.g. Wasabi) have experienced same issue.

    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

    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).

  18. nopara73 commented at 12:01 pm on February 22, 2022: none
    FTR there’s gettxout. I think my suggestion would fit best there.
  19. t-bast commented at 12:31 pm on February 22, 2022: member

    FTR there’s gettxout. I think my suggestion would fit best there. @nopara73 do you mean that instead of adding a new RPC like I’m doing, we could update gettxout to return the spending tx when the utxo is spent?

    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 gettxout instead of adding a new RPC, but that would make it harder (backwards-compatibility wise) to accept a list of prevouts as @glozow suggests here.

  20. t-bast force-pushed on Feb 24, 2022
  21. t-bast force-pushed on Feb 24, 2022
  22. DrahtBot commented at 6:51 am on February 25, 2022: member

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #25204 (rpc: remove deprecated top-level fee fields from mempool entries by theStack)
    • #22341 (rpc: add getxpub by Sjors)
    • #21422 (Add feerate histogram to getmempoolinfo by kiminuo)

    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.

  23. t-bast renamed this:
    Add RPC to get mempool spending tx
    rpc: add rpc to get mempool spending tx
    on Feb 25, 2022
  24. t-bast renamed this:
    rpc: add rpc to get mempool spending tx
    rpc: add rpc to get mempool txs spending specific prevouts
    on Feb 25, 2022
  25. w0xlt commented at 3:35 pm on February 25, 2022: contributor
    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).
  26. t-bast force-pushed on Feb 25, 2022
  27. t-bast commented at 3:40 pm on February 25, 2022: member

    Maybe the two commits can be merged.

    Definitely, done. I was waiting for the builds to complete to check that I hadn’t missed something else that would need fixing.

  28. DrahtBot added the label Needs rebase on Mar 3, 2022
  29. MarcoFalke commented at 7:23 pm on March 3, 2022: member

    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).

    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?

  30. in doc/release-notes.md:127 in 0015ceb04c outdated
    122@@ -123,6 +123,8 @@ New RPCs
    123   a future release. Note that in either case, the `status` field
    124   now reflects the status of the current block rather than the next
    125   block. (#23508)
    126+- A new `getmempooltxspendingprevout` RPC has been added, which scans the mempool to find
    127+  transactions spending any of the given outpoints. (#24408)
    


    MarcoFalke commented at 7:24 pm on March 3, 2022:

    Maybe move this to a separate file for now?

    doc/release-notes-24408.md


    t-bast commented at 0:01 am on March 4, 2022:
  31. in src/rpc/blockchain.cpp:792 in 0015ceb04c outdated
    785@@ -786,6 +786,90 @@ static RPCHelpMan getmempoolentry()
    786     };
    787 }
    788 
    789+static RPCHelpMan getmempooltxspendingprevout()
    790+{
    791+    return RPCHelpMan{"getmempooltxspendingprevout",
    792+                "\nScans the mempool to find transactions spending any of the given outputs\n",
    


    MarcoFalke commented at 7:25 pm on March 3, 2022:
    0                "Scans the mempool to find transactions spending any of the given outputs",
    

    nit: I think those can be removed


  32. t-bast commented at 11:52 pm on March 3, 2022: member

    Thanks for the review @MarcoFalke!

    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?

    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:

    • I verify that the output is unspent with gettxout (without including the mempool)
      • If it’s spent, then I have nothing to do (actually it triggers another flow to spend the output of that spending tx, but that’s orthogonal)
      • Otherwise, I check if there is already a mempool tx spending this output with getmempooltxspendingprevout
        • If nothing is in the mempool, I’ll use fundrawtransaction to add inputs and broadcast the transaction
        • Otherwise, I’ll modify the mempool transaction to pay more fees (by lowering/removing the existing change output and potentially adding new inputs)

    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?

  33. t-bast force-pushed on Mar 4, 2022
  34. DrahtBot removed the label Needs rebase on Mar 4, 2022
  35. t-bast commented at 2:02 pm on March 4, 2022: member

    Actually there is a simpler place where we’ll use that new RPC in our codebase. When a lightning node restarts, it lists its channels from its internal DB and verifies that the funding outpoint of each channel is still unspent (with gettxout).

    If we discover than a channel has actually been spent (maybe our counterparty is malicious and saw that we were offline, and tried to cheat), we want to find the spending transaction to be able to react and publish our set of follow-up transactions. So the first thing we do is look into the mempool, currently by dumping it and checking every transaction ourselves. With getmempooltxspendingprevout we would avoid a performance hit here.

    Of course, if the spending transaction isn’t in the mempool, then we have to check recent blocks one by one, but that shouldn’t happen often, and since lightning nodes shouldn’t be offline for too long, you usually only have to check 1 or 2 blocks (which means transferring < 5MB which is much smaller than the whole mempool).

  36. in doc/release-notes/release-notes-24408.md:4 in 4c96203ec3 outdated
    0@@ -0,0 +1,5 @@
    1+New RPCs
    2+--------
    3+
    4+- A new `getmempooltxspendingprevout` RPC has been added, which scans the mempool to find
    


    unknown commented at 3:49 am on March 5, 2022:
    0- A new `getmempoolprevout` RPC has been added, which scans the mempool to find
    
  37. luke-jr commented at 8:00 pm on March 7, 2022: member
    This seems like it should be using a watch-only wallet instead.
  38. in src/rpc/blockchain.cpp:812 in 4c96203ec3 outdated
    807+                    {
    808+                        {RPCResult::Type::OBJ, "", "",
    809+                        {
    810+                            {RPCResult::Type::STR_HEX, "txid", "the transaction id of the spent output"},
    811+                            {RPCResult::Type::NUM, "vout", "the vout value of the spent output"},
    812+                            {RPCResult::Type::STR_HEX, "spendingtxid", "the transaction id of the mempool transaction spending this output (ommitted if unspent)"},
    


    danielabrozzoni commented at 4:41 pm on March 9, 2022:
    nit: I think it should be spelled “omitted” (only one m) :)

  39. danielabrozzoni approved
  40. danielabrozzoni commented at 5:26 pm on March 9, 2022: contributor

    tACK 4c96203ec36f5634d6f73f3b9a1d99bbd91dcc21 - the code looks good to me, I tested locally and it works as expected.

    I would have needed this API quite a few times in the past, so I’d be happy to see this merged :)

  41. MarcoFalke commented at 5:47 pm on March 9, 2022: member

    This seems like it should be using a watch-only wallet instead.

    This wouldn’t work for users that don’t have or don’t want a bdb/sqlite dependency as well the overhead of having to deal with wallet maintenance. So I can see the argument that this RPC makes it easier for users.

  42. DrahtBot added the label Needs rebase on Mar 16, 2022
  43. t-bast force-pushed on Mar 24, 2022
  44. t-bast commented at 9:45 am on March 24, 2022: member

    This seems like it should be using a watch-only wallet instead. @luke-jr can you detail how that would work? I may be missing something, but I don’t see how that would address my issue. I do need a regular wallet as I’m relying heavily on fundrawtransaction, it would be wasteful to need both a watch-only wallet and a regular wallet to make this use-case work?

  45. t-bast commented at 9:47 am on March 24, 2022: member

    Sorry for the delay in responding to comments and rebasing, I’ve been away for a while, the PR is now rebased.

    One of the points that I’d like more feedback on is that 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 complete tx data for 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.

  46. DrahtBot removed the label Needs rebase on Mar 24, 2022
  47. in src/rpc/mempool.cpp:648 in 9a9011c5aa outdated
    432+        if (nOutput < 0) {
    433+            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
    434+        }
    435+
    436+        const COutPoint output(txid, nOutput);
    437+        outputs.push_back(output);
    


    vincenzopalazzo commented at 3:31 pm on April 1, 2022:
    0        outputs.emplace_back(txid, nOutput);
    

    Change this can avoid a copy of the object



    t-bast commented at 8:55 am on April 4, 2022:
    The tests started failing after this change, so I’m reverting it. To be honest, I don’t think we care about such optimizations on just a few bytes here.

    vincenzopalazzo commented at 9:12 am on April 4, 2022:

    The tests started failing after this change, so I’m reverting it.

    Mh! strange the Ci looks like failing again.

    To be honest, I don’t think we care about such optimizations on just a few bytes here.

    Right, this can be also a followup PR too


    t-bast commented at 9:18 am on April 4, 2022:

    Mh! strange the Ci looks like failing again.

    True! That is strange, I tried rebasing, let’s see if that fixes it.


    MarcoFalke commented at 11:07 am on April 4, 2022:
    Test failure is due to stricter documentation checking in current master, not due to commit 8e6db71ff6cf2c8f724183996b4842fcf53d16b0 . I left a comment with a potential fix.

    t-bast commented at 1:59 pm on April 4, 2022:
    Thanks for the tip, I re-added the change requested in that comment in https://github.com/bitcoin/bitcoin/pull/24408/commits/e143e80fb93a752f522c7cc3e98a4f55626d6b49
  48. in src/rpc/mempool.cpp:667 in 9a9011c5aa outdated
    451+        if (spendingTx != nullptr) {
    452+            o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
    453+        }
    454+
    455+        result.push_back(o);
    456+    }
    


    vincenzopalazzo commented at 3:36 pm on April 1, 2022:
     0    for (const COutPoint& output : outputs) {
     1        result.emplace_back(UniValue::VOBJ);
     2        UniValue o = result.back();
     3        o.pushKV("txid", output.hash.ToString());
     4        o.pushKV("vout", (uint64_t)output.n);
     5
     6        const CTransaction* spendingTx = mempool.GetConflictTx(output);
     7        if (spendingTx != nullptr) {
     8            o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
     9        }
    10    }
    

    In this way we avoid the move too, and for a transaction this is important IMHO


    t-bast commented at 7:15 am on April 4, 2022:
    Thanks for the review! This doesn’t seem to compile though…and I’m not sure it’s needed at all, we’re not dealing with a whole transaction here, just a txid, so a copy is really not something we care about IMO.

    vincenzopalazzo commented at 8:22 am on April 4, 2022:

    Thanks for the review! This doesn’t seem to compile though

    I will try to compile it locally maybe something wrong with UniValue::VOBJ that my mind compiler is not able to catch right now!

    just a txid, so a copy is really not something we care about IMO

    Right, I wrote a bad motivation, I mean here there COutPoint can be a lot, and this loop with the emplace_back and be optimized, because with the push_back to need to copy the structure in a pointer this can cause move, or a copy in some case.

    With emplace_back you need to allocate the space of the struct one time, and this will summarize from:

    push_back: 1 stack allocation + 1 copy to heap, and the copy cost O(N) emplace_back: 1 allocation and this with reserve() cost O(1)

    But yes, overall it is not a big deal.

  49. vincenzopalazzo changes_requested
  50. vincenzopalazzo commented at 3:38 pm on April 1, 2022: none

    Concept ACK

    Just some code styling improvement

  51. t-bast force-pushed on Apr 4, 2022
  52. t-bast force-pushed on Apr 4, 2022
  53. in src/rpc/mempool.cpp:613 in b68dd3b931 outdated
    608+                    {
    609+                        {RPCResult::Type::OBJ, "", "",
    610+                        {
    611+                            {RPCResult::Type::STR_HEX, "txid", "the transaction id of the spent output"},
    612+                            {RPCResult::Type::NUM, "vout", "the vout value of the spent output"},
    613+                            {RPCResult::Type::STR_HEX, "spendingtxid", "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
    


    MarcoFalke commented at 11:05 am on April 4, 2022:
    0                            {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=/*true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
    

    This may fix the CI failure (haven’t tested)


    t-bast commented at 1:58 pm on April 4, 2022:
    Thanks for the tip, this seems to be working! Love the new github name btw ;)
  54. vincenzopalazzo approved
  55. in test/functional/rpc_mempool_info.py:23 in e143e80fb9 outdated
    18+    def set_test_params(self):
    19+        self.num_nodes = 1
    20+        self.setup_clean_chain = True
    21+
    22+    def skip_test_if_missing_module(self):
    23+        self.skip_if_no_wallet()
    


    MarcoFalke commented at 12:55 pm on April 6, 2022:

    I’ve reworked this test to use the MiniWallet.

    If you want you can take the diff and squash. It requires a rebase on current master, though.

    However, for some reason one line in the test fails and I fail to see why. I can take another look there, unless someone beats me to it.

    Also, there are some whitespace fixes. You can check them with git diff --ignore-all-space.

      0diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
      1index fe25dc9edc..2d07b8bd2e 100644
      2--- a/src/rpc/mempool.cpp
      3+++ b/src/rpc/mempool.cpp
      4@@ -590,83 +590,83 @@ static RPCHelpMan getmempoolentry()
      5 static RPCHelpMan getmempooltxspendingprevout()
      6 {
      7     return RPCHelpMan{"getmempooltxspendingprevout",
      8-                "Scans the mempool to find transactions spending any of the given outputs",
      9+        "Scans the mempool to find transactions spending any of the given outputs",
     10+        {
     11+            {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).",
     12                 {
     13-                    {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).",
     14+                    {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
     15                         {
     16-                            {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
     17-                                {
     18-                                    {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
     19-                                    {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
     20-                                },
     21-                            },
     22+                            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
     23+                            {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
     24                         },
     25                     },
     26                 },
     27-                RPCResult{
     28-                    RPCResult::Type::ARR, "", "",
     29-                    {
     30-                        {RPCResult::Type::OBJ, "", "",
     31-                        {
     32-                            {RPCResult::Type::STR_HEX, "txid", "the transaction id of the spent output"},
     33-                            {RPCResult::Type::NUM, "vout", "the vout value of the spent output"},
     34-                            {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
     35-                        }},
     36-                    }
     37-                },
     38-                RPCExamples{
     39-                    HelpExampleCli("getmempooltxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
     40-                    + HelpExampleRpc("getmempooltxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
     41-                },
     42+            },
     43+        },
     44+        RPCResult{
     45+            RPCResult::Type::ARR, "", "",
     46+            {
     47+                {RPCResult::Type::OBJ, "", "",
     48+                {
     49+                    {RPCResult::Type::STR_HEX, "txid", "the transaction id of the spent output"},
     50+                    {RPCResult::Type::NUM, "vout", "the vout value of the spent output"},
     51+                    {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
     52+                }},
     53+            }
     54+        },
     55+        RPCExamples{
     56+            HelpExampleCli("getmempooltxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
     57+            + HelpExampleRpc("getmempooltxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
     58+        },
     59         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     60-{
     61-    RPCTypeCheckArgument(request.params[0], UniValue::VARR);
     62-    const UniValue& output_params = request.params[0];
     63-    if (output_params.empty()) {
     64-        throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing");
     65-    }
     66+        {
     67+            RPCTypeCheckArgument(request.params[0], UniValue::VARR);
     68+            const UniValue& output_params = request.params[0];
     69+            if (output_params.empty()) {
     70+                throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing");
     71+            }
     72 
     73-    std::vector<COutPoint> outputs;
     74-    outputs.reserve(output_params.size());
     75+            std::vector<COutPoint> outputs;
     76+            outputs.reserve(output_params.size());
     77 
     78-    for (unsigned int idx = 0; idx < output_params.size(); idx++) {
     79-        const UniValue& o = output_params[idx].get_obj();
     80+            for (unsigned int idx = 0; idx < output_params.size(); idx++) {
     81+                const UniValue& o = output_params[idx].get_obj();
     82 
     83-        RPCTypeCheckObj(o,
     84-            {
     85-                {"txid", UniValueType(UniValue::VSTR)},
     86-                {"vout", UniValueType(UniValue::VNUM)},
     87-            });
     88+                RPCTypeCheckObj(o,
     89+                                {
     90+                                    {"txid", UniValueType(UniValue::VSTR)},
     91+                                    {"vout", UniValueType(UniValue::VNUM)},
     92+                                });
     93+
     94+                const uint256 txid(ParseHashO(o, "txid"));
     95+                const int nOutput = find_value(o, "vout").get_int();
     96+                if (nOutput < 0) {
     97+                    throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
     98+                }
     99 
    100-        const uint256 txid(ParseHashO(o, "txid"));
    101-        const int nOutput = find_value(o, "vout").get_int();
    102-        if (nOutput < 0) {
    103-            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
    104-        }
    105+                outputs.emplace_back(txid, nOutput);
    106+            }
    107 
    108-        outputs.emplace_back(txid, nOutput);
    109-    }
    110+            const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
    111+            LOCK(mempool.cs);
    112 
    113-    const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
    114-    LOCK(mempool.cs);
    115-
    116-    UniValue result{UniValue::VARR};
    117+            UniValue result{UniValue::VARR};
    118 
    119-    for (const COutPoint& output : outputs) {
    120-        UniValue o(UniValue::VOBJ);
    121-        o.pushKV("txid", output.hash.ToString());
    122-        o.pushKV("vout", (uint64_t)output.n);
    123+            for (const COutPoint& output : outputs) {
    124+                UniValue o(UniValue::VOBJ);
    125+                o.pushKV("txid", output.hash.ToString());
    126+                o.pushKV("vout", (uint64_t)output.n);
    127 
    128-        const CTransaction* spendingTx = mempool.GetConflictTx(output);
    129-        if (spendingTx != nullptr) {
    130-            o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
    131-        }
    132+                const CTransaction* spendingTx = mempool.GetConflictTx(output);
    133+                if (spendingTx != nullptr) {
    134+                    o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
    135+                }
    136 
    137-        result.push_back(o);
    138-    }
    139+                result.push_back(o);
    140+            }
    141 
    142-    return result;
    143-},
    144+            return result;
    145+        },
    146     };
    147 }
    148 
    149diff --git a/test/functional/rpc_mempool_info.py b/test/functional/rpc_mempool_info.py
    150index ef5b9535d4..da4314444d 100755
    151--- a/test/functional/rpc_mempool_info.py
    152+++ b/test/functional/rpc_mempool_info.py
    153@@ -13,19 +13,17 @@ from test_framework.util import (
    154     assert_raises_rpc_error,
    155     chain_transaction,
    156 )
    157+from test_framework.wallet import MiniWallet
    158+
    159 
    160 class RPCMempoolInfoTest(BitcoinTestFramework):
    161     def set_test_params(self):
    162         self.num_nodes = 1
    163-        self.setup_clean_chain = True
    164-
    165-    def skip_test_if_missing_module(self):
    166-        self.skip_if_no_wallet()
    167 
    168     def run_test(self):
    169-        # Mine some blocks and have them mature.
    170-        self.generate(self.nodes[0], COINBASE_MATURITY + 1)
    171-        confirmed_utxo = self.nodes[0].listunspent(1)[0]
    172+        self.wallet = MiniWallet(self.nodes[0])
    173+        self.wallet.rescan_utxos()
    174+        confirmed_utxo = self.wallet.get_utxo()
    175 
    176         # Create a tree of unconfirmed transactions in the mempool:
    177         #             txA
    178@@ -41,15 +39,21 @@ class RPCMempoolInfoTest(BitcoinTestFramework):
    179         #            \   /
    180         #             \ /
    181         #             txH
    182-        fee = Decimal("0.0001")
    183-        (txidA, txA_amount) = chain_transaction(self.nodes[0], [confirmed_utxo['txid']], [0], confirmed_utxo['amount'], fee, 2)
    184-        (txidB, txB_amount) = chain_transaction(self.nodes[0], [txidA], [0], txA_amount, fee, 2)
    185-        (txidC, txC_amount) = chain_transaction(self.nodes[0], [txidA], [1], txA_amount, fee, 2)
    186-        txidD, _ = chain_transaction(self.nodes[0], [txidB], [0], txB_amount, fee, 1)
    187-        (txidE, txE_amount) = chain_transaction(self.nodes[0], [txidB], [1], txB_amount, fee, 3)
    188-        (txidF, txF_amount) = chain_transaction(self.nodes[0], [txidC], [0], txC_amount, fee, 2)
    189-        txidG, _ = chain_transaction(self.nodes[0], [txidC], [1], txC_amount, fee, 1)
    190-        txidH, _ = chain_transaction(self.nodes[0], [txidE, txidF], [0, 1], txE_amount + txF_amount, fee, 1)
    191+
    192+        def create_tx(**kwargs):
    193+            return self.wallet.send_self_transfer_multi(
    194+                from_node=self.nodes[0],
    195+                **kwargs,
    196+            )
    197+
    198+        txA = create_tx(utxos_to_spend=[confirmed_utxo], num_outputs=2)
    199+        txB, txC = [create_tx(utxos_to_spend=[u], num_outputs=2) for u in txA["new_utxos"]]
    200+        txD, txE = [create_tx(utxos_to_spend=[u]) for u in txB["new_utxos"]]
    201+        txF, txG = [create_tx(utxos_to_spend=[u]) for u in txC["new_utxos"]]
    202+        txH = create_tx(utxos_to_spend=txE["new_utxos"] + txF["new_utxos"])
    203+        txidA, txidB, txidC, txidD, txidE, txidF, txidG, txidH = [
    204+            tx["txid"] for tx in [txA, txB, txC, txD, txE, txF, txG, txH]
    205+        ]
    206 
    207         mempool = self.nodes[0].getrawmempool()
    208         assert_equal(len(mempool), 8)
    

    t-bast commented at 2:31 pm on April 6, 2022:

    Many thanks for the suggestion! I have rebased and applied it in https://github.com/bitcoin/bitcoin/pull/24408/commits/aa39757914efd638f76dbb20d21865e8f0488c03, along with a small fix for the MiniWallet issue (by default it looked like there was no utxo generated for the wallet).

    Regarding the whitespace changes, I agree with you that it’s much better now, I thought I had to stick with the formatting used by the other functions in this file for consistency (even though it looked weird).


    MarcoFalke commented at 2:42 pm on April 6, 2022:
    Good to see you fixed up the test. If you squash everything down into one commit, it should be easier for others to review. You don’t need to mention me in the commit body.

    t-bast commented at 2:44 pm on April 6, 2022:
    Thanks, done!
  56. t-bast force-pushed on Apr 6, 2022
  57. t-bast force-pushed on Apr 6, 2022
  58. t-bast force-pushed on Apr 6, 2022
  59. vincenzopalazzo approved
  60. t-bast commented at 8:18 am on April 8, 2022: member

    @danielabrozzoni @w0xlt @glozow would you mind having a look at the latest state of this PR?

    My only open question/thing to consider is whether I should include the whole spending tx instead of just the spendingtxid in the results. I chose to only include the spendingtxid because otherwise if a transaction appears multiple times (because it spends many of the provided prevouts) it would be duplicated, which is a bandwidth waste. Callers can chain this with getmempoolentry to get more complete tx data for the conflicting transaction. If you think it makes more sense to directly include the mempool tx let me know, I don’t have very strong feelings about that.

  61. vincenzopalazzo commented at 11:26 am on April 8, 2022: none

    My only open question/thing to consider is whether I should include the whole spending tx instead of just the spendingtxid in the results. I chose to only include the spendingtxid because otherwise if a transaction appears multiple times (because it spends many of the provided prevouts) it would be duplicated, which is a bandwidth waste.

    IMHO the RPC command needs to do one operation, and in a clean way. What getmempooltxspendingprevout already done with your actual implementation. How I can see in my mind if the getmempooltxspendingprevout return all the mem pool entry is a solution that makes the API a little bit messy?

    A solution that can fix your open question is to add another RPC method that returns all the spendingtxid for each prevouts in there the duplicated can be fine because lives in a separate JSON object.

    What do you think?

  62. t-bast commented at 1:00 pm on April 8, 2022: member

    IMHO the RPC command needs to do one operation, and in a clean way. What getmempooltxspendingprevout already done with your actual implementation.

    :+1:

    A solution that can fix your open question is to add another RPC method that returns all the spendingtxid for each prevouts in there the duplicated can be fine because lives in a separate JSON object.

    I would avoid adding yet another RPC method, since the same result can be easily achieved by doing getmempooltxspendingprevout followed by getmempoolentry. I would add new RPC methods only for things that really cannot be done with existing RPCs, otherwise bitcoind will end up with way too many RPC methods which is going to be a pain to maintain.

  63. vincenzopalazzo commented at 1:06 pm on April 8, 2022: none

    I would avoid adding yet another RPC method, since the same result can be easily achieved by doing getmempooltxspendingprevout followed by getmempoolentry. I would add new RPC methods only for things that really cannot be done with existing RPCs, otherwise bitcoind will end up with way too many RPC methods which is going to be a pain to maintain.

    I have your same idea here!

  64. in test/functional/rpc_mempool_info.py:84 in c3cde8ca86 outdated
    79+        result = self.nodes[0].getmempooltxspendingprevout([ {'txid' : txidB, 'vout' : 0}, {'txid' : txidG, 'vout' : 3} ])
    80+        assert_equal(result, [ {'txid' : txidB, 'vout' : 0, 'spendingtxid' : txidD}, {'txid' : txidG, 'vout' : 3} ])
    81+
    82+        self.log.info("Unknown input fields")
    83+        result = self.nodes[0].getmempooltxspendingprevout([ {'txid' : txidC, 'vout' : 1, 'unknown' : 42} ])
    84+        assert_equal(result, [ {'txid' : txidC, 'vout' : 1, 'spendingtxid' : txidG} ])
    


    glozow commented at 9:26 pm on April 22, 2022:
    Question: would it be better for this to throw a RPC_INVALID_PARAMETER?

    t-bast commented at 7:51 am on April 25, 2022:
    Good question. I tend to silently ignore unknown parameters in APIs because it’s generally more friendly to clients. If a future version of bitcoind introduces new optional parameters, a client can safely start including them without caring about the version of bitcoind it’s running against, and calls will work on older bitcoind as well. That’s not a very strong reason though, I can change that if you want.

    glozow commented at 9:24 pm on April 25, 2022:
    No objections here! Being more user-friendly sounds good :grin:
  65. in src/rpc/mempool.cpp:655 in c3cde8ca86 outdated
    650+            const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
    651+            LOCK(mempool.cs);
    652+
    653+            UniValue result{UniValue::VARR};
    654+
    655+            for (const COutPoint& output : outputs) {
    


    glozow commented at 9:27 pm on April 22, 2022:
    Small nit: since the type is COutPoint, I would prefer to name this prevout or outpoint so we don’t confuse it with a COutput.

  66. in src/rpc/mempool.cpp:590 in c3cde8ca86 outdated
    586@@ -587,6 +587,89 @@ static RPCHelpMan getmempoolentry()
    587     };
    588 }
    589 
    590+static RPCHelpMan getmempooltxspendingprevout()
    


    glozow commented at 9:32 pm on April 22, 2022:
    Also a nit (especially since that PR didn’t exist when you opened this one): if we end up with #24539 as well, a more generic RPC named gettxspendingprevout might be better, i.e. to retrieve a transaction from block or from mempool.

    t-bast commented at 8:34 am on April 25, 2022:

    Good idea! I changed that in https://github.com/bitcoin/bitcoin/pull/24408/commits/8c9152c1098acec4b1b4e333240f0e95eea21159 so that #24539 can build on top of it. I currently kept it in mempool.cpp and in the mempool RPCs list, I feel it’s the responsibility of #24539 to move it elsewhere, is that ok @sstone?

    I think #24539 won’t have to modify any of the RPC fields, which is great. Callers will simply follow up with getrawtransaction which will provide the confirmations field, informing them of whether the sending transaction is in the mempool or is confirmed.


    sstone commented at 2:42 pm on April 25, 2022:
    Yes, and I agree that it makes sense to rebase #24539 on this PR
  67. in test/functional/rpc_mempool_info.py:51 in c3cde8ca86 outdated
    46+
    47+        txA = create_tx(utxos_to_spend=[confirmed_utxo], num_outputs=2)
    48+        txB = create_tx(utxos_to_spend=[txA["new_utxos"][0]], num_outputs=2)
    49+        txC = create_tx(utxos_to_spend=[txA["new_utxos"][1]], num_outputs=2)
    50+        txD = create_tx(utxos_to_spend=[txB["new_utxos"][0]], num_outputs=1)
    51+        txE = create_tx(utxos_to_spend=[txB["new_utxos"][1]], num_outputs=3)
    


    glozow commented at 9:33 pm on April 22, 2022:
    nit: seems like txE only needs 1 output

  68. glozow commented at 9:35 pm on April 22, 2022: member

    code review ACK c3cde8ca864f13a638f558d1631f144ffa37e03d. Looks correct, clean, and well-tested to me. Some nits if you retouch.

    My only open question/thing to consider is whether I should include the whole spending tx instead of just the spendingtxid in the results.

    I don’t feel strongly, but if you change your mind, I think it’s perfectly fine to include the whole spending tx. There’s not much waste since we’ve already retrieved it from the mempool, and only a privileged user can be using the RPC interface anyway.

  69. t-bast force-pushed on Apr 25, 2022
  70. t-bast commented at 8:38 am on April 25, 2022: member

    Thanks for the review @glozow!

    I don’t feel strongly, but if you change your mind, I think it’s perfectly fine to include the whole spending tx. There’s not much waste since we’ve already retrieved it from the mempool, and only a privileged user can be using the RPC interface anyway.

    I am more and more convinced that it’s better to only have the spendingtxid. There is a lot of useful information in the output of getrawtransaction that callers will want, so they will very likely follow up with a call to that RPC anyway (and we don’t want to replicate all of its contents in the output of gettxspendingprevout).

    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 bitcoind nodes on remote machines accessed through a (sometimes flaky) VPN (which we’ve seen some lightning node operators do).

  71. glozow commented at 0:17 am on April 26, 2022: member
    re ACK 8c9152c1098acec4b1b4e333240f0e95eea21159
  72. MarcoFalke commented at 12:35 pm on April 27, 2022: member
    cc Antoine for review (@darosior @ariard), if interested.
  73. in src/rpc/mempool.cpp:612 in 8c9152c109 outdated
    607+            RPCResult::Type::ARR, "", "",
    608+            {
    609+                {RPCResult::Type::OBJ, "", "",
    610+                {
    611+                    {RPCResult::Type::STR_HEX, "txid", "the transaction id of the spent output"},
    612+                    {RPCResult::Type::NUM, "vout", "the vout value of the spent output"},
    


    ariard commented at 3:11 pm on April 27, 2022:
    super-nit: if you wanna normalize the wording, one of the information outcome of this RPC is that output might not be spent in fact, so might said “the check output” here and above.

  74. ariard commented at 3:18 pm on April 27, 2022: member

    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.

  75. t-bast commented at 6:39 am on April 28, 2022: member

    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.

    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 bitcoind setup.

    Protecting against commit tx pinning is an entirely separate issue as well, which exists regardless of whether this RPC exists or not.

  76. t-bast force-pushed on Apr 28, 2022
  77. luke-jr commented at 8:09 pm on April 29, 2022: member

    @luke-jr can you detail how that would work?

    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.

    it would be wasteful to need both a watch-only wallet and a regular wallet to make this use-case work?

    At least legacy wallets support both normal and watch-only in the same wallet…

  78. in src/rpc/mempool.cpp:639 in 8f9335f19c outdated
    634+
    635+                RPCTypeCheckObj(o,
    636+                                {
    637+                                    {"txid", UniValueType(UniValue::VSTR)},
    638+                                    {"vout", UniValueType(UniValue::VNUM)},
    639+                                });
    


    w0xlt commented at 4:30 am on May 1, 2022:

    nit: With this change, RPC will not accept invalid/unrelated parameters.

    0                RPCTypeCheckObj(o,
    1                                /*typesExpected=*/{
    2                                    {"txid", UniValueType(UniValue::VSTR)},
    3                                    {"vout", UniValueType(UniValue::VNUM)},
    4                                }, /*fAllowNull=*/false, /*fStrict=*/true);
    

    t-bast commented at 6:51 am on May 2, 2022:
    Thanks for the tip! I’m leaning more towards being flexible here with regards to unknown parameters (see this comment) but it’s a weak preference, if you feel more strongly about being strict here I can apply your proposed change, let me know!

    w0xlt commented at 10:17 am on May 2, 2022:
    Both options work for me. I don’t have a strong opinion on that. I just assume that if an invalid parameter is used then maybe the user is expecting different behavior than the default.

    t-bast commented at 11:20 am on May 2, 2022:
    Since both you and @glozow noticed this, it’s probably worth making this stricter as you suggest. Done in https://github.com/bitcoin/bitcoin/pull/24408/commits/ad14372cab1897b3f5efafe9861ec696f5e97060
  79. in test/functional/rpc_mempool_info.py:84 in 8f9335f19c outdated
    79+        result = self.nodes[0].gettxspendingprevout([ {'txid' : txidB, 'vout' : 0}, {'txid' : txidG, 'vout' : 3} ])
    80+        assert_equal(result, [ {'txid' : txidB, 'vout' : 0, 'spendingtxid' : txidD}, {'txid' : txidG, 'vout' : 3} ])
    81+
    82+        self.log.info("Unknown input fields")
    83+        result = self.nodes[0].gettxspendingprevout([ {'txid' : txidC, 'vout' : 1, 'unknown' : 42} ])
    84+        assert_equal(result, [ {'txid' : txidC, 'vout' : 1, 'spendingtxid' : txidG} ])
    


    w0xlt commented at 4:33 am on May 1, 2022:

    nit (related to the previous suggestion):

    0        self.log.info("Unknown input fields")
    1        assert_raises_rpc_error(-3, "Unexpected key unknown", self.nodes[0].gettxspendingprevout, [{'txid' : txidC, 'vout' : 1, 'unknown' : 42}])
    

  80. w0xlt approved
  81. w0xlt commented at 4:34 am on May 1, 2022: contributor

    tACK https://github.com/bitcoin/bitcoin/pull/24408/commits/8f9335f19ce75515f9e6944de503a5278a92f464

    I confirmed that the new RPC correctly returns the ids of transactions that spend the outpoints passed as a parameter.

  82. t-bast commented at 6:59 am on May 2, 2022: member

    @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 bitcoind, but having bitcoind do it for us could be useful.

    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).

  83. darosior commented at 8:22 am on May 2, 2022: member

    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 8f9335f19ce75515f9e6944de503a5278a92f464 – 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).)

  84. t-bast commented at 8:54 am on May 2, 2022: member

    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.

    I agree, and you’re one step ahead, see #24539 :) Thanks for the comments!

  85. t-bast force-pushed on May 2, 2022
  86. w0xlt approved
  87. Add RPC to get mempool txs spending outputs
    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.
    4185570340
  88. t-bast force-pushed on May 5, 2022
  89. t-bast commented at 1:16 pm on May 5, 2022: member
  90. t-bast commented at 7:44 am on May 18, 2022: member
    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 :pray:
  91. darosior commented at 8:46 am on May 18, 2022: member
    re-utACK 418557034055f740951294e7677ae9fd5149ea9b
  92. vincenzopalazzo approved
  93. danielabrozzoni approved
  94. danielabrozzoni commented at 9:40 am on May 18, 2022: contributor
    re-tACK 418557034055f740951294e7677ae9fd5149ea9b
  95. fanquake requested review from glozow on May 18, 2022
  96. luke-jr referenced this in commit a2cca8c669 on May 21, 2022
  97. fanquake commented at 7:30 am on May 23, 2022: member
    cc @glozow
  98. w0xlt approved
  99. MarcoFalke merged this on May 27, 2022
  100. MarcoFalke closed this on May 27, 2022

  101. sidhujag referenced this in commit 03c4764216 on May 28, 2022
  102. t-bast deleted the branch on May 29, 2022
  103. DrahtBot locked this on May 29, 2023

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-07-03 07:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me