Remove legacy `Parse(U)Int*` #32520

pull maflcko wants to merge 17 commits into bitcoin:master from maflcko:2504-int-parsing changing 18 files +132 −395
  1. maflcko commented at 8:06 PM on May 15, 2025: member

    The legacy int parsing is problematic, because it accepts the + sign for unsigned integers. In all cases this is either:

    • Useless, because the + sign was already rejected.
    • Erroneous and inconsistent, when third party parsers reject it. (C.f. #32365)
    • Confusing, because the + sign is neither documented, nor can it be assumed to be present.

    Fix all issues by removing the legacy int parsing.

  2. refactor: Use ToIntegral in ParseHDKeypath
    ToIntegral<uint32_t> only accepts numbers, so just use that to replace
    the equivalent but more verbose way with find_first_not_of+ParseUInt32.
    fa23ed7fc2
  3. refactor: Use ToIntegral in CreateFromDump
    The unsigned version is never serialized with a `+` prefix, so the
    stricter ToIntegral can be used.
    fa98041325
  4. cli: Reject + sign in -netinfo level parsing
    It would be confusing to specify the sign for an unsigned value here, so
    reject it.
    fa9c45577d
  5. DrahtBot commented at 8:06 PM on May 15, 2025: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32520.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK stickies-v, brunoerg
    Stale ACK w0xlt, fjahr

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #32387 ([DRAFT] ipc: add windows support by ryanofsky)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  6. init: Reject + sign in -*port parsing
    It would be confusing to specify the sign for an unsigned value here, so
    reject it.
    fa89652e68
  7. net: Reject + sign when parsing subnet mask
    It does not make sense and it is rejected by other parsers as well:
    
    >>> ipaddress.ip_network("1.2.3.0/+24")
    ValueError: '1.2.3.0/+24' does not appear to be an IPv4 or IPv6 network
    fab4c2967d
  8. Reject + sign in SplitHostPort
    It is better to reject it with an error. For example,
    
    $ bitcoin-cli -rpcconnect=127.0.0.1:+23501 -getinfo
    error: Invalid port provided in -rpcconnect: 127.0.0.1:+23501
    fa479857ed
  9. Reject + sign when checking -ipcfd fa123afa0e
  10. test: Reject + sign when parsing regtest deployment params
    The + sign does not make sense for times or heights.
    fafd43c691
  11. maflcko force-pushed on May 15, 2025
  12. DrahtBot added the label CI failed on May 15, 2025
  13. DrahtBot commented at 8:14 PM on May 15, 2025: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task lint: https://github.com/bitcoin/bitcoin/runs/42316044773</sub> <sub>LLM reason (✨ experimental): The CI failure is due to Python linting errors identified by ruff. </sub>

    <details><summary>Hints</summary>

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

    </details>

  14. rest: Reject + sign in /blockhashbyheight/ 8888bb499d
  15. maflcko force-pushed on May 15, 2025
  16. DrahtBot removed the label CI failed on May 15, 2025
  17. fanquake requested review from stickies-v on May 16, 2025
  18. brunoerg commented at 1:28 PM on May 16, 2025: contributor

    Concept ACK

  19. in src/rest.cpp:967 in 8888bb499d outdated
     961 | @@ -962,8 +962,8 @@ static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
     962 |      std::string height_str;
     963 |      const RESTResponseFormat rf = ParseDataFormat(height_str, str_uri_part);
     964 |  
     965 | -    int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785
     966 | -    if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
     967 | +    const auto blockheight{ToIntegral<int32_t>(height_str)};
     968 | +    if (!blockheight || *blockheight < 0) {
     969 |          return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str));
    


    stickies-v commented at 4:52 PM on May 16, 2025:

    If we use SAFE_CHARS_URI here (seems appropriate since we're dealing with a uri) the + sign is returned in the error description:

    <details> <summary>git diff on fa24012c18</summary>

    diff --git a/src/rest.cpp b/src/rest.cpp
    index b340567bd1..464880224c 100644
    --- a/src/rest.cpp
    +++ b/src/rest.cpp
    @@ -964,7 +964,7 @@ static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
     
         const auto blockheight{ToIntegral<int32_t>(height_str)};
         if (!blockheight || *blockheight < 0) {
    -        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str));
    +        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str, SAFE_CHARS_URI));
         }
     
         CBlockIndex* pblockindex = nullptr;
    diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
    index ff76697884..a853b2ec0e 100755
    --- a/test/functional/interface_rest.py
    +++ b/test/functional/interface_rest.py
    @@ -272,7 +272,7 @@ class RESTTest (BitcoinTestFramework):
             resp = self.test_rest_request(f"/blockhashbyheight/{INVALID_PARAM}", ret_type=RetType.OBJ, status=400)
             assert_equal(resp.read().decode('utf-8').rstrip(), f"Invalid height: {INVALID_PARAM}")
             resp = self.test_rest_request("/blockhashbyheight/+1", ret_type=RetType.OBJ, status=400)
    -        assert_equal(resp.read().decode('utf-8').rstrip(), "Invalid height: 1")
    +        assert_equal(resp.read().decode('utf-8').rstrip(), "Invalid height: +1")
             resp = self.test_rest_request("/blockhashbyheight/1000000", ret_type=RetType.OBJ, status=404)
             assert_equal(resp.read().decode('utf-8').rstrip(), "Block height out of range")
             resp = self.test_rest_request("/blockhashbyheight/-1", ret_type=RetType.OBJ, status=400)
    
    

    </details>


    maflcko commented at 7:40 AM on May 17, 2025:

    thx, added a new commit on top with you as co-author

  20. in src/bitcoin-tx.cpp:232 in fa61548d5a outdated
     228 | @@ -229,16 +229,15 @@ static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
     229 |  
     230 |  static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx)
     231 |  {
     232 | -    // parse requested index
     233 | -    int64_t inIdx = -1;
     234 | -    if (strInIdx != "" && (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size()))) {
     235 | +    const auto idx{ToIntegral<int64_t>(strInIdx)};
    


    stickies-v commented at 5:19 PM on May 16, 2025:

    nit: I think making this a size_t simplifies it a bit more?

    <details> <summary>git diff on fa24012c18</summary>

    diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
    index 816f11045a..9e2c6d0902 100644
    --- a/src/bitcoin-tx.cpp
    +++ b/src/bitcoin-tx.cpp
    @@ -229,13 +229,13 @@ static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
     
     static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx)
     {
    -    const auto idx{ToIntegral<int64_t>(strInIdx)};
    -    if (strInIdx != "" && (!idx || *idx < 0 || *idx >= static_cast<int64_t>(tx.vin.size()))) {
    +    const auto idx{ToIntegral<size_t>(strInIdx)};
    +    if (strInIdx != "" && (!idx  || *idx >= tx.vin.size())) {
             throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
         }
     
         // set the nSequence to MAX_INT - 2 (= RBF opt in flag)
    -    int cnt = 0;
    +    size_t cnt = 0;
         for (CTxIn& txin : tx.vin) {
             if (strInIdx == "" || cnt == *idx) {
                 if (txin.nSequence > MAX_BIP125_RBF_SEQUENCE) {
    
    

    </details>


    maflcko commented at 7:41 AM on May 17, 2025:

    consensus rules aren't architecture or compiler-dependent, so I used uint32_t instead


    stickies-v commented at 2:12 PM on May 19, 2025:

    Ah, good point, that seems sensible. (+ for other)

  21. in src/bitcoin-tx.cpp:510 in faeb49a4c2 outdated
     506 | @@ -507,26 +507,20 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
     507 |  
     508 |  static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
     509 |  {
     510 | -    // parse requested deletion index
     511 | -    int64_t inIdx;
     512 | -    if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) {
     513 | +    const auto idx{ToIntegral<uint64_t>(strInIdx)};
    


    stickies-v commented at 5:23 PM on May 16, 2025:

    nit: looks safe because we're comparing with tx.vin.size(), but using size_t seems more appropriate for a container index? (+ for MutateTxDelOutput)


    maflcko commented at 7:41 AM on May 17, 2025:

    (same)

  22. in src/test/fuzz/parse_numbers.cpp:18 in fa24012c18 outdated
      13 | @@ -14,24 +14,9 @@ FUZZ_TARGET(parse_numbers)
      14 |  
      15 |      (void)ParseMoney(random_string);
      16 |  
      17 | -    uint8_t u8;
      18 | -    (void)ParseUInt8(random_string, &u8);
    


    stickies-v commented at 5:29 PM on May 16, 2025:

    It seems we now no longer have ToIntegral fuzz test coverage. It's a fairly simple function, so maybe that's fine, but so was ParseIntegral. Reckon it's worth keeping ToIntegral for the most common types in this harness?


    maflcko commented at 7:41 AM on May 17, 2025:

    thx, added some fuzz

  23. stickies-v approved
  24. stickies-v commented at 5:48 PM on May 16, 2025: contributor

    ACK fa24012c181850d4adc5cca0fdba76d584ec3a00, nice cleanup.

    Introduces some slight behaviour change when passing integer arguments through startup options, the REST interface, as and through bitcoin-cli and bitcoin-tx. While some of these changes may break existing user flows that were previously fine, they should hopefully be quite rare, and I think are worth the cleanup and more strict parsing.

  25. DrahtBot requested review from brunoerg on May 16, 2025
  26. rest: Use SAFE_CHARS_URI in SanitizeString error msg
    When dealing with URI parts, it seems more consistent to use
    corresponding SAFE_CHARS_URI mode in error messages.
    
    Co-Authored-By: stickies-v <stickies-v@protonmail.com>
    fab06ac037
  27. bitcoin-tx: Reject + sign in nversion parsing
    It would be confusing to specify the sign for an unsigned value here, so
    reject it.
    dddd9e5fe3
  28. bitcoin-tx: Reject + sign in locktime faff25a558
  29. bitcoin-tx: Reject + sign in replaceable parsing fa8acaf0b9
  30. bitcoin-tx: Reject + sign in vout parsing face2519fa
  31. bitcoin-tx: Reject + sign in MutateTxDel* fa84e6c36c
  32. maflcko force-pushed on May 17, 2025
  33. maflcko force-pushed on May 17, 2025
  34. fjahr commented at 1:05 PM on May 19, 2025: contributor

    Concept ACK

  35. in src/test/fuzz/parse_numbers.cpp:27 in fa2445eea3 outdated
      22 | +        const auto u64{ToIntegral<uint64_t>(random_string)};
      23 | +        if (i8) {
      24 | +            assert(i8 == i16);
      25 | +            if (*i8 > 0) {
      26 | +                assert(i8 == u8);
      27 | +            }
    


    stickies-v commented at 1:26 PM on May 19, 2025:

    nit: could assert that uints don't underflow from --prefixed inputs?

    <details> <summary>git diff on fa2445eea3</summary>

    diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp
    index b2465de517..21b1d534f2 100644
    --- a/src/test/fuzz/parse_numbers.cpp
    +++ b/src/test/fuzz/parse_numbers.cpp
    @@ -6,6 +6,9 @@
     #include <util/moneystr.h>
     #include <util/strencodings.h>
     
    +#include <cassert>
    +#include <cstdint>
    +#include <optional>
     #include <string>
     
     FUZZ_TARGET(parse_numbers)
    @@ -24,12 +27,16 @@ FUZZ_TARGET(parse_numbers)
                 assert(i8 == i16);
                 if (*i8 > 0) {
                     assert(i8 == u8);
    +            } else {
    +                assert(u8.value_or(0) == 0);
                 }
             }
             if (i16) {
                 assert(i16 == i32);
                 if (*i16 > 0) {
                     assert(i16 == u16);
    +            } else {
    +                assert(u16.value_or(0) == 0);
                 }
             } else {
                 assert(!i8);
    @@ -38,6 +45,8 @@ FUZZ_TARGET(parse_numbers)
                 assert(i32 == i64);
                 if (*i32 > 0) {
                     assert(i32 == u32);
    +            } else {
    +                assert(u32.value_or(0) == 0);
                 }
             } else {
                 assert(!i16);
    @@ -45,6 +54,8 @@ FUZZ_TARGET(parse_numbers)
             if (i64) {
                 if (*i64 > 0) {
                     assert(i64 == u64);
    +            } else {
    +                assert(u64.value_or(0) == 0);
                 }
             } else {
                 assert(!i32);
    
    

    </details>


    maflcko commented at 3:17 PM on May 19, 2025:

    thx. I don't want to check any explicit values, so I pushed something else.

  36. stickies-v approved
  37. stickies-v commented at 2:16 PM on May 19, 2025: contributor

    re-ACK fa2445eea3e380386ab01631b735c7eeb6b76b58

  38. DrahtBot requested review from w0xlt on May 19, 2025
  39. DrahtBot requested review from fjahr on May 19, 2025
  40. maflcko force-pushed on May 19, 2025
  41. refactor: Remove unused Parse(U)Int* 3333282933
  42. maflcko force-pushed on May 19, 2025
  43. stickies-v commented at 4:00 PM on May 19, 2025: contributor

    re-ACK 33332829333b589420f8038541d04ec6970f051d

  44. fjahr commented at 9:56 PM on May 19, 2025: contributor

    utACK 33332829333b589420f8038541d04ec6970f051d

    I think I found two leftover mentions of the ParseInt in documentation: https://github.com/fjahr/bitcoin/commit/1f2f1a432b0fdb3d84eecc9dc3a2db34d88dc892 I can option a follow-up if you don't want to address it here.

  45. doc: Remove ParseInt mentions in documentation
    In the dev notes, remove the whole section, because:
    
    * ParseDouble was removed in commit
      fa9d72a7947d2cff541794e21e0040c3c1d43b32
    * The locale-dependent atoi is already checked by
      test/lint/lint-locale-dependence.py
    
    Co-authored-by: Fabian Jahr <fjahr@protonmail.com>
    faf55fc80b
  46. maflcko commented at 5:05 AM on May 20, 2025: member

    thx, I pushed a commit similar to yours. (Generally, when it comes to the dev notes, I think it is clear that no one is reading them, because of all the stale references in it. We should probably work on reducing them to only contain the important stuff that is not already checked by the automated linters or not already covered by other docs)

  47. stickies-v commented at 9:21 AM on May 20, 2025: contributor

    re-ACK faf55fc80b

  48. brunoerg approved
  49. brunoerg commented at 1:54 PM on May 20, 2025: contributor

    code review ACK faf55fc80b11f3d9b0b12c1b26a9612ea9ce9b40

    Did a grep to check if anything is missing, seems fine.

  50. fanquake merged this on May 20, 2025
  51. fanquake closed this on May 20, 2025

  52. maflcko deleted the branch on May 20, 2025

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-28 06:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me