mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-12 11:19:08 -05:00
Implement utxoupdatepsbt RPC and tests
This commit is contained in:
parent
e3b1c7a9d6
commit
7344a7b998
2 changed files with 93 additions and 1 deletions
|
@ -1691,6 +1691,70 @@ UniValue converttopsbt(const JSONRPCRequest& request)
|
||||||
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
|
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniValue utxoupdatepsbt(const JSONRPCRequest& request)
|
||||||
|
{
|
||||||
|
if (request.fHelp || request.params.size() != 1) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
RPCHelpMan{"utxoupdatepsbt",
|
||||||
|
"\nUpdates a PSBT with witness UTXOs retrieved from the UTXO set or the mempool.\n",
|
||||||
|
{
|
||||||
|
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
|
||||||
|
},
|
||||||
|
RPCResult {
|
||||||
|
" \"psbt\" (string) The base64-encoded partially signed transaction with inputs updated\n"
|
||||||
|
},
|
||||||
|
RPCExamples {
|
||||||
|
HelpExampleCli("utxoupdatepsbt", "\"psbt\"")
|
||||||
|
}}.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
RPCTypeCheck(request.params, {UniValue::VSTR}, true);
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch previous transactions (inputs):
|
||||||
|
CCoinsView viewDummy;
|
||||||
|
CCoinsViewCache view(&viewDummy);
|
||||||
|
{
|
||||||
|
LOCK2(cs_main, mempool.cs);
|
||||||
|
CCoinsViewCache &viewChain = *pcoinsTip;
|
||||||
|
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
|
||||||
|
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);
|
||||||
|
|
||||||
|
std::vector<std::vector<unsigned char>> solutions_data;
|
||||||
|
txnouttype which_type = Solver(coin.out.scriptPubKey, solutions_data);
|
||||||
|
if (which_type == TX_WITNESS_V0_SCRIPTHASH || which_type == TX_WITNESS_V0_KEYHASH || which_type == TX_WITNESS_UNKNOWN) {
|
||||||
|
input.witness_utxo = coin.out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ssTx << psbtx;
|
||||||
|
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
|
||||||
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const CRPCCommand commands[] =
|
static const CRPCCommand commands[] =
|
||||||
{ // category name actor (function) argNames
|
{ // category name actor (function) argNames
|
||||||
|
@ -1709,6 +1773,7 @@ static const CRPCCommand commands[] =
|
||||||
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
|
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
|
||||||
{ "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} },
|
{ "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} },
|
||||||
{ "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
|
{ "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
|
||||||
|
{ "rawtransactions", "utxoupdatepsbt", &utxoupdatepsbt, {"psbt"} },
|
||||||
|
|
||||||
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
|
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
|
||||||
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
|
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
|
||||||
|
|
|
@ -20,7 +20,7 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
self.setup_clean_chain = False
|
self.setup_clean_chain = False
|
||||||
self.num_nodes = 3
|
self.num_nodes = 3
|
||||||
# TODO: remove -txindex. Currently required for getrawtransaction call.
|
# TODO: remove -txindex. Currently required for getrawtransaction call.
|
||||||
self.extra_args = [[], ["-txindex"], ["-txindex"]]
|
self.extra_args = [["-txindex"], ["-txindex"], ["-txindex"]]
|
||||||
|
|
||||||
def skip_test_if_missing_module(self):
|
def skip_test_if_missing_module(self):
|
||||||
self.skip_if_no_wallet()
|
self.skip_if_no_wallet()
|
||||||
|
@ -296,5 +296,32 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
# Test decoding error: invalid base64
|
# Test decoding error: invalid base64
|
||||||
assert_raises_rpc_error(-22, "TX decode failed invalid base64", self.nodes[0].decodepsbt, ";definitely not base64;")
|
assert_raises_rpc_error(-22, "TX decode failed invalid base64", self.nodes[0].decodepsbt, ";definitely not base64;")
|
||||||
|
|
||||||
|
# Send to all types of addresses
|
||||||
|
addr1 = self.nodes[1].getnewaddress("", "bech32")
|
||||||
|
txid1 = self.nodes[0].sendtoaddress(addr1, 11)
|
||||||
|
vout1 = find_output(self.nodes[0], txid1, 11)
|
||||||
|
addr2 = self.nodes[1].getnewaddress("", "legacy")
|
||||||
|
txid2 = self.nodes[0].sendtoaddress(addr2, 11)
|
||||||
|
vout2 = find_output(self.nodes[0], txid2, 11)
|
||||||
|
addr3 = self.nodes[1].getnewaddress("", "p2sh-segwit")
|
||||||
|
txid3 = self.nodes[0].sendtoaddress(addr3, 11)
|
||||||
|
vout3 = find_output(self.nodes[0], txid3, 11)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# Update a PSBT with UTXOs from the node
|
||||||
|
# Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness
|
||||||
|
psbt = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1},{"txid":txid2, "vout":vout2},{"txid":txid3, "vout":vout3}], {self.nodes[0].getnewaddress():32.999})
|
||||||
|
decoded = self.nodes[1].decodepsbt(psbt)
|
||||||
|
assert "witness_utxo" not in decoded['inputs'][0] and "non_witness_utxo" not in decoded['inputs'][0]
|
||||||
|
assert "witness_utxo" not in decoded['inputs'][1] and "non_witness_utxo" not in decoded['inputs'][1]
|
||||||
|
assert "witness_utxo" not in decoded['inputs'][2] and "non_witness_utxo" not in decoded['inputs'][2]
|
||||||
|
updated = self.nodes[1].utxoupdatepsbt(psbt)
|
||||||
|
decoded = self.nodes[1].decodepsbt(updated)
|
||||||
|
assert "witness_utxo" in decoded['inputs'][0] and "non_witness_utxo" not in decoded['inputs'][0]
|
||||||
|
assert "witness_utxo" not in decoded['inputs'][1] and "non_witness_utxo" not in decoded['inputs'][1]
|
||||||
|
assert "witness_utxo" not in decoded['inputs'][2] and "non_witness_utxo" not in decoded['inputs'][2]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
PSBTTest().main()
|
PSBTTest().main()
|
||||||
|
|
Loading…
Add table
Reference in a new issue