aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSamuel Dobson <dobsonsa68@gmail.com>2021-01-26 12:38:38 +1300
committerSamuel Dobson <dobsonsa68@gmail.com>2021-01-26 13:15:13 +1300
commit4b15ffe9913e5f1e4d7a7922ddd7ef83e5b091d3 (patch)
tree04ba017451b2ffba114a4600a76a20ed15846cf7 /src
parent52d84a45e2fa3def71853cd31d5582ae31ea85d3 (diff)
parent8f0b64fb513e8c6cdd1f115856100a4ef5afe23e (diff)
Merge #20832: rpc: Better error messages for invalid addresses
8f0b64fb513e8c6cdd1f115856100a4ef5afe23e Better error messages for invalid addresses (Bezdrighin) Pull request description: This PR addresses #20809. We add more detailed error messages in case an invalid address is provided inside the 'validateaddress' and 'getaddressinfo' RPC calls. This also covers the case when a user provides an address from a wrong network. We also add a functional test to test the new error messages. ACKs for top commit: kristapsk: ACK 8f0b64fb513e8c6cdd1f115856100a4ef5afe23e meshcollider: Code review ACK 8f0b64fb513e8c6cdd1f115856100a4ef5afe23e Tree-SHA512: ca0f806ab573e96b79e98d9f8c810b81fa99c638d9b5e4d99dc18c8bd2568e6a802ec305fdfb2983574a97a19a46fd53b77645f8078fb77e9deb24ad2a22cf93
Diffstat (limited to 'src')
-rw-r--r--src/key_io.cpp46
-rw-r--r--src/key_io.h1
-rw-r--r--src/rpc/misc.cpp15
-rw-r--r--src/wallet/rpcwallet.cpp12
4 files changed, 61 insertions, 13 deletions
diff --git a/src/key_io.cpp b/src/key_io.cpp
index a270ede864..e27673fd16 100644
--- a/src/key_io.cpp
+++ b/src/key_io.cpp
@@ -12,6 +12,9 @@
#include <assert.h>
#include <string.h>
+/// Maximum witness length for Bech32 addresses.
+static constexpr std::size_t BECH32_WITNESS_PROG_MAX_LEN = 40;
+
namespace {
class DestinationEncoder
{
@@ -65,10 +68,11 @@ public:
std::string operator()(const CNoDestination& no) const { return {}; }
};
-CTxDestination DecodeDestination(const std::string& str, const CChainParams& params)
+CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, std::string& error_str)
{
std::vector<unsigned char> data;
uint160 hash;
+ error_str = "";
if (DecodeBase58Check(str, data, 21)) {
// base58-encoded Bitcoin addresses.
// Public-key-hash-addresses have version 0 (or 111 testnet).
@@ -85,10 +89,21 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
return ScriptHash(hash);
}
+
+ // Set potential error message.
+ // This message may be changed if the address can also be interpreted as a Bech32 address.
+ error_str = "Invalid prefix for Base58-encoded address";
}
data.clear();
auto bech = bech32::Decode(str);
- if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) {
+ if (bech.second.size() > 0) {
+ error_str = "";
+
+ if (bech.first != params.Bech32HRP()) {
+ error_str = "Invalid prefix for Bech32 address";
+ return CNoDestination();
+ }
+
// Bech32 decoding
int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16)
// The rest of the symbols are converted witness program bytes.
@@ -109,11 +124,21 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
return scriptid;
}
}
+
+ error_str = "Invalid Bech32 v0 address data size";
+ return CNoDestination();
+ }
+
+ if (version > 16) {
+ error_str = "Invalid Bech32 address witness version";
return CNoDestination();
}
- if (version > 16 || data.size() < 2 || data.size() > 40) {
+
+ if (data.size() < 2 || data.size() > BECH32_WITNESS_PROG_MAX_LEN) {
+ error_str = "Invalid Bech32 address data size";
return CNoDestination();
}
+
WitnessUnknown unk;
unk.version = version;
std::copy(data.begin(), data.end(), unk.program);
@@ -121,6 +146,10 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
return unk;
}
}
+
+ // Set error message if address can't be interpreted as Base58 or Bech32.
+ if (error_str.empty()) error_str = "Invalid address format";
+
return CNoDestination();
}
} // namespace
@@ -208,14 +237,21 @@ std::string EncodeDestination(const CTxDestination& dest)
return std::visit(DestinationEncoder(Params()), dest);
}
+CTxDestination DecodeDestination(const std::string& str, std::string& error_msg)
+{
+ return DecodeDestination(str, Params(), error_msg);
+}
+
CTxDestination DecodeDestination(const std::string& str)
{
- return DecodeDestination(str, Params());
+ std::string error_msg;
+ return DecodeDestination(str, error_msg);
}
bool IsValidDestinationString(const std::string& str, const CChainParams& params)
{
- return IsValidDestination(DecodeDestination(str, params));
+ std::string error_msg;
+ return IsValidDestination(DecodeDestination(str, params, error_msg));
}
bool IsValidDestinationString(const std::string& str)
diff --git a/src/key_io.h b/src/key_io.h
index d80c08f49c..bd81f7847e 100644
--- a/src/key_io.h
+++ b/src/key_io.h
@@ -23,6 +23,7 @@ std::string EncodeExtPubKey(const CExtPubKey& extpubkey);
std::string EncodeDestination(const CTxDestination& dest);
CTxDestination DecodeDestination(const std::string& str);
+CTxDestination DecodeDestination(const std::string& str, std::string& error_msg);
bool IsValidDestinationString(const std::string& str);
bool IsValidDestinationString(const std::string& str, const CChainParams& params);
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index b3102a236d..215e48ca26 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -39,13 +39,14 @@ static RPCHelpMan validateaddress()
RPCResult{
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not. If not, this is the only property returned."},
+ {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"},
{RPCResult::Type::STR, "address", "The bitcoin address validated"},
{RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address"},
{RPCResult::Type::BOOL, "isscript", "If the key is a script"},
{RPCResult::Type::BOOL, "iswitness", "If the address is a witness address"},
{RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program"},
{RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program"},
+ {RPCResult::Type::STR, "error", /* optional */ true, "Error message, if any"},
}
},
RPCExamples{
@@ -54,13 +55,14 @@ static RPCHelpMan validateaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- CTxDestination dest = DecodeDestination(request.params[0].get_str());
- bool isValid = IsValidDestination(dest);
+ std::string error_msg;
+ CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
+ const bool isValid = IsValidDestination(dest);
+ CHECK_NONFATAL(isValid == error_msg.empty());
UniValue ret(UniValue::VOBJ);
ret.pushKV("isvalid", isValid);
- if (isValid)
- {
+ if (isValid) {
std::string currentAddress = EncodeDestination(dest);
ret.pushKV("address", currentAddress);
@@ -69,7 +71,10 @@ static RPCHelpMan validateaddress()
UniValue detail = DescribeAddress(dest);
ret.pushKVs(detail);
+ } else {
+ ret.pushKV("error", error_msg);
}
+
return ret;
},
};
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 9db327c913..b865130642 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3820,13 +3820,19 @@ RPCHelpMan getaddressinfo()
LOCK(pwallet->cs_wallet);
- UniValue ret(UniValue::VOBJ);
- CTxDestination dest = DecodeDestination(request.params[0].get_str());
+ std::string error_msg;
+ CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
+
// Make sure the destination is valid
if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
+ // Set generic error message in case 'DecodeDestination' didn't set it
+ if (error_msg.empty()) error_msg = "Invalid address";
+
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error_msg);
}
+ UniValue ret(UniValue::VOBJ);
+
std::string currentAddress = EncodeDestination(dest);
ret.pushKV("address", currentAddress);