diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh index 478a347d8c9d..70957cee0269 100755 --- a/ci/test/00_setup_env_native_valgrind.sh +++ b/ci/test/00_setup_env_native_valgrind.sh @@ -9,7 +9,7 @@ export LC_ALL=C.UTF-8 export PACKAGES="valgrind clang llvm libclang-rt-dev python3-zmq libevent-dev bsdmainutils libboost-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev" export USE_VALGRIND=1 export NO_DEPENDS=1 -export TEST_RUNNER_EXTRA="--exclude rpc_bind,feature_bind_extra --timeout-factor=4" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547 +export TEST_RUNNER_EXTRA="--exclude feature_init,rpc_bind,feature_bind_extra --timeout-factor=4" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547 export GOAL="install" # Temporarily pin dwarf 4, until valgrind can understand clang's dwarf 5 export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no CC=clang-19 CXX=clang++-19 CFLAGS='-gdwarf-4' CXXFLAGS='-gdwarf-4'" # TODO enable GUI diff --git a/ci/test/00_setup_env_s390x.sh b/ci/test/00_setup_env_s390x.sh index 2bb4d5e57562..bd343888f923 100755 --- a/ci/test/00_setup_env_s390x.sh +++ b/ci/test/00_setup_env_s390x.sh @@ -19,7 +19,7 @@ fi # Use debian to avoid 404 apt errors export CONTAINER_NAME=ci_s390x export RUN_UNIT_TESTS=true -export TEST_RUNNER_EXTRA="--exclude rpc_bind,feature_bind_extra" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547 +export TEST_RUNNER_EXTRA="--exclude feature_init,rpc_bind,feature_bind_extra" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547 export RUN_FUNCTIONAL_TESTS=true export GOAL="install" export BITCOIN_CONFIG="--enable-reduce-exports" diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index 2d92718ba73f..05ad6e6021c0 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -16,6 +16,7 @@ : []: .onion: + .b32.i2p: The output will be two data structures with the peers in binary format: @@ -52,6 +53,12 @@ def name_to_bip155(addr): return (BIP155Network.TORV2, vchAddr) else: raise ValueError('Invalid onion %s' % vchAddr) + elif addr.endswith('.b32.i2p'): + vchAddr = b32decode(addr[0:-8] + '====', True) + if len(vchAddr) == 32: + return (BIP155Network.I2P, vchAddr) + else: + raise ValueError(f'Invalid I2P {vchAddr}') elif '.' in addr: # IPv4 return (BIP155Network.IPV4, bytes((int(x) for x in addr.split('.')))) elif ':' in addr: # IPv6 or CJDNS diff --git a/contrib/valgrind.supp b/contrib/valgrind.supp index 20e03849e15d..04f13ca9b104 100644 --- a/contrib/valgrind.supp +++ b/contrib/valgrind.supp @@ -13,8 +13,8 @@ # # Note that suppressions may depend on OS and/or library versions. # Tested on: -# * aarch64 (Ubuntu 20.04 system libs, without gui) -# * x86_64 (Ubuntu 18.04 system libs, without gui) +# * aarch64 (Ubuntu 22.04 system libs, clang, without gui) +# * x86_64 (Ubuntu 22.04 system libs, clang, without gui) { Suppress libstdc++ warning - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65434 Memcheck:Leak @@ -91,33 +91,6 @@ ... fun:GetCoin } -{ - Suppress wcsnrtombs glibc SSE4 warning (could be related: https://stroika.atlassian.net/browse/STK-626) - Memcheck:Addr16 - fun:__wcsnlen_sse4_1 - fun:wcsnrtombs -} -{ - Suppress boost warning - Memcheck:Leak - fun:_Znwm - ... - fun:_ZN5boost9unit_test9framework5state17execute_test_treeEmjPKNS2_23random_generator_helperE - fun:_ZN5boost9unit_test9framework3runEmb - fun:_ZN5boost9unit_test14unit_test_mainEPFbvEiPPc - fun:main -} -{ - Suppress boost still reachable memory warning - Memcheck:Leak - match-leak-kinds: reachable - fun:_Znwm - ... - fun:_M_construct_aux - fun:_M_construct - fun:basic_string - fun:path -} { Suppress LogInstance still reachable memory warning Memcheck:Leak diff --git a/doc/bips.md b/doc/bips.md index b2db8e8637fb..4ab4e807b255 100644 --- a/doc/bips.md +++ b/doc/bips.md @@ -38,7 +38,8 @@ Versions and PRs are relevant to Bitcoin's core if not mentioned other. * [`BIP 147`](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki): NULLDUMMY softfork as of **v0.13.1** ([PR 8636](https://github.com/bitcoin/bitcoin/pull/8636) and [PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)), *buried* since **v0.19.0** ([PR #16060](https://github.com/bitcoin/bitcoin/pull/16060)). * [`BIP 152`](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki): Compact block transfer and related optimizations are used as of **v0.13.0** ([PR 8068](https://github.com/bitcoin/bitcoin/pull/8068)). * [`BIP 155`](https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki): The 'addrv2' and 'sendaddrv2' messages which enable relay of Tor V3 addresses (and other networks) are supported as of **v18.0** ([PR 19954](https://github.com/bitcoin/bitcoin/pull/19954)). -* [`BIP 158`](https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki): Compact Block Filters for Light Clients can be indexed as of **Dash Core v18.0** ([PR dash#4314](https://github.com/dashpay/dash/pull/4314), [PR #14121](https://github.com/bitcoin/bitcoin/pull/14121)). +* [`BIP 157`](https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki) + [`158`](https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki): Compact Block Filters for Light Clients can be indexed as of **Dash Core v18.0** ([PR dash#4314](https://github.com/dashpay/dash/pull/4314)), ([PR #14121](https://github.com/bitcoin/bitcoin/pull/14121)) and served to peers on the P2P network as of **v0.21.0** ([PR #16442](https://github.com/bitcoin/bitcoin/pull/16442)). * [`BIP 159`](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki): The `NODE_NETWORK_LIMITED` service bit is signalled as of **v0.16.0** ([PR 11740](https://github.com/bitcoin/bitcoin/pull/11740)), and such nodes are connected to as of **v0.17.0** ([PR 10387](https://github.com/bitcoin/bitcoin/pull/10387)). * [`BIP 174`](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki): RPCs to operate on Partially Signed Bitcoin Transactions (PSBT) are present as of **v18.0** ([PR 13557](https://github.com/bitcoin/bitcoin/pull/13557)). * [`BIP 324`](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki): The v2 transport protocol specified by BIP324 and the associated `NODE_P2P_V2` service bit are supported as of **v22.0**, but off by default ([PR 28331](https://github.com/bitcoin/bitcoin/pull/28331)). On by default as of **v22.1** ([PR 29347](https://github.com/bitcoin/bitcoin/pull/29347)). diff --git a/doc/release-notes-16807.md b/doc/release-notes-16807.md new file mode 100644 index 000000000000..3cdd0b36af5a --- /dev/null +++ b/doc/release-notes-16807.md @@ -0,0 +1,10 @@ +Updated RPCs +------------ + +- The `validateaddress` RPC now returns an `error_locations` array for invalid +addresses, with the indices of invalid character locations in the address (if +known). For example, this will attempt to locate up to two Bech32 errors, and +return their locations if successful. Success and correctness are only guaranteed +if fewer than two substitution errors have been made. +The error message returned in the `error` field now also returns more specific +errors when decoding fails. \ No newline at end of file diff --git a/src/key_io.cpp b/src/key_io.cpp index b90c08bb13dd..0962759f6020 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -39,12 +39,16 @@ class DestinationEncoder std::string operator()(const CNoDestination& no) const { return {}; } }; -CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, std::string& error_str) +CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, std::string& error_str, std::vector* error_locations) { std::vector data; uint160 hash; error_str = ""; - if (DecodeBase58Check(str, data, 21)) { + + // Note this will be false if it is a valid Bech32 address for a different network + bool is_bech32 = (ToLower(str.substr(0, params.Bech32PlatformHRP().size())) == params.Bech32PlatformHRP()); + + if (!is_bech32 && DecodeBase58Check(str, data, 21)) { // base58-encoded Dash addresses. // Public-key-hash-addresses have version 76 (or 140 testnet). // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. @@ -61,12 +65,39 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par return ScriptHash(hash); } - // Set potential error message. - error_str = "Invalid prefix for Base58-encoded address"; + // If the prefix of data matches either the script or pubkey prefix, the length must have been wrong + if ((data.size() >= script_prefix.size() && + std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) || + (data.size() >= pubkey_prefix.size() && + std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin()))) { + error_str = "Invalid length for Base58 address"; + } else { + error_str = "Invalid prefix for Base58-encoded address"; + } + return CNoDestination(); + } else if (!is_bech32) { + // Try Base58 decoding without the checksum, using a much larger max length + if (!DecodeBase58(str, data, 100)) { + error_str = "Not a valid Bech32m or Base58 encoding"; + } else { + error_str = "Invalid checksum or length of Base58 address"; + } + return CNoDestination(); } - // Set error message if address can't be interpreted as Base58. - if (error_str.empty()) error_str = "Invalid address format"; + // Dash has no Bech32m encoding for L1 destinations: a string using the Platform + // HRP can only be a DIP-18 Platform address, which never encodes an L1 destination. + // Decode it anyway to tell the user why exactly it got rejected. + std::string platform_error_str; + const bool is_platform = IsValidPlatformDestination(DecodePlatformDestination(str, params, platform_error_str)); + + // Perform Bech32 error location + auto res = bech32::LocateErrors(str); + error_str = res.first; + if (error_locations) *error_locations = std::move(res.second); + if (error_str.empty()) { + error_str = is_platform ? "This is a Dash Platform address, not a Dash Core address" : platform_error_str; + } return CNoDestination(); } } // namespace @@ -156,9 +187,9 @@ std::string EncodeDestination(const CTxDestination& dest) return std::visit(DestinationEncoder(Params()), dest); } -CTxDestination DecodeDestination(const std::string& str, std::string& error_msg) +CTxDestination DecodeDestination(const std::string& str, std::string& error_msg, std::vector* error_locations) { - return DecodeDestination(str, Params(), error_msg); + return DecodeDestination(str, Params(), error_msg, error_locations); } CTxDestination DecodeDestination(const std::string& str) @@ -170,7 +201,7 @@ CTxDestination DecodeDestination(const std::string& str) bool IsValidDestinationString(const std::string& str, const CChainParams& params) { std::string error_msg; - return IsValidDestination(DecodeDestination(str, params, error_msg)); + return IsValidDestination(DecodeDestination(str, params, error_msg, nullptr)); } bool IsValidDestinationString(const std::string& str) diff --git a/src/key_io.h b/src/key_io.h index 11bbe5cd6fb1..0b12df251f5e 100644 --- a/src/key_io.h +++ b/src/key_io.h @@ -24,7 +24,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); +CTxDestination DecodeDestination(const std::string& str, std::string& error_msg, std::vector* error_locations = nullptr); bool IsValidDestinationString(const std::string& str); bool IsValidDestinationString(const std::string& str, const CChainParams& params); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index edea3b9e3443..f9fb90cb06d8 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -948,7 +948,7 @@ const RPCResult getblock_vin{ {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"}, {RPCResult::Type::STR_HEX, "hex", "The raw public key script bytes, hex-encoded"}, {RPCResult::Type::STR, "address", /*optional=*/true, "The Dash address (only if a well-defined address exists)"}, - {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + {RPCResult::Type::STR, "type", "The output type (e.g. " + GetAllOutputTypes() + ")"}, }}, }}, }}, diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp index 2ad98f709d38..540dca2437dd 100644 --- a/src/rpc/output_script.cpp +++ b/src/rpc/output_script.cpp @@ -39,6 +39,10 @@ static RPCHelpMan validateaddress() {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address"}, {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)", + { + {RPCResult::Type::NUM, "index", "index of a potential error"}, + }}, } }, RPCExamples{ @@ -48,7 +52,8 @@ static RPCHelpMan validateaddress() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { std::string error_msg; - CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg); + std::vector error_locations; + CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg, &error_locations); const bool isValid = IsValidDestination(dest); CHECK_NONFATAL(isValid == error_msg.empty()); @@ -64,6 +69,9 @@ static RPCHelpMan validateaddress() UniValue detail = DescribeAddress(dest); ret.pushKVs(detail); } else { + UniValue error_indices(UniValue::VARR); + for (int i : error_locations) error_indices.push_back(i); + ret.pushKV("error_locations", error_indices); ret.pushKV("error", error_msg); } diff --git a/src/test/fuzz/script_format.cpp b/src/test/fuzz/script_format.cpp index 69c8e7331630..eecfc8960df3 100644 --- a/src/test/fuzz/script_format.cpp +++ b/src/test/fuzz/script_format.cpp @@ -3,7 +3,9 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include +#include #include