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

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

    Code Coverage & Benchmarks

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

    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.

    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.

  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

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

    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.

  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:

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

    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?

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

    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?

     0diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp
     1index b2465de517..21b1d534f2 100644
     2--- a/src/test/fuzz/parse_numbers.cpp
     3+++ b/src/test/fuzz/parse_numbers.cpp
     4@@ -6,6 +6,9 @@
     5 #include <util/moneystr.h>
     6 #include <util/strencodings.h>
     7 
     8+#include <cassert>
     9+#include <cstdint>
    10+#include <optional>
    11 #include <string>
    12 
    13 FUZZ_TARGET(parse_numbers)
    14@@ -24,12 +27,16 @@ FUZZ_TARGET(parse_numbers)
    15             assert(i8 == i16);
    16             if (*i8 > 0) {
    17                 assert(i8 == u8);
    18+            } else {
    19+                assert(u8.value_or(0) == 0);
    20             }
    21         }
    22         if (i16) {
    23             assert(i16 == i32);
    24             if (*i16 > 0) {
    25                 assert(i16 == u16);
    26+            } else {
    27+                assert(u16.value_or(0) == 0);
    28             }
    29         } else {
    30             assert(!i8);
    31@@ -38,6 +45,8 @@ FUZZ_TARGET(parse_numbers)
    32             assert(i32 == i64);
    33             if (*i32 > 0) {
    34                 assert(i32 == u32);
    35+            } else {
    36+                assert(u32.value_or(0) == 0);
    37             }
    38         } else {
    39             assert(!i16);
    40@@ -45,6 +54,8 @@ FUZZ_TARGET(parse_numbers)
    41         if (i64) {
    42             if (*i64 > 0) {
    43                 assert(i64 == u64);
    44+            } else {
    45+                assert(u64.value_or(0) == 0);
    46             }
    47         } else {
    48             assert(!i32);
    

    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: 2025-05-20 21:12 UTC

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