-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(net): optimize disconnectRandom by tracking block receive time #6704
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
Changes from all commits
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 |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.stereotype.Component; | ||
| import org.tron.common.utils.Sha256Hash; | ||
| import org.tron.core.capsule.BlockCapsule.BlockId; | ||
| import org.tron.core.config.args.Args; | ||
| import org.tron.core.exception.P2pException; | ||
| import org.tron.core.exception.P2pException.TypeEnum; | ||
|
|
@@ -44,7 +45,10 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep | |
| peer.getAdvInvReceive().put(item, System.currentTimeMillis()); | ||
| advService.addInv(item); | ||
| if (type.equals(InventoryType.BLOCK) && peer.getAdvInvSpread().getIfPresent(item) == null) { | ||
| peer.setLastInteractiveTime(System.currentTimeMillis()); | ||
| long headNum = tronNetDelegate.getHeadBlockId().getNum(); | ||
| if (new BlockId(id).getNum() > headNum) { | ||
| peer.setLastInteractiveTime(System.currentTimeMillis()); | ||
| } | ||
|
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] Good comparing. If new BlockId(id).getNum() < solidity Num, we should set the LastInteractiveTime as very old time.
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. If the time isn't refreshed, the time will fall behind, so the added logic isn't very significant. It's also possible that the other party, due to network latency, is slightly behind and might be mistakenly affected.
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. Let's improve it, suppose if new BlockId(id).getNum() < solidityNum - THRESHOLD, it takes into account the impact of network latency under extreme conditions. Since you’ve modified this part, it’s recommended to imporove the logic here.
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. I think this is redundant logic. Besides increasing complexity, it doesn't provide any defensive capability. Previously, malicious nodes would do this because broadcasting blocks below the solidified block level could refresh the time. Now that refreshing is no longer possible, and there's no longer a list of blocks below the solidified block level being broadcast. |
||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,10 @@ | |
| import static org.tron.common.math.Maths.ceil; | ||
| import static org.tron.common.math.Maths.max; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Comparator; | ||
| import java.util.HashMap; | ||
| import java.util.IdentityHashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Optional; | ||
|
|
@@ -44,7 +46,7 @@ public class ResilienceService { | |
|
|
||
| @Autowired | ||
| private ChainBaseManager chainBaseManager; | ||
|
|
||
| public void init() { | ||
| if (Args.getInstance().isOpenFullTcpDisconnect) { | ||
| executor.scheduleWithFixedDelay(() -> { | ||
|
|
@@ -86,6 +88,7 @@ private void disconnectRandom() { | |
| .collect(Collectors.toList()); | ||
|
|
||
| if (peers.size() >= minBroadcastPeerSize) { | ||
| peers = getRandomDisconnectionPeers(peers); | ||
| long now = System.currentTimeMillis(); | ||
| Map<Object, Integer> weights = new HashMap<>(); | ||
| peers.forEach(peer -> { | ||
|
|
@@ -121,6 +124,14 @@ private void disconnectRandom() { | |
| } | ||
|
|
||
|
|
||
| private List<PeerConnection> getRandomDisconnectionPeers(List<PeerConnection> peers) { | ||
| Map<PeerConnection, Long> snapshot = new IdentityHashMap<>(peers.size()); | ||
| peers.forEach(p -> snapshot.put(p, p.getBlockRcvTime())); | ||
| List<PeerConnection> sorted = new ArrayList<>(peers); | ||
| sorted.sort(Comparator.comparingLong(snapshot::get)); | ||
| return sorted.subList(0, sorted.size() / 2); | ||
|
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] There are two problems:
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. The main purpose is to increase randomness; things that are too certain are more vulnerable to attack. If not sorted by blockRcvTime, then what should be used for sorting? |
||
| } | ||
|
|
||
| private void disconnectLan() { | ||
| if (!isLanNode()) { | ||
| return; | ||
|
|
||
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.
[QUESTION] Small wording suggestion. The PR description says this prevents "attackers from forging activity via stale block hashes" — the stale-replay part is correct and a real improvement, but I don't think arbitrary forgery is prevented:
BlockId(Sha256Hash)reinterprets the first 8 bytes of the hash as a big-endian long (BlockCapsule.java:347-352) without verifying the hash matches any real block. An adversary can craft a 32-byte hash whose high bytes encodeheadNum + 1and still pass the check.The check still has clear value (this is the only path that refreshes
lastInteractiveTimeon INV —P2pEventHandlerImpl.updateLastInteractiveTimedoesn't includeINVENTORYin its switch, so without this, tightening any historical block hash refreshes the inactivity clock). Just suggest narrowing the description to "prevent stale block hashes from refreshing lastInteractiveTime" rather than the broader "forging activity" framing — keeps expectations accurate.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.
This modification doesn't completely solve the update problem, but rather increases the cost of malicious activity, preventing attackers from refreshing the time using expired block hashes—an attack with very low cost.
The attack you mentioned does exist, but attackers would need to construct malicious blocks, significantly increasing the cost of such an attack.