Skip to content

Commit a5b4883

Browse files
committed
rpc: extract psbt updating logic into ProcessPSBT
This function is called from utxoupdatepsbt and will be modified in a following commit to allow for updating inputs with the `non_witness_utxo` as well.
1 parent 8ae2808 commit a5b4883

File tree

1 file changed

+62
-53
lines changed

1 file changed

+62
-53
lines changed

src/rpc/rawtransaction.cpp

Lines changed: 62 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,63 @@ static std::vector<RPCArg> CreateTxDoc()
169169
};
170170
}
171171

172+
// Update PSBT with information from the mempool, the UTXO set, and the provided descriptors
173+
PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std::any& context, const HidingSigningProvider& provider)
174+
{
175+
// Unserialize the transactions
176+
PartiallySignedTransaction psbtx;
177+
std::string error;
178+
if (!DecodeBase64PSBT(psbtx, psbt_string, error)) {
179+
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
180+
}
181+
182+
// Fetch previous transactions (inputs):
183+
CCoinsView viewDummy;
184+
CCoinsViewCache view(&viewDummy);
185+
{
186+
NodeContext& node = EnsureAnyNodeContext(context);
187+
const CTxMemPool& mempool = EnsureMemPool(node);
188+
ChainstateManager& chainman = EnsureChainman(node);
189+
LOCK2(cs_main, mempool.cs);
190+
CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip();
191+
CCoinsViewMemPool viewMempool(&viewChain, mempool);
192+
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
193+
194+
for (const CTxIn& txin : psbtx.tx->vin) {
195+
view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
196+
}
197+
198+
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
199+
}
200+
201+
// Fill the inputs
202+
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
203+
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
204+
PSBTInput& input = psbtx.inputs.at(i);
205+
206+
if (input.non_witness_utxo || !input.witness_utxo.IsNull()) {
207+
continue;
208+
}
209+
210+
const Coin& coin = view.AccessCoin(psbtx.tx->vin[i].prevout);
211+
212+
if (IsSegWitOutput(provider, coin.out.scriptPubKey)) {
213+
input.witness_utxo = coin.out;
214+
}
215+
216+
// Update script/keypath information using descriptor data.
217+
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
218+
// we don't actually care about those here, in fact.
219+
SignPSBTInput(provider, psbtx, i, &txdata, /*sighash=*/1);
220+
}
221+
222+
// Update script/keypath information using descriptor data.
223+
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
224+
UpdatePSBTOutput(provider, psbtx, i);
225+
}
226+
return psbtx;
227+
}
228+
172229
static RPCHelpMan getrawtransaction()
173230
{
174231
return RPCHelpMan{
@@ -1594,13 +1651,6 @@ static RPCHelpMan utxoupdatepsbt()
15941651
},
15951652
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
15961653
{
1597-
// Unserialize the transactions
1598-
PartiallySignedTransaction psbtx;
1599-
std::string error;
1600-
if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
1601-
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
1602-
}
1603-
16041654
// Parse descriptors, if any.
16051655
FlatSigningProvider provider;
16061656
if (!request.params[1].isNull()) {
@@ -1609,53 +1659,12 @@ static RPCHelpMan utxoupdatepsbt()
16091659
EvalDescriptorStringOrObject(descs[i], provider);
16101660
}
16111661
}
1612-
// We don't actually need private keys further on; hide them as a precaution.
1613-
HidingSigningProvider public_provider(&provider, /*hide_secret=*/true, /*hide_origin=*/false);
1614-
1615-
// Fetch previous transactions (inputs):
1616-
CCoinsView viewDummy;
1617-
CCoinsViewCache view(&viewDummy);
1618-
{
1619-
NodeContext& node = EnsureAnyNodeContext(request.context);
1620-
const CTxMemPool& mempool = EnsureMemPool(node);
1621-
ChainstateManager& chainman = EnsureChainman(node);
1622-
LOCK2(cs_main, mempool.cs);
1623-
CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip();
1624-
CCoinsViewMemPool viewMempool(&viewChain, mempool);
1625-
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
1626-
1627-
for (const CTxIn& txin : psbtx.tx->vin) {
1628-
view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
1629-
}
1630-
1631-
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
1632-
}
1633-
1634-
// Fill the inputs
1635-
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
1636-
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
1637-
PSBTInput& input = psbtx.inputs.at(i);
1638-
1639-
if (input.non_witness_utxo || !input.witness_utxo.IsNull()) {
1640-
continue;
1641-
}
1642-
1643-
const Coin& coin = view.AccessCoin(psbtx.tx->vin[i].prevout);
1644-
1645-
if (IsSegWitOutput(provider, coin.out.scriptPubKey)) {
1646-
input.witness_utxo = coin.out;
1647-
}
1648-
1649-
// Update script/keypath information using descriptor data.
1650-
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
1651-
// we don't actually care about those here, in fact.
1652-
SignPSBTInput(public_provider, psbtx, i, &txdata, /*sighash=*/1);
1653-
}
16541662

1655-
// Update script/keypath information using descriptor data.
1656-
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
1657-
UpdatePSBTOutput(public_provider, psbtx, i);
1658-
}
1663+
// We don't actually need private keys further on; hide them as a precaution.
1664+
const PartiallySignedTransaction& psbtx = ProcessPSBT(
1665+
request.params[0].get_str(),
1666+
request.context,
1667+
HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false));
16591668

16601669
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
16611670
ssTx << psbtx;

0 commit comments

Comments
 (0)