diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-12 15:48:57 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-23 04:37:36 +0000 |
| commit | 49167989ad8f39f7d617e2e081b848678465b7ed (patch) | |
| tree | d9264a36c6e74edd52a3131bdc685ebaba400210 /src | |
| parent | 5335d2c75555188129504605ac70ef72fef91c2c (diff) | |
net: give up waiting for a message body that is never going to arrive
THIS IS THE WEDGE, and the note asking for this fix has been in the file
since Bitcoin 0.1: 'need a mechanism to give up waiting for overlong
message size error'.
ProcessMessages reads a header, finds the body has not all arrived, pushes
the header back onto the front of vRecv and breaks. Correct for a message
still in flight. For one that never completes it is permanent: every later
pass re-reads the same header and breaks in the same place, so that peer is
never parsed again for the life of the process -- no blocks, no inv, not
even its version message.
Nothing rescues it:
- a header may claim up to 256 MB (0x10000000) and still pass
CMessageHeader::IsValid(), so a garbage or truncated header is accepted
and then waited on forever;
- nLastRecv is refreshed by any byte that arrives (net.cpp), whether or
not it parses, so the inactivity timeout that drops a silent peer never
fires on a peer that is merely unreadable;
- the socket stays ESTABLISHED and select() keeps offering it, so nothing
at the socket layer notices either.
It also explains the signature that made no sense. A peer stuck before its
version message is parsed keeps nStartingHeight == -1, so a node with a full
vNodes still reports GetPeerMedianHeight() == -1 -- which is exactly what
was observed, and exactly what disabled the pool's behind-the-network gate
and let it fork.
Evidence, from two wedges captured on the pool by the watchdog:
MESSAGE-BREAK 2 ran 2014 times in the deaf window against 607 in the healthy
window before it, while blocks received went to zero and the node kept
processing miner traffic normally throughout.
Now: a peer that announces a body and does not deliver it within two minutes
is dropped, and says so in the log unconditionally. Two minutes is far
beyond any legitimate header-to-body gap and well inside
BTF_RECV_TIMEOUT_SECS.
(cherry picked from commit ca1e99288e03ac7beb2b97cf615a3255b4635d95)
Diffstat (limited to 'src')
| -rw-r--r-- | src/net.h | 23 |
1 files changed, 23 insertions, 0 deletions
| @@ -127,6 +127,25 @@ static const int64 DISCONNECT_DRAIN_SECS = 60; | |||
| 127 | // 3966-4085 -- 120 blocks, 5890.50 BTF -- were mined in exactly this state. | 127 | // 3966-4085 -- 120 blocks, 5890.50 BTF -- were mined in exactly this state. |
| 128 | static const int64 BTF_NO_PEER_HEIGHT_MAX_SECS = 20 * 60; | 128 | static const int64 BTF_NO_PEER_HEIGHT_MAX_SECS = 20 * 60; |
| 129 | 129 | ||
| 130 | // How long a peer may leave this node waiting for the rest of a message it | ||
| 131 | // announced in a header. | ||
| 132 | // | ||
| 133 | // ProcessMessages rewinds the header and breaks when the body has not all | ||
| 134 | // arrived, which is correct for a message still in flight and unbounded for | ||
| 135 | // one that never completes: the same header is re-read, the loop breaks | ||
| 136 | // again, and that peer is never parsed again for the life of the process. | ||
| 137 | // Satoshi left the note -- "need a mechanism to give up waiting for overlong | ||
| 138 | // message size error" -- and this is it. | ||
| 139 | // | ||
| 140 | // It never self-corrects, because any byte that arrives refreshes nLastRecv, | ||
| 141 | // so the inactivity timeout that would otherwise drop the peer never fires. | ||
| 142 | // The socket stays healthy, select() keeps reporting it, and the node goes | ||
| 143 | // silently deaf one peer at a time. | ||
| 144 | // | ||
| 145 | // A header may legitimately outrun its body by a round trip or two; two | ||
| 146 | // minutes is far beyond that, and well inside BTF_RECV_TIMEOUT_SECS. | ||
| 147 | static const int64 BTF_MSG_WAIT_MAX_SECS = 2 * 60; | ||
| 148 | |||
| 130 | // Send a ping after this long with nothing to say, so a peer running the | 149 | // Send a ping after this long with nothing to say, so a peer running the |
| 131 | // inactivity check above does not mistake a quiet node for a dead one. Must | 150 | // inactivity check above does not mistake a quiet node for a dead one. Must |
| 132 | // stay well under BTF_RECV_TIMEOUT_SECS. | 151 | // stay well under BTF_RECV_TIMEOUT_SECS. |
| @@ -651,6 +670,10 @@ public: | |||
| 651 | string strBtfAddr; | 670 | string strBtfAddr; |
| 652 | string strBtfMeeting; | 671 | string strBtfMeeting; |
| 653 | 672 | ||
| 673 | // When this peer's message loop first rewound a header to wait for the | ||
| 674 | // rest of a message, or 0 when it is not waiting. See ProcessMessages. | ||
| 675 | int64 nMsgWaitSince; | ||
| 676 | |||
| 654 | // Height this peer announced in its version message, or -1 if it sent a | 677 | // Height this peer announced in its version message, or -1 if it sent a |
| 655 | // version message that predates the field. Knowing how far along everyone | 678 | // version message that predates the field. Knowing how far along everyone |
| 656 | // else is, is the only way a node can tell "in sync" from "not hearing". | 679 | // else is, is the only way a node can tell "in sync" from "not hearing". |