-
Notifications
You must be signed in to change notification settings - Fork 1.8k
fix(net): correct block validation, fetching, and peer contribution #7005
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release_v4.8.3
Are you sure you want to change the base?
Changes from all commits
9a5d1b6
1afae71
2d5125f
1f4eb95
29312f3
fbc11c6
018b8c4
fa3b818
a25d6dd
9e3f752
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |
| import org.springframework.stereotype.Component; | ||
| import org.tron.common.prometheus.MetricKeys; | ||
| import org.tron.common.prometheus.Metrics; | ||
| import org.tron.common.utils.Sha256Hash; | ||
| import org.tron.core.Constant; | ||
| import org.tron.core.capsule.BlockCapsule; | ||
| import org.tron.core.capsule.BlockCapsule.BlockId; | ||
|
|
@@ -77,6 +78,12 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep | |
| check(peer, blockMessage); | ||
| } | ||
|
|
||
| if (blockCapsule.getNum() <= 0 | ||
| || blockCapsule.getParentHashStr().size() != Sha256Hash.LENGTH | ||
| || new BlockId(blockCapsule.getParentHash()).getNum() != blockCapsule.getNum() - 1) { | ||
| throw new P2pException(TypeEnum.BAD_BLOCK, "block number does not follow parent"); | ||
| } | ||
|
|
||
| blockMessage.sanitize(); | ||
|
|
||
| if (peer.getSyncBlockRequested().containsKey(blockId)) { | ||
|
|
@@ -89,7 +96,12 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep | |
| if (peer.isRelayPeer()) { | ||
| peer.getAdvInvSpread().put(item, now); | ||
| } | ||
| Long time = peer.getAdvInvRequest().remove(item); | ||
| Long time = peer.getAdvInvRequest().get(item); | ||
| long interval = blockId.getNum() - tronNetDelegate.getHeadBlockId().getNum(); | ||
| BlockResult result = processBlock(peer, blockMessage.getBlockCapsule()); | ||
| if (result == BlockResult.ACCEPTED || result == BlockResult.SYNC_REQUIRED) { | ||
| peer.setBlockRcvTime(System.currentTimeMillis()); | ||
| } | ||
| if (null != time) { | ||
| MetricsUtil.histogramUpdateUnCheck(MetricsKey.NET_LATENCY_FETCH_BLOCK | ||
| + peer.getInetAddress(), now - time); | ||
|
|
@@ -98,9 +110,6 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep | |
| } | ||
| Metrics.histogramObserve(MetricKeys.Histogram.BLOCK_RECEIVE_DELAY, | ||
| (now - blockMessage.getBlockCapsule().getTimeStamp()) / Metrics.MILLISECONDS_PER_SECOND); | ||
| fetchBlockService.blockFetchSuccess(blockId); | ||
| long interval = blockId.getNum() - tronNetDelegate.getHeadBlockId().getNum(); | ||
| processBlock(peer, blockMessage.getBlockCapsule()); | ||
| logger.info( | ||
| "Receive block/interval {}/{} from {} fetch/delay {}/{}ms, " | ||
| + "txs/process {}/{}ms, witness: {}", | ||
|
|
@@ -125,46 +134,62 @@ private void check(PeerConnection peer, BlockMessage msg) throws P2pException { | |
| } | ||
| } | ||
|
|
||
| private void processBlock(PeerConnection peer, BlockCapsule block) throws P2pException { | ||
| private BlockResult processBlock(PeerConnection peer, BlockCapsule block) throws P2pException { | ||
| BlockId blockId = block.getBlockId(); | ||
| boolean flag = tronNetDelegate.validBlock(block); | ||
| if (!flag) { | ||
| logger.warn("Receive a bad block from {}, {}, {}", | ||
| boolean activeWitness = tronNetDelegate.validBlock(block); | ||
| // Retain pending fetch/request state if validation throws so disconnect can retry. | ||
| // Otherwise, complete the fetch; inactive witnesses trigger sync recovery below. | ||
| fetchBlockService.blockFetchSuccess(blockId); | ||
| peer.getAdvInvRequest().remove(new Item(blockId, InventoryType.BLOCK)); | ||
| if (!activeWitness) { | ||
| logger.warn("Receive a block from an inactive witness, peer {}, block {}, witness {}", | ||
| peer.getInetSocketAddress(), blockId.getString(), | ||
| Hex.toHexString(block.getWitnessAddress().toByteArray())); | ||
| return; | ||
| syncService.startSync(peer); | ||
| return BlockResult.STATE_FAILED; | ||
|
Comment on lines
+144
to
+149
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [SHOULD] Limit repeated no-progress synchronization triggered by inactive signers. A Please consider adding a retry limit or cooldown for repeated recovery attempts that do not result in verified chain progress, while preserving the legitimate recovery path when the local node is actually behind or its witness set is stale.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We prefer to keep The concern about repeated recovery attempts without progress is valid. We’ll address retry limits and activity accounting in a separate PR while preserving this recovery path. |
||
| } | ||
|
|
||
| peer.updateLastInteractiveTime(System.currentTimeMillis()); | ||
| long headNum = tronNetDelegate.getHeadBlockId().getNum(); | ||
| if (block.getNum() < headNum || tronNetDelegate.containBlock(blockId)) { | ||
| logger.warn("Receive a low block {}, head {}", blockId.getString(), headNum); | ||
| return BlockResult.IGNORED; | ||
| } | ||
|
|
||
| if (!tronNetDelegate.containBlock(block.getParentBlockId())) { | ||
| logger.warn("Get unlink block {} from {}, head is {}", blockId.getString(), | ||
| peer.getInetAddress(), tronNetDelegate.getHeadBlockId().getString()); | ||
| syncService.startSync(peer); | ||
| return; | ||
| } | ||
|
|
||
| long headNum = tronNetDelegate.getHeadBlockId().getNum(); | ||
| if (block.getNum() < headNum) { | ||
| logger.warn("Receive a low block {}, head {}", blockId.getString(), headNum); | ||
| return; | ||
| return BlockResult.SYNC_REQUIRED; | ||
| } | ||
|
|
||
| broadcast(new BlockMessage(block)); | ||
|
|
||
| try { | ||
| tronNetDelegate.processBlock(block, false); | ||
| peer.setBlockRcvTime(System.currentTimeMillis()); | ||
| witnessProductBlockService.validWitnessProductTwoBlock(block); | ||
|
|
||
| Item item = new Item(blockId, InventoryType.BLOCK); | ||
| tronNetDelegate.getActivePeer().forEach(p -> { | ||
| if (p.getAdvInvReceive().getIfPresent(item) != null) { | ||
| p.setBlockBothHave(blockId); | ||
| } | ||
| }); | ||
| } catch (Exception e) { | ||
| logger.warn("Process adv block {} from peer {} failed. reason: {}", | ||
| blockId, peer.getInetAddress(), e.getMessage()); | ||
| syncService.startSync(peer); | ||
| return BlockResult.STATE_FAILED; | ||
| } | ||
| if (tronNetDelegate.isHitDown()) { | ||
| return BlockResult.IGNORED; | ||
| } | ||
| advService.confirmBlockInventory(blockId); | ||
| witnessProductBlockService.validWitnessProductTwoBlock(block); | ||
|
|
||
| Item item = new Item(blockId, InventoryType.BLOCK); | ||
| tronNetDelegate.getActivePeer().forEach(p -> { | ||
| if (p.getAdvInvReceive().getIfPresent(item) != null) { | ||
| p.setBlockBothHave(blockId); | ||
| } | ||
| }); | ||
| return BlockResult.ACCEPTED; | ||
| } | ||
|
|
||
| private enum BlockResult { | ||
| ACCEPTED, SYNC_REQUIRED, IGNORED, STATE_FAILED | ||
| } | ||
|
|
||
| private void broadcast(BlockMessage blockMessage) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[SHOULD] Do not unconditionally update the activity timestamp for sync requests that make no progress.
A fully synchronized peer with
remainNum=0can repeatedly send a summary containing only the local node’s current head. The response contains the same single block withremainNumstill at 0, so no synchronization progress is made, yetlastInteractiveTimeis still updated here after processing. At the same time,check()bypasses the message rate limiter whenremainNum=0.As a result, even after tightening the accounting for
INV/BLOCK, this path can still be used to avoid applicable inactivity filtering and reduce the peer’s likelihood of random eviction.Please move the activity update to a point where response progress can be determined, avoid crediting single-block confirmation responses as useful activity, and apply rate limiting to requests with
remainNum=0as well.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your review. Repeated single-BlockId
SYNC_BLOCK_CHAINrequests can refresh peer activity without actual synchronization progress. We’ll address this known issue in a separate PR, while preserving legitimate startup/restart and sync-completion behavior. This PR will retain the existingSYNC_BLOCK_CHAINhandling.