Commit Graph

50438 Commits

Author SHA1 Message Date
Matthew Zipkin
28b69e2988 http: stop processing requests from a client when send buffer is full
Prevents a memory exhaustion case where a misbehaving client
refuses to read responses and drain the socket buffer. Instead of
packing more data on to the server-side m_send_buffer, stop
dispatching requests from the client to workers
2026-09-09 13:15:46 -04:00
merge-script
f0c839ace5 Merge bitcoin/bitcoin#36123: http: throttle per-connection reads while a request is in flight
3d1004cb9b http: throttle per-connection reads while a request is in flight (Matthew Zipkin)

Pull request description:

  This patches a memory exhaustion scenario found while auditing the new http server with kimi-k3. A shallow version of this scenario was addressed in #35735 (See  https://github.com/bitcoin/bitcoin/pull/35735#discussion_r3720177656 and https://github.com/bitcoin/bitcoin/pull/35735#issuecomment-5217000202) but a OOM vector still remained.

  On master when the sever is busy handling a request from a client, it will still read data from that client and "queue up" the next request. In #35735 we handled the scenario where that additional incoming data was an invalid HTTP request by not attempting to parse the data. However, we didn't add a size limit.

  A misbehaving client could block its request queue with something like `waitforblock` and then flood the server with nonsense data without any limit.

  The solution in this patch is to not even read from the socket at all if we are busy with a request. Similar to the intent of #35735, the kernel will buffer incoming data until backpressure kicks in and the TCP window drops to 0.

  If unaddressed, the attack vector is still limited to authenticated clients: unauthenticated REST requests don't block for very long, so the server *should* be able to drain the receive buffer.

ACKs for top commit:
  jeanpablojp:
    tACK 3d1004cb9b
  frankomosh:
    ACK 3d1004cb9b
  hodlinator:
    ACK 3d1004cb9b
  winterrdog:
    tACK 3d1004cb9b
  sedited:
    ACK 3d1004cb9b

Tree-SHA512: 56f7678a9ab6789aa542c1f252df0b6ccf9137cb426ff915a0a3fe8285200fdb62b7a47c476ed8617c3592e7a7eac18158cd8c0dac309cdcf4e5fd887e016209
2026-09-05 17:15:57 +02:00
merge-script
0f206eed51 Merge bitcoin/bitcoin#36130: test: add tests in transaction_tests.cpp covering live mutants
5ce3a0b4aa test: cover legacy sigops count CHECKMULTISIG inaccurately (ViniciusCestarii)
a5fc82e2b1 test: cover enforce BIP68 to tx versions higher than 2 (ViniciusCestarii)
bba1d4150e test: cover IsFinalTx requires every input to be SEQUENCE_FINAL (ViniciusCestarii)

Pull request description:

  Kills some live mutants on tx_verify.cpp that affect consensus found with https://github.com/ViniciusCestarii/mutant-harness. They are:

  <details>
  <summary>tx_verify.cpp (killed by 5c35785d6ddda80d5147616342e42d759490e6b9): <code>IsFinalTx</code>: sequence loop returns on the first input instead of requiring all of them</summary>

  ```diff
  diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
  index e580a9d..46009a6 100644
  --- a/src/consensus/tx_verify.cpp
  +++ b/src/consensus/tx_verify.cpp
  @@ -35,11 +35,7 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
       // also check that the spending input's nSequence != SEQUENCE_FINAL,
       // ensuring that an unsatisfied nLockTime value will actually cause
       // IsFinalTx() to return false here:
  -    for (const auto& txin : tx.vin) {
  -        if (!(txin.nSequence == CTxIn::SEQUENCE_FINAL))
  -            return false;
  -    }
  -    return true;
  +    return std::ranges::any_of(tx.vin, [](const CTxIn& txin) { return txin.nSequence == CTxIn::SEQUENCE_FINAL; });
   }

   std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block)
  ```

  </details>

  <details>
  <summary>tx_verify.cpp (killed by 3ef559d9a5cb79e4721b68427ad679d9f4f6392a): <code>CalculateSequenceLocks</code>: <code>tx.version >= 2</code> -> <code>tx.version == 2</code></summary>

  ```diff
  diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
  index e580a9d..0faaa55 100644
  --- a/src/consensus/tx_verify.cpp
  +++ b/src/consensus/tx_verify.cpp
  @@ -54,7 +54,7 @@ std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags
       int nMinHeight = -1;
       int64_t nMinTime = -1;

  -    bool fEnforceBIP68 = tx.version >= 2 && flags & LOCKTIME_VERIFY_SEQUENCE;
  +    bool fEnforceBIP68 = tx.version == 2 && flags & LOCKTIME_VERIFY_SEQUENCE;

       // Do not enforce sequence numbers as a relative lock time
       // unless we have been instructed to
  ```

  </details>

  <details>
  <summary>tx_verify.cpp (killed by 1944eb409055d88eeaf7888b18a75289c506a943): <code>GetLegacySigOpCount</code>: <code>scriptSig.GetSigOpCount(false)</code> -> <code>GetSigOpCount(true)</code></summary>

  ```diff
  diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
  index e580a9d..0b98597 100644
  --- a/src/consensus/tx_verify.cpp
  +++ b/src/consensus/tx_verify.cpp
  @@ -120,7 +120,7 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx)
       unsigned int nSigOps = 0;
       for (const auto& txin : tx.vin)
       {
  -        nSigOps += txin.scriptSig.GetSigOpCount(false);
  +        nSigOps += txin.scriptSig.GetSigOpCount(true);
       }
       for (const auto& txout : tx.vout)
       {
  ```

  </details>

  Recommend reviewing per commit.

ACKs for top commit:
  jeanpablojp:
    tACK 5ce3a0b4aa
  instagibbs:
    ACK 5ce3a0b4aa
  brunoerg:
    ACK 5ce3a0b4aa
  sedited:
    ACK 5ce3a0b4aa

