rpc: add "ischange: true" in wallet gettransaction decoded tx output

This commit is contained in:
Matthew Zipkin
2025-05-15 11:32:04 -04:00
parent c779ee3a40
commit d633db5416
5 changed files with 25 additions and 6 deletions

View File

@@ -21,6 +21,7 @@ class SigningProvider;
class uint256;
class UniValue;
class CTxUndo;
class CTxOut;
/**
* Verbose level for block's transaction
@@ -46,6 +47,6 @@ std::string FormatScript(const CScript& script);
std::string EncodeHexTx(const CTransaction& tx);
std::string SighashToStr(unsigned char sighash_type);
void ScriptToUniv(const CScript& script, UniValue& out, bool include_hex = true, bool include_address = false, const SigningProvider* provider = nullptr);
void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS);
void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS, std::function<bool(const CTxOut&)> is_change_func = {});
#endif // BITCOIN_CORE_IO_H

View File

@@ -168,7 +168,7 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_hex, bool i
out.pushKV("type", GetTxnOutputType(type));
}
void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex, const CTxUndo* txundo, TxVerbosity verbosity)
void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex, const CTxUndo* txundo, TxVerbosity verbosity, std::function<bool(const CTxOut&)> is_change_func)
{
CHECK_NONFATAL(verbosity >= TxVerbosity::SHOW_DETAILS);
@@ -243,6 +243,11 @@ void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry
UniValue o(UniValue::VOBJ);
ScriptToUniv(txout.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
out.pushKV("scriptPubKey", std::move(o));
if (is_change_func && is_change_func(txout)) {
out.pushKV("ischange", true);
}
vout.push_back(std::move(out));
if (have_undo) {

View File

@@ -118,6 +118,7 @@ static std::vector<RPCResult> DecodeTxDoc(const std::string& txid_field_doc)
{RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},
{RPCResult::Type::NUM, "n", "index"},
{RPCResult::Type::OBJ, "scriptPubKey", "", ScriptPubKeyDoc()},
{RPCResult::Type::BOOL, "ischange", /*optional=*/true, "Output script is change (only if wallet transaction and true for selected rpcwallet)"},
}},
}},
};

View File

@@ -796,7 +796,16 @@ RPCHelpMan gettransaction()
if (verbose) {
UniValue decoded(UniValue::VOBJ);
TxToUniv(*wtx.tx, /*block_hash=*/uint256(), /*entry=*/decoded, /*include_hex=*/false);
TxToUniv(*wtx.tx,
/*block_hash=*/uint256(),
/*entry=*/decoded,
/*include_hex=*/false,
/*txundo=*/nullptr,
/*verbosity=*/TxVerbosity::SHOW_DETAILS,
/*is_change_func=*/[&pwallet](const CTxOut& txout) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
AssertLockHeld(pwallet->cs_wallet);
return OutputIsChange(*pwallet, txout);
});
entry.pushKV("decoded", std::move(decoded));
}

View File

@@ -542,13 +542,16 @@ class WalletTest(BitcoinTestFramework):
destination = self.nodes[1].getnewaddress()
txid = self.nodes[0].sendtoaddress(destination, 0.123)
tx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded']
output_addresses = [vout['scriptPubKey']['address'] for vout in tx["vout"]]
assert len(output_addresses) > 1
for address in output_addresses:
assert len(tx["vout"]) > 1
for vout in tx["vout"]:
address = vout['scriptPubKey']['address']
ischange = self.nodes[0].getaddressinfo(address)['ischange']
assert_equal(ischange, address != destination)
if ischange:
change = address
assert vout["ischange"]
else:
assert "ischange" not in vout
self.nodes[0].setlabel(change, 'foobar')
assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False)