mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-08 10:31:50 -05:00
allow fundtx rpcs to work with external inputs
This commit is contained in:
parent
d5cfb864ae
commit
38f5642ccc
1 changed files with 109 additions and 3 deletions
|
@ -43,6 +43,7 @@
|
|||
|
||||
#include <univalue.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
using interfaces::FoundBlock;
|
||||
|
||||
|
@ -3213,6 +3214,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
|
|||
{"fee_rate", UniValueType()}, // will be checked by AmountFromValue() in SetFeeEstimateMode()
|
||||
{"feeRate", UniValueType()}, // will be checked by AmountFromValue() below
|
||||
{"psbt", UniValueType(UniValue::VBOOL)},
|
||||
{"solving_data", UniValueType(UniValue::VOBJ)},
|
||||
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
|
||||
{"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
|
||||
{"replaceable", UniValueType(UniValue::VBOOL)},
|
||||
|
@ -3289,6 +3291,54 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
|
|||
coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet);
|
||||
}
|
||||
|
||||
if (options.exists("solving_data")) {
|
||||
UniValue solving_data = options["solving_data"].get_obj();
|
||||
if (solving_data.exists("pubkeys")) {
|
||||
for (const UniValue& pk_univ : solving_data["pubkeys"].get_array().getValues()) {
|
||||
const std::string& pk_str = pk_univ.get_str();
|
||||
if (!IsHex(pk_str)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not hex", pk_str));
|
||||
}
|
||||
const std::vector<unsigned char> data(ParseHex(pk_str));
|
||||
CPubKey pubkey(data.begin(), data.end());
|
||||
if (!pubkey.IsFullyValid()) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not a valid public key", pk_str));
|
||||
}
|
||||
coinControl.m_external_provider.pubkeys.emplace(pubkey.GetID(), pubkey);
|
||||
// Add witness script for pubkeys
|
||||
const CScript wit_script = GetScriptForDestination(WitnessV0KeyHash(pubkey));
|
||||
coinControl.m_external_provider.scripts.emplace(CScriptID(wit_script), wit_script);
|
||||
}
|
||||
}
|
||||
|
||||
if (solving_data.exists("scripts")) {
|
||||
for (const UniValue& script_univ : solving_data["scripts"].get_array().getValues()) {
|
||||
const std::string& script_str = script_univ.get_str();
|
||||
if (!IsHex(script_str)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not hex", script_str));
|
||||
}
|
||||
std::vector<unsigned char> script_data(ParseHex(script_str));
|
||||
const CScript script(script_data.begin(), script_data.end());
|
||||
coinControl.m_external_provider.scripts.emplace(CScriptID(script), script);
|
||||
}
|
||||
}
|
||||
|
||||
if (solving_data.exists("descriptors")) {
|
||||
for (const UniValue& desc_univ : solving_data["descriptors"].get_array().getValues()) {
|
||||
const std::string& desc_str = desc_univ.get_str();
|
||||
FlatSigningProvider desc_out;
|
||||
std::string error;
|
||||
std::vector<CScript> scripts_temp;
|
||||
std::unique_ptr<Descriptor> desc = Parse(desc_str, desc_out, error, true);
|
||||
if (!desc) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unable to parse descriptor '%s': %s", desc_str, error));
|
||||
}
|
||||
desc->Expand(0, desc_out, scripts_temp, desc_out);
|
||||
coinControl.m_external_provider = Merge(coinControl.m_external_provider, desc_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.vout.size() == 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
|
||||
|
||||
|
@ -3306,6 +3356,19 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
|
|||
setSubtractFeeFromOutputs.insert(pos);
|
||||
}
|
||||
|
||||
// Fetch specified UTXOs from the UTXO set to get the scriptPubKeys and values of the outputs being selected
|
||||
// and to match with the given solving_data. Only used for non-wallet outputs.
|
||||
std::map<COutPoint, Coin> coins;
|
||||
for (const CTxIn& txin : tx.vin) {
|
||||
coins[txin.prevout]; // Create empty map entry keyed by prevout.
|
||||
}
|
||||
wallet.chain().findCoins(coins);
|
||||
for (const auto& coin : coins) {
|
||||
if (!coin.second.out.IsNull()) {
|
||||
coinControl.Select(coin.first, coin.second.out);
|
||||
}
|
||||
}
|
||||
|
||||
bilingual_str error;
|
||||
|
||||
if (!FundTransaction(wallet, tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
|
||||
|
@ -3321,8 +3384,9 @@ static RPCHelpMan fundrawtransaction()
|
|||
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
|
||||
"Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
|
||||
"The inputs added will not be signed, use signrawtransactionwithkey\n"
|
||||
" or signrawtransactionwithwallet for that.\n"
|
||||
"Note that all existing inputs must have their previous output transaction be in the wallet.\n"
|
||||
"or signrawtransactionwithwallet for that.\n"
|
||||
"All existing inputs must either have their previous output transaction be in the wallet\n"
|
||||
"or be in the UTXO set. Solving data must be provided for non-wallet inputs.\n"
|
||||
"Note that all inputs selected must be of standard form and P2SH scripts must be\n"
|
||||
"in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
|
||||
"You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
|
||||
|
@ -3357,6 +3421,26 @@ static RPCHelpMan fundrawtransaction()
|
|||
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
|
||||
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
|
||||
" \"" + FeeModes("\"\n\"") + "\""},
|
||||
{"solving_data", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "Keys and scripts needed for producing a final transaction with a dummy signature.\n"
|
||||
"Used for fee estimation during coin selection.",
|
||||
{
|
||||
{"pubkeys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Public keys involved in this transaction.",
|
||||
{
|
||||
{"pubkey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A public key"},
|
||||
},
|
||||
},
|
||||
{"scripts", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Scripts involved in this transaction.",
|
||||
{
|
||||
{"script", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A script"},
|
||||
},
|
||||
},
|
||||
{"descriptors", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Descriptors that provide solving data for this transaction.",
|
||||
{
|
||||
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "A descriptor"},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"options"},
|
||||
{"iswitness", RPCArg::Type::BOOL, RPCArg::DefaultHint{"depends on heuristic tests"}, "Whether the transaction hex is a serialized witness transaction.\n"
|
||||
|
@ -4489,7 +4573,9 @@ static RPCHelpMan walletcreatefundedpsbt()
|
|||
{
|
||||
return RPCHelpMan{"walletcreatefundedpsbt",
|
||||
"\nCreates and funds a transaction in the Partially Signed Transaction format.\n"
|
||||
"Implements the Creator and Updater roles.\n",
|
||||
"Implements the Creator and Updater roles.\n"
|
||||
"All existing inputs must either have their previous output transaction be in the wallet\n"
|
||||
"or be in the UTXO set. Solving data must be provided for non-wallet inputs.\n",
|
||||
{
|
||||
{"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Leave empty to add inputs automatically. See add_inputs option.",
|
||||
{
|
||||
|
@ -4546,6 +4632,26 @@ static RPCHelpMan walletcreatefundedpsbt()
|
|||
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
|
||||
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
|
||||
" \"" + FeeModes("\"\n\"") + "\""},
|
||||
{"solving_data", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "Keys and scripts needed for producing a final transaction with a dummy signature.\n"
|
||||
"Used for fee estimation during coin selection.",
|
||||
{
|
||||
{"pubkeys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Public keys involved in this transaction.",
|
||||
{
|
||||
{"pubkey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A public key"},
|
||||
},
|
||||
},
|
||||
{"scripts", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Scripts involved in this transaction.",
|
||||
{
|
||||
{"script", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A script"},
|
||||
},
|
||||
},
|
||||
{"descriptors", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Descriptors that provide solving data for this transaction.",
|
||||
{
|
||||
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "A descriptor"},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"options"},
|
||||
{"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
|
||||
|
|
Loading…
Add table
Reference in a new issue