aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/rawtransaction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc/rawtransaction.cpp')
-rw-r--r--src/rpc/rawtransaction.cpp145
1 files changed, 91 insertions, 54 deletions
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 21d49fda9d..4a918cbd42 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -172,6 +172,91 @@ static std::vector<RPCArg> CreateTxDoc()
};
}
+// Update PSBT with information from the mempool, the UTXO set, the txindex, and the provided descriptors
+PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std::any& context, const HidingSigningProvider& provider)
+{
+ // Unserialize the transactions
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodeBase64PSBT(psbtx, psbt_string, error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ if (g_txindex) g_txindex->BlockUntilSyncedToCurrentChain();
+ const NodeContext& node = EnsureAnyNodeContext(context);
+
+ // If we can't find the corresponding full transaction for all of our inputs,
+ // this will be used to find just the utxos for the segwit inputs for which
+ // the full transaction isn't found
+ std::map<COutPoint, Coin> coins;
+
+ // Fetch previous transactions:
+ // First, look in the txindex and the mempool
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& psbt_input = psbtx.inputs.at(i);
+ const CTxIn& tx_in = psbtx.tx->vin.at(i);
+
+ // The `non_witness_utxo` is the whole previous transaction
+ if (psbt_input.non_witness_utxo) continue;
+
+ CTransactionRef tx;
+
+ // Look in the txindex
+ if (g_txindex) {
+ uint256 block_hash;
+ g_txindex->FindTx(tx_in.prevout.hash, block_hash, tx);
+ }
+ // If we still don't have it look in the mempool
+ if (!tx) {
+ tx = node.mempool->get(tx_in.prevout.hash);
+ }
+ if (tx) {
+ psbt_input.non_witness_utxo = tx;
+ } else {
+ coins[tx_in.prevout]; // Create empty map entry keyed by prevout
+ }
+ }
+
+ // If we still haven't found all of the inputs, look for the missing ones in the utxo set
+ if (!coins.empty()) {
+ FindCoins(node, coins);
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ // If there are still missing utxos, add them if they were found in the utxo set
+ if (!input.non_witness_utxo) {
+ const CTxIn& tx_in = psbtx.tx->vin.at(i);
+ const Coin& coin = coins.at(tx_in.prevout);
+ if (!coin.out.IsNull() && IsSegWitOutput(provider, coin.out.scriptPubKey)) {
+ input.witness_utxo = coin.out;
+ }
+ }
+ }
+ }
+
+ const PrecomputedTransactionData& txdata = PrecomputePSBTData(psbtx);
+
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ if (PSBTInputSigned(psbtx.inputs.at(i))) {
+ continue;
+ }
+
+ // Update script/keypath information using descriptor data.
+ // Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
+ // we don't actually care about those here, in fact.
+ SignPSBTInput(provider, psbtx, /*index=*/i, &txdata, /*sighash=*/1);
+ }
+
+ // Update script/keypath information using descriptor data.
+ for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
+ UpdatePSBTOutput(provider, psbtx, i);
+ }
+
+ RemoveUnnecessaryTransactions(psbtx, /*sighash_type=*/1);
+
+ return psbtx;
+}
+
static RPCHelpMan getrawtransaction()
{
return RPCHelpMan{
@@ -1580,7 +1665,7 @@ static RPCHelpMan converttopsbt()
static RPCHelpMan utxoupdatepsbt()
{
return RPCHelpMan{"utxoupdatepsbt",
- "\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set or the mempool.\n",
+ "\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set, txindex, or the mempool.\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
{"descriptors", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "An array of either strings or objects", {
@@ -1599,13 +1684,6 @@ static RPCHelpMan utxoupdatepsbt()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- // Unserialize the transactions
- PartiallySignedTransaction psbtx;
- std::string error;
- if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
- }
-
// Parse descriptors, if any.
FlatSigningProvider provider;
if (!request.params[1].isNull()) {
@@ -1614,53 +1692,12 @@ static RPCHelpMan utxoupdatepsbt()
EvalDescriptorStringOrObject(descs[i], provider);
}
}
- // We don't actually need private keys further on; hide them as a precaution.
- HidingSigningProvider public_provider(&provider, /*hide_secret=*/true, /*hide_origin=*/false);
-
- // Fetch previous transactions (inputs):
- CCoinsView viewDummy;
- CCoinsViewCache view(&viewDummy);
- {
- 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
-
- for (const CTxIn& txin : psbtx.tx->vin) {
- view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
- }
-
- view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
- }
-
- // Fill the inputs
- const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
- for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- PSBTInput& input = psbtx.inputs.at(i);
- if (input.non_witness_utxo || !input.witness_utxo.IsNull()) {
- continue;
- }
-
- const Coin& coin = view.AccessCoin(psbtx.tx->vin[i].prevout);
-
- if (IsSegWitOutput(provider, coin.out.scriptPubKey)) {
- input.witness_utxo = coin.out;
- }
-
- // Update script/keypath information using descriptor data.
- // Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
- // we don't actually care about those here, in fact.
- SignPSBTInput(public_provider, psbtx, i, &txdata, /*sighash=*/1);
- }
-
- // Update script/keypath information using descriptor data.
- for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
- UpdatePSBTOutput(public_provider, psbtx, i);
- }
+ // We don't actually need private keys further on; hide them as a precaution.
+ const PartiallySignedTransaction& psbtx = ProcessPSBT(
+ request.params[0].get_str(),
+ request.context,
+ HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false));
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;