Repository navigation
Conversation
…quested.getKey().peekLast()
| } | ||
| } | ||
|
|
||
| if (msg.getRemainNum() == 0) { |
There was a problem hiding this comment.
[SHOULD] Complete synchronization for terminal inventories containing only known blocks.
This guard still accepts a multi-block terminal response once its last block reaches the HELLO height. If every returned block is already known, processMessage() drains syncBlockToFetch but falls through to syncNext() at lines 95–101, leaving needSyncFromPeer=true.
A peer can repeatedly return a summary-linked suffix of known blocks with remainNum=0. Each round refreshes both the shared-block timestamp and the inventory-request timestamp, and rebuilds the summary under forkLock. For prolonged operation, the peer can periodically advance the suffix to blocks already obtained from honest peers before the summary’s lower bound overtakes it.
Please complete synchronization when remainNum == 0 && syncBlockToFetch.isEmpty() after removing known blocks, preserving needSyncFromUs. The existing testKnownMultiBlockResponseRequestsNextSummary currently asserts the redundant continuation and should instead assert completion and no further syncNext() call.
There was a problem hiding this comment.
Thanks for the review. Fixed in 562e004. After removing locally known blocks, a terminal inventory with an empty fetch queue now completes the download without calling syncNext(), while preserving needSyncFromUs. I updated the regression test to cover two- and three-block responses with needSyncFromUs both true and false.
SeriousCoding789
left a comment
There was a problem hiding this comment.
The fixes here look correct to me: the request snapshot is validated before any state change, the future-height check no longer overflows, negative remainNum is rejected, and the new chain-inventory timeout uses the request's own timestamp.
One gap seems to remain beyond the terminal-response case raised in the review above. Intermediate pages (remainNum > 0) are not covered by the HELLO-head check, and a full page made up only of blocks we already have can pass every check without moving blockBothHave forward; the handler then drains the queue and calls syncNext() again. The underlying reason is that the 30-second progress check in PeerStatusCheck looks at blockBothHaveUpdateTime, which setBlockBothHave() refreshes whether or not the height increased, so activity is counted as progress.
Two possible directions:
- For
remainNum > 0, require the last block of the page to be higher thanpeer.getBlockBothHave().getNum(). An honest page starts from the highest summary block on the peer's main chain and spansSYNC_FETCH_BATCH_NUMblocks, and fork depth is bounded by the solidified height, so an honest page should always end aboveblockBothHave. - More generally, refresh
blockBothHaveUpdateTimeonly when the shared height increases, so any response shape that does not advance synchronization is caught by the existing 30-second check. The othersetBlockBothHave()callers (broadcast handling, serving fetches) would need to be checked against that change.
| if (chainInventoryMessage.getRemainNum() == 0 && peer.getSyncBlockToFetch().isEmpty()) { | ||
| peer.setTronState(TronState.SYNC_COMPLETED); | ||
| peer.setNeedSyncFromPeer(false); | ||
| return; | ||
| } |
There was a problem hiding this comment.
[SHOULD] Premature completion of a multi-block terminal response leaves the remote peer in upload state.
Introduced by commit 562e004e31.
The new early return can complete local synchronization without clearing the remote peer's upload state.
Reproduction scenario:
- Peer A is at height 100 and requests a chain summary from peer B at height 102.
- B responds with
[100, 101, 102]andremainNum=0, settingneedSyncFromUs=trueon its connection. - Before the response arrives, A obtains blocks 101 and 102 from other peers.
- The new handler finds all returned blocks already known, completes synchronization, and returns without sending the final summary confirmation.
A now considers synchronization complete, but B still believes A is downloading. Consequently, B drops subsequent block and transaction announcements from A and stops broadcasting announcements to A until another synchronization or reconnection resets the state.
The remote upload flag is cleared only when a subsequent summary request produces a single-block response in SyncBlockChainMsgHandler.java (line 48). The affected announcement filters are in InventoryMsgHandler.java (line 72) and AdvService.java (line 306).
Confirmed by a two-peer handler regression test:
| Behavior | Current code | Pre-change handler |
|---|---|---|
| A completes synchronization | Yes | Yes |
| B clears upload state | No | Yes |
| B accepts subsequent block/transaction announcements | Both dropped | Both accepted |
Please preserve a bounded final confirmation exchange so the remote upload state is cleared, while retaining protection against repeated summary loops. Add a two-peer regression test covering this multi-block terminal response.
The existing multi-block tests only verify local state and therefore miss this cross-peer regression.
There was a problem hiding this comment.
Thanks for pointing out the asymmetric peer state. Fixed in 82feab3: when all blocks in a multi-block terminal response are already available locally, we send an updated chain summary instead of completing sync immediately. The remote peer can then return a single-block response and clear needSyncFromUs. I added a two-peer regression test covering sync completion and subsequent inventory processing.
| public static final long ADV_TIME_OUT = 20000L; | ||
| public static final long SYNC_TIME_OUT = 5000L; | ||
| public static final long SYNC_TIME_OUT = 10000L; | ||
| public static final long NET_MAX_TRX_PER_SECOND = 700L; |
There was a problem hiding this comment.
Please also consider reducing ADV_TIME_OUT. The current 20-second timeout for block and transaction fetch requests following broadcast announcements is rather long, delaying the handling of unresponsive peers and the rescheduling of outstanding requests.
| private void check(PeerConnection peer, ChainInventoryMessage msg) throws P2pException { | ||
| if (peer.getSyncChainRequested() == null) { | ||
| HelloMessage hello = peer.getHelloMessageReceive(); | ||
| if (hello == null) { |
| } | ||
| } | ||
|
|
||
| if (chainInventoryMessage.getRemainNum() == 0 && peer.getSyncBlockToFetch().isEmpty()) { |
There was a problem hiding this comment.
Synchronization should not be marked complete here solely because remainNum is zero and the fetch queue is empty. When the remote peer returns multiple blocks, it sets needSyncFromUs=true. We still need to send an updated chain summary through syncNext so that the remote peer can return a single common block and clear that state. Local synchronization can then complete through the existing single-block branch.
Please remove this early-completion branch. Repeated responses containing known blocks from a malicious peer should be handled by separately limiting repeated exchanges that make no progress, without skipping the normal final confirmation exchange. Tests should also cover synchronization state on both peers and verify that subsequent announcement exchange resumes.
There was a problem hiding this comment.
Agreed. I removed the early completion branch in 82feab3. To prevent the previous no-progress loop, the handler now rejects a multi-block terminal response if its terminal block was already included in the requested summary; at that point, a normal peer should return a single block. A regression test covers the repeated-response case.
Keep sync completion handling while applying the release branch block lock to fetch decisions. Retain tests for both behaviors.
What does this PR do?
Fix chain-summary request timeouts and local sync completion, allow more time for sync block batches, and reject terminal chain-inventory responses that contradict the peer's HELLO head.
SYNC_TIME_OUTfrom five to ten seconds. It applies to both outstandingsyncChainRequestedchain-summary requests and requested sync blocks. A sync fetch can request up to 100 blocks from one peer in a single batch. The periodic status check disconnects the peer withTIME_OUTafter the threshold is exceeded. Chain-summary requests use their original request timestamp; other traffic and block progress do not extend that deadline.remainNum == 0, require the last block height to be at least the HELLO head. Reject lower terminal responses withSYNC_FAILED, mapped to aSYNC_FAILdisconnect. Intermediate pages withremainNum > 0may still end below HELLO.remainNumand finish the local download while preservingneedSyncFromUs. KeepTronState.SYNC_COMPLETEDso a later download can restart; unknown queued blocks continue through the fetch path.Why are these changes required?
The five-second timeout could expire before all blocks in a batch of up to 100 arrive from a responsive peer, causing a premature disconnect and interrupting synchronization. Ten seconds gives the batch more time while retaining a deadline for unresponsive peers. The block-progress timeout also does not provide a deadline for an individual chain-summary request. Repeated terminal responses containing known blocks below the HELLO head can refresh progress timestamps and trigger more summary requests without advancing synchronization. The terminal-height check closes this gap.
A single-block response does not initiate a remote download. For example, when HELLO advertised height 50 and our summary ends at 100, a known-block response
[50], remainNum=0can legitimately finish our download. SettingneedSyncFromUs=truewould instead suppress normal inventory exchange while waiting for a remote download that never starts. Preserving the upload flag retains any existing upload synchronization without inventing a new one.This PR has been tested by:
PeerStatusCheckMockTestandPeerStatusCheckTestpassed. These cover six-second chain-summary and sync-block requests remaining active, as well as requests past the ten-second threshold timing out.syncNext()on rejection; the actualSYNC_FAILdisconnect reason; valid terminal heights; and pagination below HELLO.checkstyleMainandcheckstyleTestpassed during development. After the timeout adjustment,:framework:checkstyleTestandgit diff --checkpassed.Compatibility and integration notes
In rare cases, a fork rollback can temporarily put an honest peer below its HELLO head and cause a
SYNC_FAILdisconnect. This transient disconnect is an accepted tradeoff: the peer may reconnect with a fresh HELLO after the default one-minute cooldown. Reconnection is not guaranteed exactly one minute later.The height check enforces consistency with the peer's HELLO advertisement; it does not prove the peer's actual current head. Chain-inventory responses do not receive block contribution credit.
When integrating with #6993, retain both HELLO and chain-summary timeout checks in the common status-check flow.