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.
This commit is contained in:
ishaanam 2022-07-27 14:24:19 +05:30
parent 8ae2808a43
commit a5b4883fb4

View file

@ -169,6 +169,63 @@ static std::vector<RPCArg> CreateTxDoc()
};
}
// Update PSBT with information from the mempool, the UTXO set, 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));
}
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
NodeContext& node = EnsureAnyNodeContext(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(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(provider, psbtx, i);
}
return psbtx;
}
static RPCHelpMan getrawtransaction()
{
return RPCHelpMan{
@ -1594,13 +1651,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()) {
@ -1609,53 +1659,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);
}
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;