diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -6,6 +6,8 @@ - The RPC `getrpcinfo` returns runtime details of the RPC server. At the moment it returns the active commands and the corresponding execution time. +- `ischange` field of boolean type that shows if an address was used for change +output was added to `getaddressinfo` method response. New RPC methods diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4902,6 +4902,8 @@ "watchonly\n" " \"isscript\" : true|false, (boolean) If the key is a " "script\n" + " \"ischange\" : true|false, (boolean) If the address was " + "used for change output\n" " \"script\" : \"type\" (string, optional) The output " "script type. Only if \"isscript\" is true and the redeemscript is " "known. Possible types: nonstandard, pubkey, pubkeyhash, " @@ -4992,6 +4994,7 @@ ret.pushKV("account", pwallet->mapAddressBook[dest].name); } } + ret.pushKV("ischange", pwallet->IsChange(scriptPubKey)); const CKeyMetadata *meta = nullptr; CKeyID key_id = GetKeyForDestination(*pwallet, dest); if (!key_id.IsNull()) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1294,6 +1294,7 @@ isminetype IsMine(const CTxOut &txout) const; Amount GetCredit(const CTxOut &txout, const isminefilter &filter) const; bool IsChange(const CTxOut &txout) const; + bool IsChange(const CScript &script) const; Amount GetChange(const CTxOut &txout) const; bool IsMine(const CTransaction &tx) const; /** should probably be renamed to IsRelevantToMe */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1487,6 +1487,10 @@ } bool CWallet::IsChange(const CTxOut &txout) const { + return IsChange(txout.scriptPubKey); +} + +bool CWallet::IsChange(const CScript &script) const { // TODO: fix handling of 'change' outputs. The assumption is that any // payment to a script that is ours, but is not in the address book is // change. That assumption is likely to break when we implement @@ -1495,9 +1499,9 @@ // outputs are 'the send' and which are 'the change' will need to be // implemented (maybe extend CWalletTx to remember which output, if any, was // change). - if (::IsMine(*this, txout.scriptPubKey)) { + if (::IsMine(*this, script)) { CTxDestination address; - if (!ExtractDestination(txout.scriptPubKey, address)) { + if (!ExtractDestination(script, address)) { return true; } diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -515,8 +515,8 @@ assert_equal(total_txs, len( self.nodes[0].listtransactions("*", 99999))) - # Test getaddressinfo. Note that these addresses are taken from - # disablewallet.py + # Test getaddressinfo on external address. Note that these addresses + # are taken from disablewallet.py assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy") address_info = self.nodes[0].getaddressinfo( @@ -528,6 +528,24 @@ assert not address_info["ismine"] assert not address_info["iswatchonly"] assert not address_info["isscript"] + assert not address_info["ischange"] + + # Test getaddressinfo 'ischange' field on change address. + self.nodes[0].generate(1) + destination = self.nodes[1].getnewaddress() + txid = self.nodes[0].sendtoaddress(destination, 0.123) + tx = self.nodes[0].decoderawtransaction( + self.nodes[0].getrawtransaction(txid)) + output_addresses = [vout['scriptPubKey']['addresses'][0] + for vout in tx["vout"]] + assert len(output_addresses) > 1 + for address in output_addresses: + ischange = self.nodes[0].getaddressinfo(address)['ischange'] + assert_equal(ischange, address != destination) + if ischange: + change = address + self.nodes[0].setlabel(change, 'foobar') + assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False) if __name__ == '__main__':