From e8d0420c370b8788665f8d6589382f8ac29de4ac Mon Sep 17 00:00:00 2001 From: pasta Date: Tue, 11 Aug 2026 10:26:39 -0500 Subject: [PATCH] fix(rpc): describe Platform addresses consistently in validateaddress and getaddressinfo validateaddress deliberately omitted scriptPubKey for DIP-18 Platform addresses while getaddressinfo reported the derived credit output script along with ismine and isscript, so the two RPCs told inconsistent stories about the same address. Align them: validateaddress now also reports the credit output script an asset lock would carry for the address (plus isscript), with help text spelling out the semantics, and getaddressinfo's help clarifies that ismine/solvable refer to that script rather than to Platform identity ownership. --- src/rpc/output_script.cpp | 14 ++++++++++++-- src/wallet/rpc/addresses.cpp | 3 ++- test/functional/rpc_invalid_address_message.py | 18 ++++++++++++------ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp index c952366479f5..71b5eeb3dc56 100644 --- a/src/rpc/output_script.cpp +++ b/src/rpc/output_script.cpp @@ -27,7 +27,9 @@ static RPCHelpMan validateaddress() { return RPCHelpMan{ "validateaddress", - "\nReturn information about the given Dash address.\n", + "\nReturn information about the given Dash address.\n" + "A DIP-18 Dash Platform address is described against the credit output script an asset\n" + "lock would carry for it.\n", { {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Dash address to validate"}, }, @@ -37,7 +39,7 @@ static RPCHelpMan validateaddress() {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"}, {RPCResult::Type::BOOL, "isplatform", /*optional=*/true, "If the address is a DIP-18 Dash Platform address"}, {RPCResult::Type::STR, "address", /*optional=*/true, "The Dash address validated"}, - {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address"}, + {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address. For a DIP-18 Platform address, the credit output script an asset lock would carry for it"}, {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script"}, {RPCResult::Type::STR, "error", /*optional=*/true, "Error message, if any"}, {RPCResult::Type::ARR, "error_locations", /*optional=*/true, "Indices of likely error locations in address, if known (e.g. Bech32 errors)", @@ -73,6 +75,14 @@ static RPCHelpMan validateaddress() ret.pushKV("isvalid", true); ret.pushKV("isplatform", true); ret.pushKV("address", EncodePlatformDestination(platform_dest)); + + const CScript credit_output_script = GetScriptForPlatformDestination(platform_dest); + ret.pushKV("scriptPubKey", HexStr(credit_output_script)); + + CTxDestination l1_dest; + if (ExtractDestination(credit_output_script, l1_dest)) { + ret.pushKVs(DescribeAddress(l1_dest)); + } } else { ret.pushKV("isvalid", false); UniValue error_indices(UniValue::VARR); diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp index 0a6ce8bdf526..6920c5749a3b 100644 --- a/src/wallet/rpc/addresses.cpp +++ b/src/wallet/rpc/addresses.cpp @@ -422,7 +422,8 @@ RPCHelpMan getaddressinfo() "\nReturn information about the given Dash address.\n" "Some of the information will only be present if the address is in the active wallet.\n" "A DIP-18 Dash Platform address is described against the credit output script an asset\n" - "lock would carry for it.\n", + "lock would carry for it; fields like \"ismine\" and \"solvable\" then refer to that\n" + "script, not to ownership of a Platform identity.\n", { {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Dash address for which to get information."}, }, diff --git a/test/functional/rpc_invalid_address_message.py b/test/functional/rpc_invalid_address_message.py index d8aa662703ec..15c6ca47af1d 100755 --- a/test/functional/rpc_invalid_address_message.py +++ b/test/functional/rpc_invalid_address_message.py @@ -8,6 +8,7 @@ from test_framework.script_util import ( keyhash_to_p2pkh_script, + scripthash_to_p2sh_script, ) from test_framework.segwit_addr import ( DIP18_TYPE_P2PKH, @@ -22,6 +23,7 @@ PLATFORM_HRP = 'tdash' PLATFORM_KEYHASH = bytes.fromhex('f7da0a2b5cbd4ff6bb2c4d89b67d2f3ffeec0525') +PLATFORM_SCRIPTHASH = bytes.fromhex('43fa183cf3fb6e9e7dc62b692aeb4fc8d8045636') def platform_address(encoding, type_byte, payload): @@ -79,13 +81,15 @@ def check_invalid(self, addr, error_str, error_locations=None): else: assert_equal(res['error_locations'], []) - def check_platform(self, addr, normalized): + def check_platform(self, addr, normalized, script, is_script): res = self.nodes[0].validateaddress(addr) assert_equal(res['isvalid'], True) assert_equal(res['isplatform'], True) assert_equal(res['address'], normalized) - # A Platform address has no layer-1 output script - assert 'scriptPubKey' not in res + # Described against the credit output script an asset lock would carry + # for it, consistent with getaddressinfo + assert_equal(res['scriptPubKey'], script.hex()) + assert_equal(res['isscript'], is_script) assert 'error' not in res assert 'error_locations' not in res @@ -106,9 +110,11 @@ def test_validateaddress(self): self.check_invalid(BECH32_INVALID_SIZE, 'Invalid Platform address payload length') # Valid Bech32m: DIP-18 Platform addresses, reported as such and normalized to lower case - self.check_platform(BECH32_VALID, BECH32_VALID) - self.check_platform(BECH32_VALID_CAPITALS, BECH32_VALID) - self.check_platform(BECH32_VALID_P2SH, BECH32_VALID_P2SH) + p2pkh_script = keyhash_to_p2pkh_script(PLATFORM_KEYHASH) + p2sh_script = scripthash_to_p2sh_script(PLATFORM_SCRIPTHASH) + self.check_platform(BECH32_VALID, BECH32_VALID, p2pkh_script, False) + self.check_platform(BECH32_VALID_CAPITALS, BECH32_VALID, p2pkh_script, False) + self.check_platform(BECH32_VALID_P2SH, BECH32_VALID_P2SH, p2sh_script, True) # Invalid Base58 self.check_invalid(BASE58_INVALID_PREFIX, 'Invalid prefix for Base58-encoded address')