Tree-SHA512: 1f5c941638fc2907759e5a8d6faf0669b7b7d03d833b51ad675bed10585dd6b232999aa2a5ecb9e0b58db81b1ec44c9916c680e872608e4fe5ee50e71f6b82b4
2026-09-05 14:27:10 +02:00
merge-script
0b43dea121 Merge bitcoin/bitcoin#36163: test: Add coverage for unsatisfiable locktime combination in PSBT ComputeTimeLock()
69a640e05e test: Add coverage for unsatisfiable locktime combination in PSBT ComputeTimeLock (nebula-21)

Pull request description:

  This PR adds a test case to `psbt2_timelock_test` covering an unsatisfiable locktime combination in `PartiallySignedTransaction::ComputeTimeLock()`.

  When different PSBT v2 inputs specify their own timelock requirement, `ComputeTimeLock()` needs to reconcile all of those into a single locktime for the whole transaction. To reconcile this locktime, all the inputs locktimes need to be height or time-based, but not a mix of them.

  The existing test already covers this failure when the input #0 is height-based and a later input is time-based, returning `std::nullopt`.
  This PR adds the other case when the input #0 is time-based and a later input is height-based, returning `std::nullopt`.
  I've basically swapped the PSBT inputs from the already existing case to cover this one.

ACKs for top commit:
  sedited:
    ACK 69a640e05e

Tree-SHA512: e7a7556df3bd278686a2d53a11b228f6f8c0e8dda79f050bad83dea89824de2e3766518fa3450b3722be86927fb4e6b9061dcd2cdb45010080b37a4fa2baffe0
2026-09-05 14:10:37 +02:00
merge-script
3596658af5 Merge bitcoin/bitcoin#36166: validation: refactor: encapsulate Chainstate::m_target_blockhash
852f201e09 validation: refactor: encapsulate Chainstate::m_target_blockhash (stickies-v)

Pull request description:

  `m_target_blockhash` is paired with a mutable `m_cached_target_block` that must be kept in sync whenever the hash changes.

  Refactor, no behaviour change.

  Addresses https://github.com/bitcoin/bitcoin/pull/36137#discussion_r3903670739

ACKs for top commit:
  kevkevinpal:
    ACK [852f201](852f201e09)
  purpleKarrot:
    ACK 852f201e09
  l0rinc:
    code review ACK 852f201e09
  alexanderwiederin:
    ACK 852f201e09
  sedited:
    ACK 852f201e09

Tree-SHA512: 6243ee9979a2493b4f495a0156a119814854d7d91c48bb18777afae928ee2c3b0280ecba3d7516ffef25d92eb15d0a3e369005d43e68afed1142161d6bf4eeda
2026-09-05 14:01:13 +02:00
merge-script
f32bfb2593 Merge bitcoin/bitcoin#35738: coins: parallel input prevout fetching followups
8e4b7ab725 fuzz: use per-level fetch scopes in coinscache_sim (Andrew Toth)
5292386b78 doc: improve CoinsViewOverlay documentation (Andrew Toth)
d552c52b08 coins: log error reason when prevout fetch submission fails (Andrew Toth)
2ffaa6e6a7 coins: delete Sync and SetBackend on CoinsViewOverlay (Andrew Toth)
330022993f coins: filter coinbase txid from parallel input fetching (Andrew Toth)

Pull request description:

  This addresses various follow-ups requested in https://github.com/bitcoin/bitcoin/pull/35295.

  - add the coinbase txid to the filter so inputs spending the coinbase are not fetched.
  - delete Sync and SetBackend from CoinsViewOverlay
  - various logging and documentation improvements
  - improve coinscache_sim fuzzing so we continue parallel fetching while more caches are added on to the cache stack

ACKs for top commit:
  optout21:
    reACK 8e4b7ab725
  l0rinc:
    ACK 8e4b7ab725
  sedited:
    ACK 8e4b7ab725

Tree-SHA512: 38001f96be6f893e2610bb81f379ecc0c40ffd39da5bfe1f5db47db1ef2f725d80ae3f9b5e25acd64e65013176ba3ba4e3e8585cb55420b2793845c292beda23
2026-09-05 13:51:19 +02:00
stickies-v
852f201e09 validation: refactor: encapsulate Chainstate::m_target_blockhash
m_target_blockhash is paired with a mutable m_cached_target_block that
must be kept in sync whenever the hash changes.
2026-09-04 10:45:38 +02:00
nebula-21
69a640e05e test: Add coverage for unsatisfiable locktime combination in PSBT ComputeTimeLock 2026-09-03 16:29:50 +02:00
ViniciusCestarii
5ce3a0b4aa test: cover legacy sigops count CHECKMULTISIG inaccurately 2026-09-03 10:56:47 -03:00
ViniciusCestarii
a5fc82e2b1 test: cover enforce BIP68 to tx versions higher than 2 2026-09-03 10:56:47 -03:00
ViniciusCestarii
bba1d4150e test: cover IsFinalTx requires every input to be SEQUENCE_FINAL 2026-09-03 10:55:49 -03:00
merge-script
4519933391 Merge bitcoin/bitcoin#36137: validation: use unused SetTargetBlockHash
4550801058 validation: use unused SetTargetBlockHash (fanquake)

Pull request description:

  This was pointed out as unused in #36103 by jeanpablojp, but that seems like a mistake from #30214, where it was introduced. See: https://github.com/bitcoin/bitcoin/pull/36137#discussion_r3906377189.

ACKs for top commit:
  stickies-v:
    ACK 4550801058
  ryanofsky:
    Code review ACK 4550801058

Tree-SHA512: 93ccac48855d384f0443b5a25c79c5e6d720b6b77ad7a2bb52989382666e4e5f32c76dd7473428d6bbb503307ada7213021591ad54e463d9f8034fe2da97d10c
2026-09-03 15:50:22 +02:00
Hennadii Stepanov
4ec6ff022a Merge bitcoin/bitcoin#36100: ci: use LLVM 23 in *san, fuzz, *cross jobs
5ba9af6b69 ci: pass LIBCXX_INCLUDE_TESTS=OFF to LLVM build (fanquake)
feb3bd46e4 clang-tidy: remove some performance-* options (fanquake)
b4bd12d3d5 ci: use LLVM 23 in *san, fuzz, *cross jobs (fanquake)

