Skip to content

Commit 7abb0f0

Browse files
committed
Merge #12194: Add change type option to fundrawtransaction
16f6f59 [qa] Test fundrawtransaction with change_type option (João Barbosa) 536ddeb [rpc] Add change_type option to fundrawtransaction (João Barbosa) 31dbd5a [wallet] Add change type to CCoinControl (João Barbosa) Pull request description: Adds a new option `change_type` to `fundrawtransaction` RPC. This is useful to override the node `-changetype` argument. The new option is exclusive to `changeAddress` option, setting both raises a RPC error. See also #11403, #12119. Tree-SHA512: 654686444f6125e37015a62f167064d54ec335701534988447be4687fa5ef9c7980a8a07cc0a03fff6ea6c4c1abf0f77a8843d535c4f3fe0bf93f968a4e676e6
2 parents eadb2da + 16f6f59 commit 7abb0f0

File tree

5 files changed

+34
-5
lines changed

5 files changed

+34
-5
lines changed

src/wallet/coincontrol.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
class CCoinControl
1717
{
1818
public:
19+
//! Custom change destination, if not set an address is generated
1920
CTxDestination destChange;
21+
//! Custom change type, ignored if destChange is set, defaults to g_change_type
22+
OutputType change_type;
2023
//! If false, allows unselected inputs, but requires all selected inputs be used
2124
bool fAllowOtherInputs;
2225
//! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria
@@ -40,6 +43,7 @@ class CCoinControl
4043
void SetNull()
4144
{
4245
destChange = CNoDestination();
46+
change_type = g_change_type;
4347
fAllowOtherInputs = false;
4448
fAllowWatchOnly = false;
4549
setSelected.clear();

src/wallet/rpcwallet.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3065,6 +3065,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
30653065
" {\n"
30663066
" \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
30673067
" \"changePosition\" (numeric, optional, default random) The index of the change output\n"
3068+
" \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
30683069
" \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
30693070
" \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n"
30703071
" \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n"
@@ -3130,6 +3131,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
31303131
{
31313132
{"changeAddress", UniValueType(UniValue::VSTR)},
31323133
{"changePosition", UniValueType(UniValue::VNUM)},
3134+
{"change_type", UniValueType(UniValue::VSTR)},
31333135
{"includeWatching", UniValueType(UniValue::VBOOL)},
31343136
{"lockUnspents", UniValueType(UniValue::VBOOL)},
31353137
{"reserveChangeKey", UniValueType(UniValue::VBOOL)}, // DEPRECATED (and ignored), should be removed in 0.16 or so.
@@ -3154,6 +3156,16 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
31543156
if (options.exists("changePosition"))
31553157
changePosition = options["changePosition"].get_int();
31563158

3159+
if (options.exists("change_type")) {
3160+
if (options.exists("changeAddress")) {
3161+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both changeAddress and address_type options");
3162+
}
3163+
coinControl.change_type = ParseOutputType(options["change_type"].get_str(), coinControl.change_type);
3164+
if (coinControl.change_type == OUTPUT_TYPE_NONE) {
3165+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown change type '%s'", options["change_type"].get_str()));
3166+
}
3167+
}
3168+
31573169
if (options.exists("includeWatching"))
31583170
coinControl.fAllowWatchOnly = options["includeWatching"].get_bool();
31593171

src/wallet/wallet.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2674,11 +2674,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
26742674
return true;
26752675
}
26762676

2677-
OutputType CWallet::TransactionChangeType(const std::vector<CRecipient>& vecSend)
2677+
OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend)
26782678
{
26792679
// If -changetype is specified, always use that change type.
2680-
if (g_change_type != OUTPUT_TYPE_NONE) {
2681-
return g_change_type;
2680+
if (change_type != OUTPUT_TYPE_NONE) {
2681+
return change_type;
26822682
}
26832683

26842684
// if g_address_type is legacy, use legacy address as change (even
@@ -2797,7 +2797,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
27972797
return false;
27982798
}
27992799

2800-
const OutputType change_type = TransactionChangeType(vecSend);
2800+
const OutputType change_type = TransactionChangeType(coin_control.change_type, vecSend);
28012801

28022802
LearnRelatedScripts(vchPubKey, change_type);
28032803
scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, change_type));

src/wallet/wallet.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
965965
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
966966
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
967967

968-
OutputType TransactionChangeType(const std::vector<CRecipient>& vecSend);
968+
OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);
969969

970970
/**
971971
* Insert additional inputs into the transaction by

test/functional/fundrawtransaction.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,19 @@ def run_test(self):
212212
out = dec_tx['vout'][0]
213213
assert_equal(change, out['scriptPubKey']['addresses'][0])
214214

215+
#########################################################
216+
# test a fundrawtransaction with a provided change type #
217+
#########################################################
218+
utx = get_unspent(self.nodes[2].listunspent(), 5)
219+
220+
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
221+
outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
222+
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
223+
assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[2].fundrawtransaction, rawtx, {'change_type': None})
224+
assert_raises_rpc_error(-5, "Unknown change type", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''})
225+
rawtx = self.nodes[2].fundrawtransaction(rawtx, {'change_type': 'bech32'})
226+
tx = self.nodes[2].decoderawtransaction(rawtx['hex'])
227+
assert_equal('witness_v0_keyhash', tx['vout'][rawtx['changepos']]['scriptPubKey']['type'])
215228

216229
#########################################################################
217230
# test a fundrawtransaction with a VIN smaller than the required amount #

0 commit comments

Comments
 (0)