From 31dbd5af4863d73c3215355ec5cf85bde2ddc5d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= <joao.paulo.barbosa@gmail.com>
Date: Mon, 15 Jan 2018 16:10:13 +0000
Subject: [PATCH 1/3] [wallet] Add change type to CCoinControl

---
 src/wallet/coincontrol.h | 4 ++++
 src/wallet/wallet.cpp    | 8 ++++----
 src/wallet/wallet.h      | 2 +-
 3 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index e1afa2de0..458e770e0 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -16,7 +16,10 @@
 class CCoinControl
 {
 public:
+    //! Custom change destination, if not set an address is generated
     CTxDestination destChange;
+    //! Custom change type, ignored if destChange is set, defaults to g_change_type
+    OutputType change_type;
     //! If false, allows unselected inputs, but requires all selected inputs be used
     bool fAllowOtherInputs;
     //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria
@@ -40,6 +43,7 @@ public:
     void SetNull()
     {
         destChange = CNoDestination();
+        change_type = g_change_type;
         fAllowOtherInputs = false;
         fAllowWatchOnly = false;
         setSelected.clear();
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 7fb26900e..07a23ce24 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2674,11 +2674,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
     return true;
 }
 
-OutputType CWallet::TransactionChangeType(const std::vector<CRecipient>& vecSend)
+OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend)
 {
     // If -changetype is specified, always use that change type.
-    if (g_change_type != OUTPUT_TYPE_NONE) {
-        return g_change_type;
+    if (change_type != OUTPUT_TYPE_NONE) {
+        return change_type;
     }
 
     // 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
                     return false;
                 }
 
-                const OutputType change_type = TransactionChangeType(vecSend);
+                const OutputType change_type = TransactionChangeType(coin_control.change_type, vecSend);
 
                 LearnRelatedScripts(vchPubKey, change_type);
                 scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, change_type));
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index e8f536634..a4684c293 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -965,7 +965,7 @@ public:
     CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
     CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
 
-    OutputType TransactionChangeType(const std::vector<CRecipient>& vecSend);
+    OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);
 
     /**
      * Insert additional inputs into the transaction by

From 536ddeb173fafbfb7418a34fb5b49a425442d603 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= <joao.paulo.barbosa@gmail.com>
Date: Tue, 16 Jan 2018 00:20:17 +0000
Subject: [PATCH 2/3] [rpc] Add change_type option to fundrawtransaction

---
 src/wallet/rpcwallet.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 6849671dc..ae4525751 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3057,6 +3057,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
                             "   {\n"
                             "     \"changeAddress\"          (string, optional, default pool address) The bitcoin address to receive the change\n"
                             "     \"changePosition\"         (numeric, optional, default random) The index of the change output\n"
+                            "     \"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"
                             "     \"includeWatching\"        (boolean, optional, default false) Also select inputs which are watch only\n"
                             "     \"lockUnspents\"           (boolean, optional, default false) Lock selected unspent outputs\n"
                             "     \"feeRate\"                (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n"
@@ -3122,6 +3123,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
             {
                 {"changeAddress", UniValueType(UniValue::VSTR)},
                 {"changePosition", UniValueType(UniValue::VNUM)},
+                {"change_type", UniValueType(UniValue::VSTR)},
                 {"includeWatching", UniValueType(UniValue::VBOOL)},
                 {"lockUnspents", UniValueType(UniValue::VBOOL)},
                 {"reserveChangeKey", UniValueType(UniValue::VBOOL)}, // DEPRECATED (and ignored), should be removed in 0.16 or so.
@@ -3146,6 +3148,16 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
         if (options.exists("changePosition"))
             changePosition = options["changePosition"].get_int();
 
+        if (options.exists("change_type")) {
+            if (options.exists("changeAddress")) {
+                throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both changeAddress and address_type options");
+            }
+            coinControl.change_type = ParseOutputType(options["change_type"].get_str(), coinControl.change_type);
+            if (coinControl.change_type == OUTPUT_TYPE_NONE) {
+                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown change type '%s'", options["change_type"].get_str()));
+            }
+        }
+
         if (options.exists("includeWatching"))
             coinControl.fAllowWatchOnly = options["includeWatching"].get_bool();
 

From 16f6f59dcf0dc8868927d6565c2bbfa40a74ba81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= <joao.paulo.barbosa@gmail.com>
Date: Wed, 24 Jan 2018 15:28:15 +0000
Subject: [PATCH 3/3] [qa] Test fundrawtransaction with change_type option

---
 test/functional/fundrawtransaction.py | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py
index b3d654922..5f7a0586e 100755
--- a/test/functional/fundrawtransaction.py
+++ b/test/functional/fundrawtransaction.py
@@ -212,6 +212,19 @@ class RawTransactionsTest(BitcoinTestFramework):
         out = dec_tx['vout'][0]
         assert_equal(change, out['scriptPubKey']['addresses'][0])
 
+        #########################################################
+        # test a fundrawtransaction with a provided change type #
+        #########################################################
+        utx = get_unspent(self.nodes[2].listunspent(), 5)
+
+        inputs  = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
+        outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
+        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
+        assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[2].fundrawtransaction, rawtx, {'change_type': None})
+        assert_raises_rpc_error(-5, "Unknown change type", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''})
+        rawtx = self.nodes[2].fundrawtransaction(rawtx, {'change_type': 'bech32'})
+        tx  = self.nodes[2].decoderawtransaction(rawtx['hex'])
+        assert_equal('witness_v0_keyhash', tx['vout'][rawtx['changepos']]['scriptPubKey']['type'])
 
         #########################################################################
         # test a fundrawtransaction with a VIN smaller than the required amount #