This PR implements an RPC called descriptorprocesspsbt. This RPC is based off of walletprocesspsbt, but instead of interacting with the wallet to update, sign and finalize a psbt, it instead accepts an array of output descriptors and uses that information along with information from the mempool, txindex, and the utxo set to do so. utxoupdatepsbt also updates a psbt in this manner, but doesn't sign or finalize it. Because of this overlap, a helper function that is added in this PR is called by both utxoupdatepsbt and descriptorprocesspsbt. Whether or not the helper function signs a psbt is dictated by if the HidingSigningProvider passed to it contains any private information. There is also a test added in this PR for this new RPC that uses p2wsh, p2wpkh, and legacy outputs.
Edit: see #25796 (comment)
rpc: add `descriptorprocesspsbt` rpc #25796
pull ishaanam wants to merge 2 commits into bitcoin:master from ishaanam:descriptorprocesspsbt changing 6 files +142 −9-
ishaanam commented at 10:27 PM on August 6, 2022: contributor
- fanquake added the label RPC/REST/ZMQ on Aug 6, 2022
- ishaanam force-pushed on Aug 7, 2022
-
DrahtBot commented at 9:35 AM on August 7, 2022: contributor
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--021abf342d371248e50ceaed478a90ca-->
Reviews
See the guideline for information on the review process.
Type Reviewers ACK achow101, instagibbs Concept ACK jonatack, Sjors Approach ACK w0xlt If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.
<!--174a7506f384e20aa4161008e828411d-->
Conflicts
Reviewers, this pull request conflicts with the following ones:
- #22838 (descriptors: Be able to specify change and receiving in a single descriptor string by achow101)
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.
-
in src/rpc/rawtransaction.cpp:204 in 0061b6d8bf outdated
201 | + } 202 | + 203 | + const Coin& coin = view.AccessCoin(psbtx.tx->vin[i].prevout); 204 | + 205 | + if (coin.IsSpent()) { 206 | + throw JSONRPCError(RPC_VERIFY_ERROR, "Input not found or already spent");
ishaanam commented at 2:52 PM on August 7, 2022:This changes the behavior of
utxoupdatepsbtsince previously it didn't throw an error if an input has already been spent or doesn't exist. I'm not sure if the lack of an error here whenutxoupdatepsbtwas initially implemented was intentional. If it was intentional, then I will remove this.
Sjors commented at 1:21 PM on August 8, 2022:b7c6179db597d00b465b4e83047af56292e372a6 Not sure what the original intention was, @achow101? Perhaps it's useful to be able to process a PSBT on a node that's behind (knows about its own cold storage UTXO, but not about others)?
In any case it should probably be its own commit (along with the test you added in the next commit)
achow101 commented at 6:30 PM on August 8, 2022:I don't remember if it was intentional, however a PSBT could spend UTXOs that do not exist yet (e.g. not-yet-broadcast transactions) so I think it would still be correct to allow UTXOs that do not exist.
ishaanam commented at 7:45 PM on August 8, 2022:Makes sense, in that case I'll remove this.
jonatack commented at 4:36 PM on August 7, 2022: memberConcept ACK
<details><summary>A few suggestions for your consideration</summary><p>
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b6715f7f20..1ccc267ec1 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -164,7 +164,7 @@ static std::vector<RPCArg> CreateTxDoc() // Update PSBT with information from the mempool, the UTXO set, and the provided descriptors. // Optionally sign the inputs which we can using information from the descriptors. -PartiallySignedTransaction ProcessPSBT(const JSONRPCRequest& request, const HidingSigningProvider provider, int sighash_type = 1, bool finalize = false) +PartiallySignedTransaction ProcessPSBT(const JSONRPCRequest& request, const HidingSigningProvider& provider, int sighash_type, bool finalize) { // Unserialize the transactions PartiallySignedTransaction psbtx; @@ -179,11 +179,9 @@ PartiallySignedTransaction ProcessPSBT(const JSONRPCRequest& request, const Hidi { - NodeContext& node = EnsureAnyNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); const CTxMemPool& mempool = EnsureMemPool(node); - ChainstateManager& chainman = EnsureChainman(node); - LOCK2(cs_main, mempool.cs); - CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip(); - CCoinsViewMemPool viewMempool(&viewChain, mempool); - view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + LOCK2(::cs_main, mempool.cs); + CCoinsViewMemPool view_mempool{&EnsureChainman(node).ActiveChainstate().CoinsTip(), mempool}; + view.SetBackend(view_mempool); // temporarily switch cache backend to db+mempool view for (const CTxIn& txin : psbtx.tx->vin) { view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail. @@ -211,7 +209,7 @@ PartiallySignedTransaction ProcessPSBT(const JSONRPCRequest& request, const Hidi } } - const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx); + const PrecomputedTransactionData& txdata = PrecomputePSBTData(psbtx); for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { if (PSBTInputSigned(psbtx.inputs.at(i))) { @@ -220,9 +218,9 @@ PartiallySignedTransaction ProcessPSBT(const JSONRPCRequest& request, const Hidi // Update script/keypath information using descriptor data. // Note that SignPSBTInput does a lot more than just constructing ECDSA signatures. - // We only actually care about those if our signing provider doesn't hide private + // We only actually care about those if our signing provider doesn't hide private // information, as is the case with `descriptorprocesspsbt` - SignPSBTInput(provider, psbtx, i, &txdata, sighash_type, nullptr, finalize); + SignPSBTInput(provider, psbtx, /*index=*/i, &txdata, sighash_type, /*out_sigdata=*/nullptr, finalize); } // Update script/keypath information using descriptor data. @@ -1649,7 +1647,11 @@ static RPCHelpMan utxoupdatepsbt() } // We don't actually need private keys further on; hide them as a precaution. - PartiallySignedTransaction psbtx = ProcessPSBT(request, HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false)); + const PartiallySignedTransaction& psbtx = ProcessPSBT( + request, + HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false), + /*sighash_type=*/1, + /*finalize=*/false); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << psbtx; diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index d5693dc555..86311adfed 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -1087,7 +1087,7 @@ std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, Fl throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str)); } if (expand_priv) { - desc->ExpandPrivate(i, provider, provider); + desc->ExpandPrivate(/*pos=*/i, provider, /*out=*/provider); } std::move(scripts.begin(), scripts.end(), std::back_inserter(ret)); } diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index d6db6fbfc6..b7a2a4628f 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -581,7 +581,7 @@ class PSBTTest(BitcoinTestFramework): break assert shuffled - # Test that we can update and sign a psbt with descriptors + self.log.info("Test descriptorprocesspsbt updates and signs a psbt with descriptors") key_info = get_generate_key() key1 = key_info[0] wpkh_addr = key_info[5]</p></details>
ishaanam force-pushed on Aug 7, 2022Sjors commented at 1:36 PM on August 8, 2022: memberConcept ACK
The first commit is a useful refactor in any case.
in src/rpc/rawtransaction.cpp:166 in 58f265f4de outdated
161 | @@ -162,6 +162,61 @@ static std::vector<RPCArg> CreateTxDoc() 162 | }; 163 | } 164 | 165 | +// Update PSBT with information from the mempool, the UTXO set, and the provided descriptors 166 | +PartiallySignedTransaction ProcessPSBT(const JSONRPCRequest& request, const HidingSigningProvider provider)
achow101 commented at 6:26 PM on August 8, 2022:In 58f265f4de0051bbecb0f7e367a39258b24d5a40 "rpc: extract psbt updating logic into ProcessPSBT()"
I think it would be better to give the
UniValuefor the PSBT and aNodeContextrather than passing in the entireJSONRPCRequest.
ishaanam commented at 7:51 PM on August 8, 2022:Done
in src/rpc/rawtransaction.cpp:1925 in b7c6179db5 outdated
1920 | + auto descs = request.params[1].get_array(); 1921 | + for (size_t i = 0; i < descs.size(); ++i) { 1922 | + EvalDescriptorStringOrObject(descs[i], provider, /*expand_priv=*/true); 1923 | + } 1924 | + 1925 | + int nHashType = ParseSighashString(request.params[2]);
achow101 commented at 6:27 PM on August 8, 2022:In b7c6179db597d00b465b4e83047af56292e372a6 "rpc: add descriptorprocesspsbt rpc"
nit: snake_case
ishaanam commented at 7:50 PM on August 8, 2022:Done
ishaanam force-pushed on Aug 8, 2022ishaanam force-pushed on Aug 26, 2022ishaanam marked this as a draft on Aug 26, 2022ishaanam commented at 7:14 PM on August 26, 2022: contributorWhile working on this PR, I noticed that it might be useful for
utxoupdatepbstto also look in the txindex (if enabled) for the full tx that an input is spending from, which would allow legacy inputs to be updated as well. I have opened a separate PR for this, #25939, and this PR builds on top of that one so thatdescriptorprocesspsbtcan do this as well.ishaanam force-pushed on Aug 26, 2022ishaanam force-pushed on Aug 26, 2022in src/rpc/rawtransaction.cpp:238 in 2b67f9b731 outdated
223 | + } 224 | + } 225 | + } 226 | + } 227 | + 228 | + const PrecomputedTransactionData& txdata = PrecomputePSBTData(psbtx);
achow101 commented at 7:33 PM on October 31, 2022:In 2b67f9b73143e07a30add5f372395a612762da46 "rpc: add descriptorprocesspsbt rpc"
Seems like this should be in the previous commit.
ishaanam commented at 5:05 PM on November 6, 2022:I've moved this to the previous commit.
w0xlt commented at 7:46 PM on October 31, 2022: contributorApproach ACK
ishaanam force-pushed on Nov 6, 2022ishaanam force-pushed on Nov 6, 2022ishaanam force-pushed on Dec 4, 2022DrahtBot added the label Needs rebase on Jan 17, 2023ishaanam force-pushed on Jan 19, 2023ishaanam commented at 4:09 AM on January 19, 2023: contributorRebased
DrahtBot removed the label Needs rebase on Jan 19, 2023ishaanam force-pushed on Jan 23, 2023DrahtBot added the label Needs rebase on Apr 21, 2023ishaanam force-pushed on Apr 22, 2023ishaanam marked this as ready for review on Apr 22, 2023DrahtBot removed the label Needs rebase on Apr 22, 2023achow101 commented at 9:30 PM on May 3, 2023: memberACK 9bf2d303295594b8327ce0d67b9c1de98701f80a
fanquake requested review from instagibbs on May 4, 2023fanquake requested review from w0xlt on May 4, 2023in src/rpc/rawtransaction.cpp:1703 in efc62d61e6 outdated
1698 | @@ -1697,7 +1699,9 @@ static RPCHelpMan utxoupdatepsbt() 1699 | const PartiallySignedTransaction& psbtx = ProcessPSBT( 1700 | request.params[0].get_str(), 1701 | request.context, 1702 | - HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false)); 1703 | + HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false), 1704 | + /*sighash_type=*/1,
instagibbs commented at 1:43 PM on May 4, 2023:use
SIGHASH_ALLfor clarity
ishaanam commented at 3:24 PM on May 5, 2023:Done
in src/rpc/rawtransaction.cpp:1982 in efc62d61e6 outdated
1977 | + sighash_type, 1978 | + finalize); 1979 | + 1980 | + // Check whether or not all of the inputs are now signed 1981 | + bool complete = true; 1982 | + for (const auto& input : psbtx.inputs) {
instagibbs commented at 1:45 PM on May 4, 2023:future work: someone make this a subroutine since this shows up in many places
in test/functional/rpc_psbt.py:668 in 9bf2d30329 outdated
662 | @@ -660,6 +663,55 @@ def test_psbt_input_keys(psbt_input, keys): 663 | break 664 | assert shuffled 665 | 666 | + self.log.info("Test descriptorprocesspsbt updates and signs a psbt with descriptors") 667 | + key_info = get_generate_key() 668 | + key1 = key_info[0]
instagibbs commented at 2:02 PM on May 4, 2023:key_info.privkey?
ishaanam commented at 3:24 PM on May 5, 2023:Done
in test/functional/rpc_psbt.py:669 in 9bf2d30329 outdated
662 | @@ -660,6 +663,55 @@ def test_psbt_input_keys(psbt_input, keys): 663 | break 664 | assert shuffled 665 | 666 | + self.log.info("Test descriptorprocesspsbt updates and signs a psbt with descriptors") 667 | + key_info = get_generate_key() 668 | + key1 = key_info[0] 669 | + wpkh_addr = key_info[5]
instagibbs commented at 2:02 PM on May 4, 2023:key_info.p2wpkh_addr?
ishaanam commented at 3:24 PM on May 5, 2023:Done
in test/functional/rpc_psbt.py:675 in 9bf2d30329 outdated
670 | + descriptor1 = descsum_create(f"wpkh({key1})") 671 | + 672 | + a_key_info = get_generate_key() 673 | + b_key_info = get_generate_key() 674 | + c_key_info = get_generate_key() 675 | + a_key = a_key_info[0]
instagibbs commented at 2:05 PM on May 4, 2023:use named tuple args where you can
in test/functional/rpc_psbt.py:681 in 9bf2d30329 outdated
676 | + a_pubkey = a_key_info[1] 677 | + b_key = b_key_info[0] 678 | + b_pubkey = b_key_info[1] 679 | + c_pubkey = c_key_info[1] 680 | + 681 | + descriptor_a = descsum_create(f"wsh(multi(2,{a_key},{b_pubkey},{c_pubkey}))")
instagibbs commented at 2:06 PM on May 4, 2023:Can you leave comments on each step to make it clear what's happening? It's been a few minutes of staring and I'm not quite sure what's exactly being tested here, making it difficult to update in the future.
My basic test flow would probably follow the lines of:
- pick a node that doesn't have a wallet
- make a single wpkh utxo
- make single input psbt with that utxo
- make sure it signs it when expected, and finalizes when expected (e.g., give it a descriptor unrelated to the utxo, partial_signature lying around when expected, vary sighash modes)
- make sure extracted tx submits to mempool ok
imo we don't need to re-test all of the descriptor logic that's internal and hopefully already tested; we just want to check that the API here makes sense and pipes through. The 2-of-3 multisig, multi-input stuff just seems like a distraction.
ishaanam commented at 3:28 PM on May 5, 2023:I've rewritten the test to reflect this. I've also added more comments.
instagibbs commented at 3:35 PM on May 5, 2023:great, thanks!
instagibbs commented at 2:19 PM on May 4, 2023: memberconcept ACK, would appreciate test tighten-up for maintainability
DrahtBot removed review request from w0xlt on May 4, 2023fb2a3a70e8rpc: add descriptorprocesspsbt rpc
This RPC can be the Updater, Signer, and optionally the Input Finalizer for a psbt, and has no interaction with the Bitcoin Core wallet.
ishaanam force-pushed on May 5, 2023instagibbs commented at 3:35 PM on May 5, 2023: memberDrahtBot requested review from achow101 on May 5, 2023fanquake assigned achow101 on May 5, 2023DrahtBot added the label CI failed on May 5, 2023in test/functional/rpc_psbt.py:966 in 80f8bdfeb3 outdated
959 | + address = key_info.p2wpkh_addr 960 | + 961 | + descriptor = descsum_create(f"wpkh({key})") 962 | + 963 | + txid = self.nodes[0].sendtoaddress(address, 1) 964 | + self.sync_all()
achow101 commented at 9:14 PM on May 5, 2023:In 80f8bdfeb3d9301e0aec4b797689cbe569eff5a6 "test: add test for
descriptorprocesspsbtRPC"This sync is failing due to an earlier unconfirmed transaction not persisting after the restart. A possible fix is to generate a block before restarting ndoe 2.
ishaanam commented at 3:28 PM on May 6, 2023:Thanks, I've fixed it.
DrahtBot requested review from achow101 on May 5, 2023test: add test for `descriptorprocesspsbt` RPC 1bce12acd3ishaanam force-pushed on May 6, 2023DrahtBot removed the label CI failed on May 6, 2023achow101 commented at 12:41 AM on May 20, 2023: memberre-ACK 1bce12acd3e271a7c88d9400b4e3a5645bc8a911
DrahtBot removed review request from achow101 on May 20, 2023DrahtBot requested review from instagibbs on May 20, 2023instagibbs commented at 10:49 AM on May 20, 2023: memberDrahtBot removed review request from instagibbs on May 20, 2023achow101 merged this on May 22, 2023achow101 closed this on May 22, 2023sidhujag referenced this in commit a17baa132b on May 23, 2023ishaanam deleted the branch on Nov 30, 2023bitcoin locked this on Nov 29, 2024Labels
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: 2026-04-13 15:13 UTC
This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me