Pull request description:

  LLVM 23.1.0 was recently released, switch to using it across sanitizer, fuzzer and cross-compilation jobs.

ACKs for top commit:
  hebasto:
    ACK 5ba9af6b69, I have reviewed the code and it looks OK.
  willcl-ark:
    ACK 5ba9af6b69

Tree-SHA512: 4d203bf1ec6100a21d9a185a37365d358859bbde79f44f93d2e4f5e3c9686f57ca06d6c73da7423eb234dad5b9501d7a09029a430d23b2bcf8aba95f2d88e66d
2026-09-03 12:32:49 +01:00
merge-script
7f0c4020e8 Merge bitcoin/bitcoin#36118: test: tolerate race condition in interface_http.py
a51df9b0ec test: tolerate race condition in interface_http.py (Matthew Zipkin)

Pull request description:

  Fixes #35632 by allowing both outcomes of a race condition. The server behavior is unchanged: in response to a malformed request we send an error code and disconnect. The issue is that sometimes on Windows the RST is caught by the platform and the receive buffer is discarded before the Python client can process it with recv().

  We can also be much more polite to misbehaving clients by implementing a lingering close using SO_LINGER as suggested in #35780 but that will require more review.

  The exact error in #35632 is hard to produce reliably but there are a few close options for reviewers. I tested this on windows native building with MSVC. In both of these cases the patch from this PR caught the error and passed the test.

  **RemoteDisconnected: Remote end closed connection without response**

  ```diff
  diff --git a/src/httpserver.cpp b/src/httpserver.cpp
  index 9bb89863af..62324d3fea 100644
  --- a/src/httpserver.cpp
  +++ b/src/httpserver.cpp
  @@ -1072,7 +1072,7 @@ std::unique_ptr<HTTPRequest> HTTPRemoteClient::TryReadRequest(const std::shared_
               e.what());

           // We failed to read a complete request from the buffer
  -        WriteNoStoreErrorReply(*client->m_req, HTTP_BAD_REQUEST);
  +        // WriteNoStoreErrorReply(*client->m_req, HTTP_BAD_REQUEST);
           client->m_disconnect = true;
           return nullptr;
       }
  ```

  **ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host**

  ```diff
  diff --git a/src/httpserver.cpp b/src/httpserver.cpp
  index 9bb89863af..be52acb874 100644
  --- a/src/httpserver.cpp
  +++ b/src/httpserver.cpp
  @@ -1154,6 +1154,11 @@ bool HTTPRemoteClient::MaybeDisconnect(std::chrono::time_point<SteadyClock> now,
                "Disconnecting HTTP client %s (id=%llu)",
                m_origin,
                m_id);
  +    auto sock{GetSock()};
  +    linger opt{};
  +    opt.l_onoff  = 1;  // enable SO_LINGER
  +    opt.l_linger = 0;  // zero timeout
  +    sock->SetSockOpt(SOL_SOCKET, SO_LINGER, &opt, sizeof(opt));
       return true;
   }

  ```

ACKs for top commit:
  jeanpablojp:
    re-ACK a51df9b0ec
  winterrdog:
    tACK a51df9b0ec
  janb84:
    re ACK a51df9b0ec
  hodlinator:
    re-ACK a51df9b0ec
  sedited:
    ACK a51df9b0ec

Tree-SHA512: a6244581b2b51af647452e0dc8cd09cdc8d975dee6a0dc8b8064cad136023dad68b4af987303bced91a662bf5fae22871ea718a6a8e68024158a9aef6c5855ef
2026-09-03 13:31:20 +02:00
fanquake
4550801058 validation: use unused SetTargetBlockHash
This was pointed out as unused in #36103, but that seems like a mistake
from #30214, where it was introduced.

Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
2026-09-03 11:26:22 +01:00
fanquake
5ba9af6b69 ci: pass LIBCXX_INCLUDE_TESTS=OFF to LLVM build 2026-09-03 10:44:02 +01:00
fanquake
feb3bd46e4 clang-tidy: remove some performance-* options 2026-09-03 10:44:02 +01:00
fanquake
b4bd12d3d5 ci: use LLVM 23 in *san, fuzz, *cross jobs 2026-09-03 10:44:01 +01:00
merge-script
d840adb9da Merge bitcoin/bitcoin#36144: rpc: detail x-bitcoin-unit in openrpc help
e85e27976b rpc: detail x-bitcoin-unit in openrpc help (will)

Pull request description:

  Addresses review comment about clarifying this field: https://github.com/bitcoin/bitcoin/pull/36131#issuecomment-5480592255

ACKs for top commit:
  sedited:
    ACK e85e27976b

Tree-SHA512: 7fd0bef8a5d37cd9d2778463b2193c58ec7cced1aa790a0a5807ef093bd51e729b6c8880e1c12af78293eb68abe791f4ae6e96e0457ce85be717e3e776f5406d
2026-09-03 09:19:22 +01:00
merge-script
f41b917f47 Merge bitcoin/bitcoin#36145: qa: Use IP_PORTRANGE_HIGH on OpenBSD for dynamic port allocation
59ebf558f3 qa: Use IP_PORTRANGE_HIGH on OpenBSD for dynamic port allocation (Hennadii Stepanov)

Pull request description:

  The default ephemeral port range on OpenBSD (1024-49151) overlaps with the test framework's static port range starting at `TEST_RUNNER_PORT_MIN`, the same way FreeBSD's does (see #34346).

  Extend `set_ephemeral_port_range()` to OpenBSD. The socket option and its values are identical to FreeBSD's, so only the platform check changes.

ACKs for top commit:
  maflcko:
    lgtm ACK 59ebf558f3
  theStack:
    utACK 59ebf558f3

Tree-SHA512: 680235cf3e1799361796c0ff36d5f19bf74f79393057dbd7b38b0e92a7df3af669873c66ca1e82f1c78999c20351663b8960990f3f209af89ee18ce0773eb7de
2026-09-03 09:12:11 +01:00
merge-script
d5fbe61f55 Merge bitcoin/bitcoin#35958: net: align v2 message type validation with v1 range
cc577de954 net: align v2 message type validation with v1 range (Bruno Garcia)

Pull request description:

  BIP324 specifies the 13-byte long-form message type encoding as "an ASCII message type (as in the v1 P2P protocol)", but V2Transport::GetMessageType() accepted bytes up to 0x7F, while for V1 it only accepts printable ASCII (0x20-0x7E).

  This changes V2 to match V1 on it and add test coverage.

ACKs for top commit:
  nervana21:
    tACK cc577de954
  ajtowns:
    utACK cc577de954
  w0xlt:
    ACK cc577de954
  sedited:
    ACK cc577de954

Tree-SHA512: 8c97ee20df2311949bbe9655c7e04507c4b47d3b18766aa6ae51691d0870f8a5c25ea54d74c9afb797754572d057b4240533da6bf3c2e0435f3cb32c5fb1c3af
2026-09-03 09:11:05 +01:00
merge-script
64ab0a0697 Merge bitcoin/bitcoin#36148: test: Avoid unsafe memory race in index_reorg_crash shutdown
fab80e82c1 test: Avoid unsafe memory race in baseindex_no_commit_ahead_of_flush (MarcoFalke)
fa0f14ef5e test: Avoid unsafe memory race in index_reorg_crash shutdown (MarcoFalke)
faf9c8e8a1 test: Clarify index.GetSummary().synced state in index_reorg_crash (MarcoFalke)

Pull request description:

  Currently, the `index_reorg_crash` test may rarely crash due to UB in sanitizers like TSan or ASan. This is perfectly fine, because it is just a rare test-only issue.

  However, fix it nonetheless by adding a missing drain of the unused in-flight events. Also, add a small check about the synced state while touching this test.

ACKs for top commit:
  arejula27:
    ACK fab80e82c1
  furszy:
    ACK fab80e82c1

Tree-SHA512: 4423e420421aa37d8b59e053f44c455fafb676102866bdf23988cf72f3d3f265b996bd953583ea8208f1534defb0e16b13ef08644be97e61959dc777a2918e5a
2026-09-03 09:00:14 +01:00
Matthew Zipkin
a51df9b0ec test: tolerate race condition in interface_http.py
Fixes #35632 by allowing both outcomes of a race condition.
The server behavior is unchanged: in response to a malformed request
we send an error code and disconnect. The issue is that sometimes
on Windows the RST is caught by the platform and the receive buffer
is discarded before the Python client can process it with recv().

We can also be much more polite to misbehaving clients by
implementing SO_LINGER as suggested in #35780 but that will require
more review.
2026-09-02 13:42:57 -04:00
MarcoFalke
fab80e82c1 test: Avoid unsafe memory race in baseindex_no_commit_ahead_of_flush
Without the drain, a BlockConnected event may execute during shutdown
and lead to memory races.
2026-09-02 16:30:55 +02:00
merge-script
b811aeabad Merge bitcoin/bitcoin#36048: util: keep wallet names literal in notification commands
db39de5601 doc: add `-walletnotify` security note (Lőrinc)
1f9dfabef6 refactor: use string views in `ReplaceAll` (Lőrinc)
469b0e59a2 util: make `ReplaceAll` literal (Lőrinc)
604d7e8fdd test: characterize walletnotify shell injection (Lőrinc)
4efaa6763a test: simplify `ReplaceAll` coverage (Lőrinc)

Pull request description:

  **Problem:** On non-Windows builds, operators can configure `-walletnotify` to run a command for wallet transactions, with `%w` replaced by the shell-escaped wallet name.
  An authenticated RPC caller allowed to create wallets can supply a name containing `$'`, request an address, and send a transaction to it.
  While replacing `%w`, `ReplaceAll()` passes the escaped wallet name to `std::regex_replace()` as replacement text.
  There, `$'` copies the command suffix into the escaped name, breaking its quote accounting and allowing shell metacharacters in the wallet name to alter the command.
  `runCommand()` passes the result to `system()`, so a suitable command template could execute additional shell commands as the node process account.
  It is not reachable over P2P or by an unauthenticated network peer.
  #25803 introduced this behavior in v24 when it replaced Boost's literal substitution with `std::regex_replace()`.

  **Fix:** Restore the literal, non-recursive contract `ReplaceAll()` had before #25803, matching every current caller's literal search and replacement text, while the wallet notification test covers a wallet name containing `$'`.

  **Related:** #35833 restricts control characters in new wallet names, while this change fixes replacement metacharacters in `ReplaceAll()`.

  This was found and disclosed responsibly by the Red Team 🟥.

ACKs for top commit:
  maflcko:
    re-ACK db39de5601 💈
  jeanpablojp:
    re-ACK db39de5601
  stickies-v:
    re-ACK db39de5601

Tree-SHA512: 0be4adecfee50cb4dab90ae3386079767694a6b1fa1d7bd1f10ef73de88707b232f1ba4975a723c465a4d34d12296d501986c657d93bd8ae0bdced16afad1b5e
2026-09-02 15:54:35 +02:00
MarcoFalke
fa0f14ef5e test: Avoid unsafe memory race in index_reorg_crash shutdown
Without the drain, a BlockConnected event may execute during shutdown
and lead to memory races.
2026-09-02 13:31:14 +02:00
MarcoFalke
faf9c8e8a1 test: Clarify index.GetSummary().synced state in index_reorg_crash
This clarifies the initial index sync thread is blocked.
2026-09-02 13:24:43 +02:00
Hennadii Stepanov
59ebf558f3 qa: Use IP_PORTRANGE_HIGH on OpenBSD for dynamic port allocation
The default ephemeral port range on OpenBSD (1024-49151) overlaps with
the test framework's static port range starting at TEST_RUNNER_PORT_MIN,
the same way FreeBSD's does (see #34346).

Extend `set_ephemeral_port_range()` to OpenBSD. The socket option and
its values are identical to FreeBSD's, so only the platform check
changes.
2026-09-02 09:48:01 +01:00
merge-script
6f6b2bbde2 Merge bitcoin/bitcoin#35808: fuzz: reset connman state in p2p targets
d29b22d078 fuzz: reset connman state in p2p targets (Hao Xu)

Pull request description:

  Resets `ConnmanTestMsg` at the start of each input in `cmpctblock` and `p2p_handshake`, matching the other reused-connman fuzz targets and preventing sticky `CConnman` state from leaking between corpus inputs.

  Before this, deterministic-fuzz-coverage showed single inputs were stable, but all-input directory runs were not:

  ```diff
  cmpctblock, src/net.cpp:4172
  - Branch (4172:9): [True: 1.21k, False: 33.0k]
  + Branch (4172:9): [True: 613, False: 33.6k]
  - Branch (4172:72): [True: 901, False: 311]
  + Branch (4172:72): [True: 497, False: 116]
  ```

  ```diff
  p2p_handshake, src/net.cpp:4172
  - Branch (4172:9): [True: 98, False: 1.67k]
  + Branch (4172:9): [True: 743, False: 1.03k]
  - Branch (4172:72): [True: 90, False: 8]
  + Branch (4172:72): [True: 612, False: 131]
  ```

  With the resets, `deterministic-fuzz-coverage` passed for both `cmpctblock` and `p2p_handshake`.

ACKs for top commit:
  nervana21:
    re-tACK d29b22d078
  maflcko:
    lgtm ACK d29b22d078

Tree-SHA512: bd445ae33ab7f9850046e3de4e318bee9ae7b38ee77ee282d0f5c3a88a4b67610dd00faef2af913a4d7d42bfc42b957855b2d85f1849f77823149baa089afce2
2026-09-02 09:29:26 +01:00
will
e85e27976b rpc: detail x-bitcoin-unit in openrpc help 2026-09-02 09:29:08 +01:00
Matthew Zipkin
3d1004cb9b http: throttle per-connection reads while a request is in flight
A client streaming pipelined requests into a busy connection
(or any connection whose replies are slower than the sender) could grow
server memory without limit, up to remote OOM.

Stop selecting RecvEvent for clients whose request is being processed;
pipelined data then backs up in the kernel socket buffer, applying TCP
backpressure to the sender. One request per connection is in flight
at a time.

Functional test streams pipelined submitblock requests into a connection
blocked on waitforblockheight. Unpatched builds continue draining the
socket buffer indefinitely, patched builds will stall.
2026-09-01 15:56:09 -04:00
Lőrinc
db39de5601 doc: add -walletnotify security note
Co-authored-by: maflcko <6399679+maflcko@users.noreply.github.com>
2026-09-01 11:49:26 -07:00
Lőrinc
1f9dfabef6 refactor: use string views in ReplaceAll
PR #25803 changed these parameters to `const std::string&` for `std::regex_replace()`.
The literal implementation no longer needs owned strings, so restore the original `std::string_view` interface.
2026-09-01 11:46:55 -07:00
Lőrinc
469b0e59a2 util: make ReplaceAll literal
`ReplaceAll()` substitutes fixed tokens in notification commands and other strings.
PR #25803 replaced the Boost helper with `std::regex_replace()`, treating searches as regular expressions and substitutes as replacement-format syntax.

Restore literal, non-recursive replacement so callers match fixed tokens and preserve replacement bytes exactly, while avoiding a new string when the search text is absent.

Co-authored-by: Rob Hamilton <6456095+Rob1Ham@users.noreply.github.com>
2026-09-01 11:46:55 -07:00
Lőrinc
604d7e8fdd test: characterize walletnotify shell injection
`-walletnotify` shell-escapes wallet names before substituting `%w` into the configured command.
`ReplaceAll()` uses `%w` as the regex pattern and the escaped wallet name as replacement text, where `$'` copies the command suffix into the escaped name and allows its shell metacharacters to alter the command.

Record the command execution, missing notification file, regex pattern matching, replacement expansion, and non-recursive replacement.
2026-09-01 11:46:55 -07:00
Lőrinc
4efaa6763a test: simplify ReplaceAll coverage
Let each case provide its input so strings outside the original fixture can use the same table without separate temporary variables.
2026-09-01 11:27:50 -07:00
merge-script
dc0395c585 Merge bitcoin/bitcoin#36112: ci: Exclude subtrees from iwyu
fa3971011d ci: Exclude subtrees from iwyu (MarcoFalke)
fa8566152a refactor: Bump old copyright header in univalue (MarcoFalke)

Pull request description:

  The iwyu CI may modify subtrees when iwyu thinks a header inside a subtree is "associated" (due to the naming).

  This happens to not be a problem on current master, but can become a problem if an iwyu-enforced file is renamed or a file is iwyu-enforced in the future.

  Fix this by excluding subtrees.

  Can be tested by running the iwyu CI on `src/test/fuzz/minisketch.cpp` and seeing a change in `minisketch.h` before this CI fix.

ACKs for top commit:
  hebasto:
    re-ACK fa3971011d.

Tree-SHA512: 9a555ab020f0f1a2bc4d70ea72011f8d42ba4bfe4a463947d31b0d208b4671b76b466f92a18b6295bc7a8c5bb67c6f697844f673fc02e18983b062d25bc0dc8c
2026-09-01 11:39:19 +01:00
merge-script
37c57bc5c2 Merge bitcoin/bitcoin#36065: test: refactor: Remove confusing ignore_errors=True
fa7be0a8df test: refactor: Remove confusing ignore_errors=True (MarcoFalke)

Pull request description:

  There is an unexplained `ignore_errors=True` in the internal `_initialize_chain` helper:

  ```py

  shutil.rmtree(cache_path('fees'), ignore_errors=True)
  ```

  This is fine, because no error should happen. But it is a bit confusing, because an ignored error may lead to a later error anyway.

  Fix that by failing early instead.

  Also, re-write the simple block to `pathlib`.

ACKs for top commit:
  willcl-ark:
    ACK fa7be0a8df

Tree-SHA512: c533a8aebd92f3f1054563f20af438165632c98f7a2f189f3306420780468b143c24001f794a79ddfc0527c9605a4cfe59949648a9a7f41bbe138128b09f0a6e
2026-09-01 09:52:00 +01:00
merge-script
8157964e66 Merge bitcoin/bitcoin#36134: doc: Correct comment about which subsystem detects lagging clocks
55390d1827 doc: Correct comment about which subsystem detects lagging clocks (Hodlinator)

Pull request description:

  Turns out a completely fresh datadir means there is no chain state to load and hence no detection of a lagging clock occurs in that subsystem. Instead we do proceed into attempting to start a headers sync.

  <details><summary>Diff to repro with fresh -datadir</summary>

  ```diff
  --- a/src/init.cpp
  +++ b/src/init.cpp
  @@ -1499,6 +1499,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
       const ArgsManager& args = *Assert(node.args);
       const CChainParams& chainparams = Params();

  +    SetMockTime(chainparams.GenesisBlock().Time() - 3h);
  +
       auto opt_max_upload = ParseByteUnits(args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET), ByteUnit::M);
       if (!opt_max_upload) {
           return InitError(strprintf(_("Unable to parse -maxuploadtarget: '%s'"), args.GetArg("-maxuploadtarget", "")));
  ```

  </details>

  Follow-up to #35351

ACKs for top commit:
  sedited:
    ACK 55390d1827
  jonatack:
    ACK 55390d1827

Tree-SHA512: 244a0cb634a0ba67fa88fe83f73111e475f6fff258cd1783b9b0a39669eefdd3b2e739a0b761616972bc938216336e18b0c0322e1201c2e805767cb813ca616c
2026-09-01 09:43:48 +01:00
merge-script
259553c662 Merge bitcoin/bitcoin#36131: rpc: Improve two field's OpenRPC types
78e691ea10 rpc: Change listunspent's ancestorfees type to NUM (sedited)
73fb9ced56 rpc: Fix private key type in signrawtransactionwithkey (sedited)

Pull request description:

  This corrects the types for two fields in the OpenRPC dump. Both changes have no effect on the rpc help output. The changes to the schema's format are:

  ```diff
  diff dump.json dump_new.json
  11452d11451
  <                 "x-bitcoin-unit": "amount",
  13844,13845c13843
  <               "type": "string",
  <               "pattern": "^[0-9a-fA-F]+$"
  ---
  >               "type": "string"
  ```

  I asked Claude to flag any inconsistencies in the dump and these were the two, out of many others, that I thought were worthwhile to fix.

ACKs for top commit:
  maflcko:
    lgtm ACK 78e691ea10
  stickies-v:
    ACK 78e691ea10
  musaHaruna:
    Tested ACK [78e691e](78e691ea10)

Tree-SHA512: 121d80520a39738c1c7375a50bb552203fe2db403cb3414195e6a79142677ac3c3509ba5f18d4b1982a8e2872c73e47cf6e54b6acd66b1a71ddcbe335ea33f34
2026-09-01 09:41:27 +01:00
Hodlinator
55390d1827 doc: Correct comment about which subsystem detects lagging clocks 2026-08-31 21:29:31 +02:00
merge-script
fe3c92cfe0 Merge bitcoin/bitcoin#36102: util: Replace !ContainsNoNUL() with ContainsNUL()
8d930981e9 refactor: Replace !ContainsNoNUL() with ContainsNUL() (Hodlinator)

Pull request description:

  Avoids frequent double negation. See also fa7078d84f when it was renamed from the previous name, "ValidAsCString()".

  Found while reviewing #35041.

ACKs for top commit:
  maflcko:
    lgtm ACK 8d930981e9
  l0rinc:
    code review ACK 8d930981e9
  sedited:
    ACK 8d930981e9
  janb84:
    ACK 8d930981e9

Tree-SHA512: 3ed1d264953f08272c115d760e8149c5985d63331404ac3b1017a277c4b3a61862915851fe45746e26ed46fe7478f851680d205e5ef83e727d061f2867fed99c
2026-08-31 19:57:03 +02:00
merge-script
58dfcf29f6 Merge bitcoin/bitcoin#35351: net: Disallow invalid HeadersSyncState due to lagging clock
ff3e2e4ebd net: Trigger process abort when behind start block MTP (Hodlinator)
1883cecb4d test: Characterize lagging-clock headers presync (Hodlinator)

Pull request description:

  ### Problem

  Headers presync computes `m_max_commitments` from the elapsed time since the chain-start MTP plus `MAX_FUTURE_BLOCK_TIME`. When the local system clock is more than `MAX_FUTURE_BLOCK_TIME` behind the chain-start MTP, that elapsed value is negative, but it is used in arithmetic assigned to the unsigned commitment cap. This can turn the intended zero bound into a large cap, letting low-work headers presync continue instead of aborting when a reasonable commitment cap would have been exceeded.

  ### Fix

  Instead of allowing an invalid `HeadersSyncState` object to be created, emit an error and **abort the node process**.

  Typically, the node will detect that the system clock is set too far in the past when comparing it to the chain tip during chain state loading and shut down before we start syncing headers. So in practice this is very unlikely to make a difference (might be possible if the system clock jumps backwards after we loaded the chain state).

  #### Commits

  * Add functional and unit characterization tests [pinning the current behavior](https://github.com/bitcoin/bitcoin/pull/35260).
  * The fix, along with corresponding test changes.

  ---

  Replaces #35208 which was clamping `m_max_commitments` to zero and then letting the `HeadersSyncState` consume headers until the block height either reached the the next `commitment_period` point and aborted, or reached the minimum work threshold and succeeded (possible when having been offline for >144 blocks).

ACKs for top commit:
  l0rinc:
    diff and code review ACK ff3e2e4ebd
  sedited:
    ACK ff3e2e4ebd
  mzumsande:
    Code Review ACK [ff3e2e4](ff3e2e4ebd)

Tree-SHA512: bdd82fd0609309aa4bea026db1b607ae856c53403ec01b2511fa2ccae9db4ff1bb9e39523b446583c09ae53823275b8a603050d9090b61fabb84fab35e458f28
2026-08-31 18:35:19 +02:00
merge-script
c0ed327845 Merge bitcoin/bitcoin#36103: validation: remove unused code
15630c7b85 validation: remove unused m_chainparams from ATMPArgs (fanquake)
84c5290149 validation: remove unused args from PolicyScriptChecks (fanquake)
a9d5cf7f99 validation: remove unused args from ConsensusScriptChecks (fanquake)
d26dc09ee3 validation: remove unused total_vsize arg from PackageRBFChecks (fanquake)
2cb6c156e1 validation: remove unused PackageMempoolAcceptResult constructor (fanquake)

Pull request description:

  Remove some unused code from validation.

ACKs for top commit:
  thomasbuilds:
    ACK 15630c7
  sedited:
    ACK 15630c7b85
  yuvicc:
    ACK 15630c7b85
  hebasto:
    ACK 15630c7b85, completeness of removing unused parameters in the `validation` module verified by overriding the `-Wunused-parameter` compiler flag for `src/validation.cpp`.
  jeanpablojp:
    tACK 15630c7b85

Tree-SHA512: a01ff6ea758132d6ad4c163d51c36d9e2cfaf91e90ca6451323591341fefec23c875af26e0b66e6cdba87ae6cab1418048c8788361d9c62fb8e0400d4dcaeac7
2026-08-31 18:22:50 +02:00
sedited
78e691ea10 rpc: Change listunspent's ancestorfees type to NUM
This seems to be the only place where a STR_AMOUNT is used for a sats
denominated fee amount. Many other places use the raw NUM type for a fee
amount, for example getblockstats and getblocktemplate. This doesn't
change the actual result of the RPC call.

The change is motivated by OpenRPC, where the field was previously given
a 'x-bitcoin-unit' tag. This usually describes a decimal amount, and may
be confusing for consumers applying this tag.
2026-08-31 17:12:59 +02:00
sedited
73fb9ced56 rpc: Fix private key type in signrawtransactionwithkey
It is base58, so shouldn't be qualified with STR_HEX. Similarly,
signmessagewithprivkey also declares the argument as a STR.

This fix is motivated by the OpenRPC dump, where fields tagged with STR_HEX are
described with a restricting regex that would make its correct usage a
violation against the unpatched schema.
2026-08-31 17:12:40 +02:00
merge-script
128e5c6805 Merge bitcoin/bitcoin#35477: test: exercise Schnorr signature cache in txvalidationcache_tests.cpp
3ba1bbfa3f test: exercise Schnorr signature cache in txvalidationcache_tests.cpp (Sebastian Falbesoner)
198b36bc85 test: respect "TAPROOT requires WITNESS" rule in `ValidateCheckInputsForAllFlags` (Sebastian Falbesoner)
e78a2a0d00 test: refactor: simplify tx vin/vout creation in txvalidationcache_tests.cpp (Sebastian Falbesoner)

Pull request description:

  The Schnorr verification path of the signature cache is currently never hit in the unit tests, i.e. with the following patch they still pass:
  ```diff
  diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
  index c6fcc8f8eb..87688c1049 100644
  --- a/src/script/sigcache.cpp
  +++ b/src/script/sigcache.cpp
  @@ -44,6 +44,7 @@ void SignatureCache::ComputeEntryECDSA(uint256& entry, const uint256& hash, cons

   void SignatureCache::ComputeEntrySchnorr(uint256& entry, const uint256& hash, std::span<const unsigned char> sig, const XOnlyPubKey& pubkey) const
   {
  +    assert(false);
       CSHA256 hasher = m_salted_hasher_schnorr;
       hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin());
   }
  ```
  This PR adds missing coverage for that by adding a Taproot key-path spend to `checkinputs_test` in `txvalidationcache_tests.cpp`. Same as for the already-existing ECDSA spends, the caching is tested across a large number of flag combinations (using `ValidateCheckInputsForAllFlags`), both with an invalid Schnorr signature (-> should only fail if `SCRIPT_VERIFY_TAPROOT` is set) and a valid one (-> should pass for all flag combinations).

ACKs for top commit:
  Bortlesboat:
    tACK 3ba1bbfa3f
  sedited:
    ACK 3ba1bbfa3f
  instagibbs:
    ACK 3ba1bbfa3f

Tree-SHA512: e43f7077d9e9ab6f8b5e9e70f0187767d65f686ce24350ce5d61cc4cdf07d5eebdf5e4327ce665c212b7cc02be1e8632a6a9fbcf6be2916f8e058993e5fb2650
2026-08-31 15:31:15 +02:00
merge-script
d2e24e951d Merge bitcoin/bitcoin#36054: test: add script_tests cases covering interpreter mutants
4a12773f26 test: cover DERSIG rejects a non-compound signature type (ViniciusCestarii)
86c7fb910d test: cover OP_16 does not count towards the opcode limit (ViniciusCestarii)
331bf79881 test: cover OP_WITHIN must pop all 3 elements (ViniciusCestarii)
3bb87bc61b test: cover OP_FROMALTSTACK must pop the altstack (ViniciusCestarii)

Pull request description:

  Kills some live mutants on interpreter.cpp that affect consensus found by https://bitcoincore.space. They are:

  <details>
  <summary><a href="https://bitcoincore.space/src/script/interpreter.cpp#3951">interpreter.cpp#3951</a>: <code>OP_FROMALTSTACK</code>: removed <code>popstack(altstack)</code></summary>

  ```diff
  diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
  index 98b16eca6b..68265d20b5 100644
  --- a/src/script/interpreter.cpp
  +++ b/src/script/interpreter.cpp
  @@ -698,7 +698,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
                       if (altstack.size() < 1)
                           return set_error(serror, SCRIPT_ERR_INVALID_ALTSTACK_OPERATION);
                       stack.push_back(altstacktop(-1));
  -                    popstack(altstack);
  +
                   }
                   break;
  ```

  </details>

  <details>
  <summary><a href="https://bitcoincore.space/src/script/interpreter.cpp#4084">interpreter.cpp#4084</a>: <code>OP_WITHIN</code>: removed one <code>popstack(stack)</code></summary>

  ```diff
  diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
  index 98b16eca6b..874cf5e1cf 100644
  --- a/src/script/interpreter.cpp
  +++ b/src/script/interpreter.cpp
  @@ -1018,7 +1018,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
                       CScriptNum bn2(stacktop(-2), fRequireMinimal);
                       CScriptNum bn3(stacktop(-1), fRequireMinimal);
                       bool fValue = (bn2 <= bn1 && bn1 < bn3);
  -                    popstack(stack);
  +
                       popstack(stack);
                       popstack(stack);
                       stack.push_back(fValue ? vchTrue : vchFalse);
  ```

  </details>

  <details>
  <summary><a href="https://bitcoincore.space/src/script/interpreter.cpp#3883">interpreter.cpp#3883</a>: opcode limit: <code>opcode > OP_16</code> → <code>opcode >= OP_16</code></summary>

  ```diff
  diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
  index 98b16eca6b..e985643606 100644
  --- a/src/script/interpreter.cpp
  +++ b/src/script/interpreter.cpp
  @@ -459,7 +459,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&

               if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) {
                   // Note how OP_RESERVED does not count towards the opcode limit.
  -                if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) {
  +                if (opcode >= OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) {
                       return set_error(serror, SCRIPT_ERR_OP_COUNT);
                   }
               }
  ```

  </details>

  <details>
  <summary><a href="https://bitcoincore.space/src/script/interpreter.cpp#3808">interpreter.cpp#3808</a>: <code>IsValidSignatureEncoding</code>: compound type check returns <code>true</code></summary>

  ```diff
  diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
  index 98b16eca6b..b613a6ac19 100644
  --- a/src/script/interpreter.cpp
  +++ b/src/script/interpreter.cpp
  @@ -133,7 +133,7 @@ bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) {
       if (sig.size() > 73) return false;

       // A signature is of type 0x30 (compound).
  -    if (sig[0] != 0x30) return false;
  +    if (sig[0] != 0x30) return true;

       // Make sure the length covers the entire signature.
       if (sig[1] != sig.size() - 3) return false;
  ```

  </details>

  Recommend reviewing per commit.

ACKs for top commit:
  instagibbs:
    ACK 4a12773f26
  brunoerg:
    ACK 4a12773f26
  jeanpablojp:
    tACK 4a12773f26

Tree-SHA512: 5f53c733d11cb5d645f420d90ab626f894ef0bb155d01b9de0cae502109b2eaa46c072797d08df115da7a8738f01f31212a207a4d0e6f782128beb37332cf46e
2026-08-31 10:01:42 +01:00
merge-script
ca7162cde5 Merge bitcoin/bitcoin#35868: rpc, wallet: fix invalid JSON in HelpExampleRpc curl examples
21d4e0ba75 rpc, wallet, test: fix invalid JSON in HelpExampleRpc curl examples (GuTS805)

Pull request description:

  Several `HelpExampleRpc` call sites reused CLI-style argument strings
  verbatim instead of valid JSON — missing commas, bare unquoted words, or
  single backslashes that are not valid JSON escapes. As a result the
  documented `curl` command for 14 RPCs (`getblockfrompeer`, `addnode`,
  `addconnection`, `sendmsgtopeer`, `restorewallet`, `getmempoolcluster`,
  `importmempool`, `getindexinfo`, `listlabels`, `unloadwallet`,
  `createwalletdescriptor`, `addhdkey`, `loadwallet`, `listunspent`) fails
  to parse as JSON if copy-pasted as-is. Also fixes a stray trailing quote
  in the `restorewallet` named-argument examples.

  This was previously raised in #31275, which sipa confirmed at runtime by
  adding a `UniValue::read` check, but that PR was closed unmerged. Since
  then two more examples broke the same way (`getmempoolcluster`,
  `addhdkey`), which is why this adds a permanent regression check to
  `rpc_help.py::dump_help()` instead of just fixing the current list.

  Fixes #35864.

ACKs for top commit:
  maflcko:
    review ACK 21d4e0ba75 🚝
  sedited:
    ACK 21d4e0ba75

Tree-SHA512: 2a8abc07d681b9dc81b8079a68421278da890049cea33a1561a48d53cbf919a30df588f559e9df94fa4a1ab7027f742f3b12c163afc25246a620340cb3522336
2026-08-29 10:33:22 +02:00
merge-script
d0e777baf5 Merge bitcoin/bitcoin#36111: rpc: bound memory for overlong Bech32 errors
7fcaccd9d0 bech32: bound overlength error locations (Lőrinc)

Pull request description:

  **Problem:** `validateaddress` reports likely error positions for invalid Bech32 inputs, including multiple useful positions for character and checksum errors.
  For an overlength input, `LocateErrors()` returns every position after the 90-character limit, which the RPC converts to a `UniValue` number before serializing the response.
  A near-limit authenticated request therefore creates about 33 million `int` values and 33 million `UniValue` objects.

  **Fix:** Return position 90 for an overlength input, which identifies where the single length violation begins.
  Character and checksum errors continue to return multiple useful positions when they can be determined.
  The tests now include an oversized example and pin the bounded result.

  **Reproducer:** Peak memory usage for a near-limit authenticated request:

  <details>
  <summary>Linux reproducer</summary>

  ```bash
  sed -i "/def test_validateaddress(self):/a\\
          self.nodes[0].validateaddress('bcrt1' + 'q' * (2**25 - 100))\\
          __import__('time').sleep(30)" test/functional/rpc_invalid_address_message.py
  cmake -B build && cmake --build build -j2
  build/test/functional/rpc_invalid_address_message.py >/dev/null 2>&1 &
  sleep 20 && awk '/VmHWM/' /proc/$(pgrep bitcoind)/status
  ```
  </details>

  ```text
  Before ████████████████████████ 5.69 GiB
  After  █░░░░░░░░░░░░░░░░░░░░░░░  240 MiB
  ```

ACKs for top commit:
  maflcko:
    lgtm ACK 7fcaccd9d0
  sedited:
    ACK 7fcaccd9d0
  janb84:
    ACK 7fcaccd9d0

Tree-SHA512: 3d439774d394f081b8107f8131963f7aa23ed048b0d6d349a80f9b3481fefeef7b5ce239fbd33606ad1f4960e6bfd899f968c50febc70d38b2fe731c6049583f
2026-08-29 10:20:42 +02:00