From 39b143fd8cfa12e9cb6b7ceed9cc43464b847c73 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Mon, 14 Sep 2026 17:15:01 +0800 Subject: [PATCH 01/11] fix the bug of check of hellomessage --- .../java/org/tron/core/config/Parameter.java | 1 + .../tron/core/net/P2pEventHandlerImpl.java | 12 + .../tron/core/net/peer/PeerConnection.java | 2 +- .../tron/core/net/peer/PeerStatusCheck.java | 8 + .../service/handshake/HandshakeService.java | 12 +- .../org/tron/core/net/P2pHelloGateTest.java | 202 ++++++++++++++ .../messagehandler/MessageHandlerTest.java | 4 +- .../core/net/peer/PeerHelloTimeoutTest.java | 109 ++++++++ .../core/net/peer/PeerStatusCheckTest.java | 3 +- .../handshake/HandshakeHeadCheckTest.java | 250 ++++++++++++++++++ 10 files changed, 599 insertions(+), 4 deletions(-) create mode 100644 framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java create mode 100644 framework/src/test/java/org/tron/core/net/peer/PeerHelloTimeoutTest.java create mode 100644 framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java diff --git a/common/src/main/java/org/tron/core/config/Parameter.java b/common/src/main/java/org/tron/core/config/Parameter.java index 0f9402641e9..951fa6e70ab 100644 --- a/common/src/main/java/org/tron/core/config/Parameter.java +++ b/common/src/main/java/org/tron/core/config/Parameter.java @@ -98,6 +98,7 @@ public class NodeConstant { public class NetConstants { public static final long SYNC_FETCH_BATCH_NUM = 2000; + public static final long HELLO_TIME_OUT = 10000L; public static final long ADV_TIME_OUT = 20000L; public static final long SYNC_TIME_OUT = 5000L; public static final long NET_MAX_TRX_PER_SECOND = 700L; diff --git a/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java b/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java index 9dd950ae57b..075332b8f46 100644 --- a/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java +++ b/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java @@ -127,6 +127,18 @@ public void onMessage(Channel c, byte[] data) { return; } + if (data == null || data.length == 0) { + peerConnection.disconnect(Protocol.ReasonCode.BAD_PROTOCOL); + return; + } + + if (peerConnection.getHelloMessageReceive() == null + && data[0] != MessageTypes.P2P_HELLO.asByte() + && data[0] != MessageTypes.P2P_DISCONNECT.asByte()) { + peerConnection.disconnect(Protocol.ReasonCode.BAD_PROTOCOL); + return; + } + if (MessageTypes.PBFT_MSG.asByte() == data[0]) { PbftMessage message = null; try { diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java index 7d7457cf2fc..694a2e702ef 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java @@ -107,7 +107,7 @@ public class PeerConnection { @Setter @Getter - private HelloMessage helloMessageReceive; + private volatile HelloMessage helloMessageReceive; @Setter @Getter diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerStatusCheck.java b/framework/src/main/java/org/tron/core/net/peer/PeerStatusCheck.java index 04eac202484..3a770a5d2e6 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerStatusCheck.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerStatusCheck.java @@ -56,6 +56,14 @@ public void statusCheck() { isDisconnected = true; } + if (!isDisconnected) { + isDisconnected = peer.getHelloMessageReceive() == null + && peer.getChannel().getStartTime() <= now - NetConstants.HELLO_TIME_OUT; + if (isDisconnected) { + logger.warn("Peer {} hello message timeout", peer.getInetAddress()); + } + } + if (!isDisconnected) { isDisconnected = peer.getAdvInvRequest().values().stream() .anyMatch(time -> time < now - NetConstants.ADV_TIME_OUT); diff --git a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java index 070a9f56406..568f1f29a62 100644 --- a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java +++ b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java @@ -6,7 +6,7 @@ import org.springframework.stereotype.Component; import org.tron.common.utils.ByteArray; import org.tron.core.ChainBaseManager; -import org.tron.core.ChainBaseManager.NodeType; +import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.config.args.Args; import org.tron.core.net.TronNetService; import org.tron.core.net.message.handshake.HelloMessage; @@ -111,6 +111,16 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) { return; } + BlockId peerHeadBlockId = msg.getHeadBlockId(); + if (peerHeadBlockId.getNum() <= headBlockNum + && peerHeadBlockId.getNum() >= chainBaseManager.getLowestBlockNum() + && !chainBaseManager.containBlockInMainChain(peerHeadBlockId)) { + logger.info("Peer {} head block is not in my main chain, peer->{}", + peer.getInetSocketAddress(), peerHeadBlockId.getString()); + peer.disconnect(ReasonCode.FORKED); + return; + } + if (msg.getHeadBlockId().getNum() < chainBaseManager.getHeadBlockId().getNum() && peer.getInetSocketAddress().equals(effectiveCheckService.getCur())) { logger.info("Peer's head block {} is below than we, peer->{}, me->{}", diff --git a/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java b/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java new file mode 100644 index 00000000000..64a1a3aaf1f --- /dev/null +++ b/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java @@ -0,0 +1,202 @@ +package org.tron.core.net; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.tron.common.utils.Sha256Hash; +import org.tron.consensus.pbft.message.PbftMessage; +import org.tron.core.ChainBaseManager; +import org.tron.core.ChainBaseManager.NodeType; +import org.tron.core.capsule.BlockCapsule.BlockId; +import org.tron.core.net.message.MessageTypes; +import org.tron.core.net.message.PbftMessageFactory; +import org.tron.core.net.message.TronMessageFactory; +import org.tron.core.net.message.adv.InventoryMessage; +import org.tron.core.net.message.base.DisconnectMessage; +import org.tron.core.net.message.handshake.HelloMessage; +import org.tron.core.net.message.keepalive.PingMessage; +import org.tron.core.net.messagehandler.BlockMsgHandler; +import org.tron.core.net.messagehandler.ChainInventoryMsgHandler; +import org.tron.core.net.messagehandler.FetchInvDataMsgHandler; +import org.tron.core.net.messagehandler.InventoryMsgHandler; +import org.tron.core.net.messagehandler.PbftDataSyncHandler; +import org.tron.core.net.messagehandler.PbftMsgHandler; +import org.tron.core.net.messagehandler.SyncBlockChainMsgHandler; +import org.tron.core.net.messagehandler.TransactionsMsgHandler; +import org.tron.core.net.peer.PeerConnection; +import org.tron.core.net.peer.PeerManager; +import org.tron.core.net.service.handshake.HandshakeService; +import org.tron.core.net.service.keepalive.KeepAliveService; +import org.tron.core.net.service.statistics.NodeStatistics; +import org.tron.core.net.service.statistics.PeerStatistics; +import org.tron.p2p.connection.Channel; +import org.tron.p2p.discover.Node; +import org.tron.protos.Protocol.Inventory.InventoryType; +import org.tron.protos.Protocol.ReasonCode; + +public class P2pHelloGateTest { + + @InjectMocks + private P2pEventHandlerImpl handler; + @Mock + private HandshakeService handshakeService; + @Mock + private KeepAliveService keepAliveService; + @Mock + private SyncBlockChainMsgHandler syncBlockChainMsgHandler; + @Mock + private ChainInventoryMsgHandler chainInventoryMsgHandler; + @Mock + private InventoryMsgHandler inventoryMsgHandler; + @Mock + private FetchInvDataMsgHandler fetchInvDataMsgHandler; + @Mock + private BlockMsgHandler blockMsgHandler; + @Mock + private TransactionsMsgHandler transactionsMsgHandler; + @Mock + private PbftDataSyncHandler pbftDataSyncHandler; + @Mock + private PbftMsgHandler pbftMsgHandler; + @Mock + private PeerConnection peer; + @Mock + private Channel channel; + + private AutoCloseable mocks; + private MockedStatic peerManager; + + @Before + public void setUp() { + mocks = MockitoAnnotations.openMocks(this); + peerManager = Mockito.mockStatic(PeerManager.class); + peerManager.when(() -> PeerManager.getPeerConnection(channel)).thenReturn(peer); + } + + @After + public void tearDown() throws Exception { + peerManager.close(); + mocks.close(); + } + + @Test + public void testRejectAllOtherMessageTypesBeforeParsing() { + try (MockedStatic tronFactory = + Mockito.mockStatic(TronMessageFactory.class); + MockedStatic pbftFactory = + Mockito.mockStatic(PbftMessageFactory.class)) { + int rejected = 0; + for (MessageTypes type : MessageTypes.values()) { + if (type != MessageTypes.P2P_HELLO && type != MessageTypes.P2P_DISCONNECT) { + handler.onMessage(channel, new byte[] {type.asByte()}); + rejected++; + } + } + verify(peer, times(rejected)).disconnect(ReasonCode.BAD_PROTOCOL); + tronFactory.verifyNoInteractions(); + pbftFactory.verifyNoInteractions(); + } + verify(peer, never()).getPeerStatistics(); + verifyNoInteractions(handshakeService, keepAliveService, syncBlockChainMsgHandler, + chainInventoryMsgHandler, inventoryMsgHandler, fetchInvDataMsgHandler, blockMsgHandler, + transactionsMsgHandler, pbftDataSyncHandler, pbftMsgHandler); + } + + @Test + public void testEmptyFramesAreRejected() { + handler.onMessage(channel, new byte[0]); + handler.onMessage(channel, null); + verify(peer, times(2)).disconnect(ReasonCode.BAD_PROTOCOL); + } + + @Test + public void testUnknownPeerIsIgnored() { + peerManager.when(() -> PeerManager.getPeerConnection(channel)).thenReturn(null); + handler.onMessage(channel, new PingMessage().getSendBytes()); + verifyNoInteractions(peer, keepAliveService); + } + + @Test + public void testHelloIsDispatchedBeforeHandshake() throws Exception { + ChainBaseManager chain = mock(ChainBaseManager.class); + when(chain.getGenesisBlockId()).thenReturn(new BlockId()); + when(chain.getSolidBlockId()).thenReturn(new BlockId()); + when(chain.getHeadBlockId()).thenReturn(new BlockId()); + when(chain.getNodeType()).thenReturn(NodeType.FULL); + HelloMessage hello = new HelloMessage( + new Node(new byte[64], "127.0.0.1", null, 18888), 0, chain); + when(peer.getPeerStatistics()).thenReturn(new PeerStatistics()); + + handler.onMessage(channel, hello.getSendBytes()); + + verify(handshakeService).processHelloMessage(eq(peer), any(HelloMessage.class)); + verify(peer, never()).disconnect(any()); + } + + @Test + public void testDisconnectIsDispatchedBeforeHandshake() { + NodeStatistics stats = mock(NodeStatistics.class); + when(peer.getPeerStatistics()).thenReturn(new PeerStatistics()); + when(peer.getP2pRateLimiter()).thenReturn(new P2pRateLimiter()); + when(peer.getChannel()).thenReturn(channel); + when(peer.getNodeStatistics()).thenReturn(stats); + + handler.onMessage(channel, new DisconnectMessage(ReasonCode.PEER_QUITING).getSendBytes()); + + verify(channel).close(); + verify(stats).nodeDisconnectedRemote(ReasonCode.PEER_QUITING); + verify(peer, never()).disconnect(any()); + } + + @Test + public void testKeepAliveIsDispatchedAfterHello() throws Exception { + when(peer.getHelloMessageReceive()).thenReturn(mock(HelloMessage.class)); + when(peer.getPeerStatistics()).thenReturn(new PeerStatistics()); + + handler.onMessage(channel, new PingMessage().getSendBytes()); + + verify(keepAliveService).processMessage(eq(peer), any(PingMessage.class)); + verify(peer, never()).disconnect(any()); + } + + @Test + public void testInventoryIsDispatchedAfterHello() throws Exception { + when(peer.getHelloMessageReceive()).thenReturn(mock(HelloMessage.class)); + when(peer.getPeerStatistics()).thenReturn(new PeerStatistics()); + InventoryMessage inventory = new InventoryMessage( + Collections.singletonList(Sha256Hash.ZERO_HASH), InventoryType.BLOCK); + + handler.onMessage(channel, inventory.getSendBytes()); + + verify(inventoryMsgHandler).processMessage(eq(peer), any(InventoryMessage.class)); + verify(peer, never()).disconnect(any()); + } + + @Test + public void testPbftIsDispatchedAfterHello() throws Exception { + when(peer.getHelloMessageReceive()).thenReturn(mock(HelloMessage.class)); + byte[] data = new byte[] {MessageTypes.PBFT_MSG.asByte()}; + PbftMessage message = mock(PbftMessage.class); + try (MockedStatic factory = Mockito.mockStatic(PbftMessageFactory.class)) { + factory.when(() -> PbftMessageFactory.create(data)).thenReturn(message); + handler.onMessage(channel, data); + verify(pbftMsgHandler).processMessage(peer, message); + verify(peer, never()).disconnect(any()); + } + } +} diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/MessageHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/MessageHandlerTest.java index be843674632..7bee1e272e2 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/MessageHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/MessageHandlerTest.java @@ -23,6 +23,7 @@ import org.tron.core.config.args.Args; import org.tron.core.net.P2pEventHandlerImpl; import org.tron.core.net.TronNetService; +import org.tron.core.net.message.handshake.HelloMessage; import org.tron.core.net.message.keepalive.PingMessage; import org.tron.core.net.peer.PeerConnection; import org.tron.core.net.peer.PeerManager; @@ -80,6 +81,7 @@ public void testPbft() { Assert.assertFalse(c1.isDisconnect()); peer = PeerManager.getPeers().get(0); + peer.setHelloMessageReceive(mock(HelloMessage.class)); BlockCapsule blockCapsule = new BlockCapsule(1, Sha256Hash.ZERO_HASH, System.currentTimeMillis(), ByteString.EMPTY); PbftMessage pbftMessage = PbftMessage.fullNodePrePrepareBlockMsg(blockCapsule, 0L); @@ -100,7 +102,7 @@ public void testPing() { Channel c1 = mock(Channel.class); Mockito.when(c1.getInetSocketAddress()).thenReturn(a1); Mockito.when(c1.getInetAddress()).thenReturn(a1.getAddress()); - PeerManager.add(ctx, c1); + PeerManager.add(ctx, c1).setHelloMessageReceive(mock(HelloMessage.class)); PingMessage pingMessage = new PingMessage(); p2pEventHandler.onMessage(c1, pingMessage.getSendBytes()); diff --git a/framework/src/test/java/org/tron/core/net/peer/PeerHelloTimeoutTest.java b/framework/src/test/java/org/tron/core/net/peer/PeerHelloTimeoutTest.java new file mode 100644 index 00000000000..fad645714ed --- /dev/null +++ b/framework/src/test/java/org/tron/core/net/peer/PeerHelloTimeoutTest.java @@ -0,0 +1,109 @@ +package org.tron.core.net.peer; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.tron.common.utils.ReflectUtils; +import org.tron.core.capsule.BlockCapsule.BlockId; +import org.tron.core.config.Parameter.NetConstants; +import org.tron.core.net.TronNetDelegate; +import org.tron.core.net.message.handshake.HelloMessage; +import org.tron.p2p.connection.Channel; +import org.tron.protos.Protocol.ReasonCode; + +public class PeerHelloTimeoutTest { + + private PeerStatusCheck service; + private PeerConnection peer; + private Channel channel; + + @Before + public void setUp() { + service = new PeerStatusCheck(); + peer = spy(new PeerConnection()); + channel = mock(Channel.class); + ReflectUtils.setFieldValue(peer, "channel", channel); + doNothing().when(peer).disconnect(any()); + TronNetDelegate delegate = mock(TronNetDelegate.class); + when(delegate.getActivePeer()).thenReturn(Collections.singletonList(peer)); + ReflectUtils.setFieldValue(service, "tronNetDelegate", delegate); + } + + @After + public void tearDown() { + service.close(); + } + + @Test + public void testIncompleteHelloTimesOutAtTenSecondsWithoutSync() { + peer.setNeedSyncFromPeer(false); + peer.setNeedSyncFromUs(false); + when(channel.getStartTime()).thenReturn(System.currentTimeMillis() - 10_000); + + service.statusCheck(); + + verify(peer).disconnect(ReasonCode.TIME_OUT); + } + + @Test + public void testRecentInteractionDoesNotExtendHelloTimeout() { + when(channel.getStartTime()).thenReturn(System.currentTimeMillis() - 20_000); + peer.setLastInteractiveTime(System.currentTimeMillis()); + peer.setBlockBothHave(new BlockId()); + + service.statusCheck(); + + verify(peer).disconnect(ReasonCode.TIME_OUT); + } + + @Test + public void testIncompleteHelloWithinDeadlineDoesNotTimeOut() { + when(channel.getStartTime()).thenReturn(System.currentTimeMillis()); + + service.statusCheck(); + + verify(peer, never()).disconnect(any()); + } + + @Test + public void testIncompleteHelloStillChecksExistingSyncTimeout() { + when(channel.getStartTime()).thenReturn(System.currentTimeMillis()); + ReflectUtils.setFieldValue(peer, "blockBothHaveUpdateTime", 0L); + + service.statusCheck(); + + verify(peer).disconnect(ReasonCode.TIME_OUT); + } + + @Test + public void testCompletedHelloDoesNotTimeOut() { + when(channel.getStartTime()).thenReturn(System.currentTimeMillis() - 60_000); + peer.setHelloMessageReceive(mock(HelloMessage.class)); + peer.setNeedSyncFromPeer(false); + + service.statusCheck(); + + verify(peer, never()).disconnect(any()); + } + + @Test + public void testCompletedHelloStillChecksRequestTimeout() { + peer.setHelloMessageReceive(mock(HelloMessage.class)); + peer.setNeedSyncFromPeer(false); + peer.getSyncBlockRequested().put(new BlockId(), + System.currentTimeMillis() - NetConstants.SYNC_TIME_OUT - 1_000); + + service.statusCheck(); + + verify(peer).disconnect(ReasonCode.TIME_OUT); + } +} diff --git a/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckTest.java b/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckTest.java index 2d734f45215..7a4fb2fd403 100644 --- a/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckTest.java +++ b/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckTest.java @@ -13,6 +13,7 @@ import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.config.Parameter.NetConstants; import org.tron.core.config.args.Args; +import org.tron.core.net.message.handshake.HelloMessage; import org.tron.p2p.connection.Channel; @@ -44,7 +45,7 @@ public void testCheck() { ReflectUtils.setFieldValue(c1, "ctx", spy(ChannelHandlerContext.class)); Mockito.doNothing().when(c1).send((byte[]) any()); - PeerManager.add(context, c1); + PeerManager.add(context, c1).setHelloMessageReceive(Mockito.mock(HelloMessage.class)); } PeerManager.getPeers().get(0).getSyncBlockRequested() diff --git a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java new file mode 100644 index 00000000000..56ad4f1e751 --- /dev/null +++ b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java @@ -0,0 +1,250 @@ +package org.tron.core.net.service.handshake; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.net.InetSocketAddress; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.tron.common.utils.ReflectUtils; +import org.tron.common.utils.Sha256Hash; +import org.tron.core.ChainBaseManager; +import org.tron.core.ChainBaseManager.NodeType; +import org.tron.core.capsule.BlockCapsule.BlockId; +import org.tron.core.net.TronNetService; +import org.tron.core.net.message.handshake.HelloMessage; +import org.tron.core.net.peer.PeerConnection; +import org.tron.core.net.peer.PeerManager; +import org.tron.core.net.service.effective.EffectiveCheckService; +import org.tron.core.net.service.relay.RelayService; +import org.tron.p2p.P2pService; +import org.tron.p2p.connection.Channel; +import org.tron.p2p.discover.Node; +import org.tron.protos.Protocol; +import org.tron.protos.Protocol.ReasonCode; + +public class HandshakeHeadCheckTest { + + private HandshakeService service; + private ChainBaseManager chain; + private PeerConnection peer; + private Channel channel; + private EffectiveCheckService effectiveCheckService; + private MockedStatic netService; + private MockedStatic peerManager; + private BlockId genesis; + private BlockId localHead; + + @Before + public void setUp() { + service = new HandshakeService(); + chain = mock(ChainBaseManager.class); + peer = mock(PeerConnection.class); + channel = mock(Channel.class); + effectiveCheckService = mock(EffectiveCheckService.class); + RelayService relayService = mock(RelayService.class); + ReflectUtils.setFieldValue(service, "chainBaseManager", chain); + ReflectUtils.setFieldValue(service, "relayService", relayService); + ReflectUtils.setFieldValue(service, "effectiveCheckService", effectiveCheckService); + when(peer.getChannel()).thenReturn(channel); + when(peer.getInetSocketAddress()).thenReturn(new InetSocketAddress("127.0.0.1", 18888)); + when(relayService.checkHelloMessage(any(), any())).thenReturn(true); + + genesis = blockId(0, 1); + localHead = blockId(100, 2); + when(chain.getGenesisBlockId()).thenReturn(genesis); + when(chain.getSolidBlockId()).thenReturn(blockId(80, 3)); + when(chain.getHeadBlockId()).thenReturn(localHead); + when(chain.getHeadBlockNum()).thenReturn(100L); + when(chain.getLowestBlockNum()).thenReturn(10L); + when(chain.getNodeType()).thenReturn(NodeType.FULL); + when(chain.containBlockInMainChain(genesis)).thenReturn(true); + when(chain.containBlockInMainChain(localHead)).thenReturn(true); + + P2pService p2p = mock(P2pService.class); + netService = Mockito.mockStatic(TronNetService.class); + netService.when(TronNetService::getP2pService).thenReturn(p2p); + peerManager = Mockito.mockStatic(PeerManager.class); + } + + @After + public void tearDown() { + peerManager.close(); + netService.close(); + } + + @Test + public void testRejectUnknownHeadAtLocalHeight() { + BlockId unknown = blockId(100, 4); + HelloMessage hello = hello(unknown); + Assert.assertTrue(hello.valid()); + + service.processHelloMessage(peer, hello); + + verify(chain).containBlockInMainChain(unknown); + assertRejected(ReasonCode.FORKED); + } + + @Test + public void testAcceptMainChainHeadAtLocalHeight() { + HelloMessage hello = hello(localHead); + + service.processHelloMessage(peer, hello); + + verify(chain).containBlockInMainChain(localHead); + assertAccepted(hello); + } + + @Test + public void testAcceptOlderMainChainHead() { + BlockId older = blockId(90, 5); + when(chain.containBlockInMainChain(older)).thenReturn(true); + HelloMessage hello = hello(older); + + service.processHelloMessage(peer, hello); + + verify(chain).containBlockInMainChain(older); + assertAccepted(hello); + } + + @Test + public void testRejectForkHeadEvenWhenSolidBlockMatches() { + BlockId forkHead = blockId(90, 6); + HelloMessage hello = hello(forkHead); + // A common solid block does not make a different, locally verifiable head acceptable. + service.processHelloMessage(peer, hello); + + verify(chain).containBlockInMainChain(genesis); + verify(chain).containBlockInMainChain(forkHead); + assertRejected(ReasonCode.FORKED); + } + + @Test + public void testCheckHeadAtLowestRetainedHeight() { + BlockId boundary = blockId(10, 7); + + service.processHelloMessage(peer, hello(boundary)); + + verify(chain).containBlockInMainChain(boundary); + assertRejected(ReasonCode.FORKED); + } + + @Test + public void testAcceptKnownHeadAtLowestRetainedHeight() { + BlockId boundary = blockId(10, 7); + when(chain.containBlockInMainChain(boundary)).thenReturn(true); + HelloMessage hello = hello(boundary); + + service.processHelloMessage(peer, hello); + + verify(chain).containBlockInMainChain(boundary); + assertAccepted(hello); + } + + @Test + public void testHigherHeadContinuesToSync() { + BlockId higher = blockId(101, 8); + HelloMessage hello = hello(higher); + + service.processHelloMessage(peer, hello); + + verify(chain, never()).containBlockInMainChain(higher); + assertAccepted(hello); + } + + @Test + public void testPrunedHeadContinuesToSync() { + BlockId pruned = blockId(9, 9); + HelloMessage hello = hello(pruned); + + service.processHelloMessage(peer, hello); + + verify(chain, never()).containBlockInMainChain(pruned); + assertAccepted(hello); + } + + @Test + public void testFullNodeChecksUnknownHeadAtHeightZero() { + when(chain.getLowestBlockNum()).thenReturn(0L); + BlockId unknown = blockId(0, 10); + + service.processHelloMessage(peer, hello(unknown)); + + verify(chain).containBlockInMainChain(unknown); + assertRejected(ReasonCode.FORKED); + } + + @Test + public void testEffectiveCheckStillRejectsOlderKnownHead() { + BlockId older = blockId(90, 5); + when(chain.containBlockInMainChain(older)).thenReturn(true); + InetSocketAddress address = peer.getInetSocketAddress(); + when(effectiveCheckService.getCur()).thenReturn(address); + + service.processHelloMessage(peer, hello(older)); + + assertRejected(ReasonCode.BELOW_THAN_ME); + } + + @Test + public void testSolidForkIsRejectedBeforeHeadCheck() { + BlockId differentSolid = blockId(80, 11); + HelloMessage hello = hello(localHead); + hello.setHelloMessage(hello.getInstance().toBuilder() + .setSolidBlockId(protoBlockId(differentSolid)).build()); + + service.processHelloMessage(peer, hello); + + verify(chain, never()).containBlockInMainChain(localHead); + assertRejected(ReasonCode.FORKED); + } + + @Test + public void testDuplicateHelloIsStillRejected() { + HelloMessage hello = hello(localHead); + when(peer.getHelloMessageReceive()).thenReturn(hello); + + service.processHelloMessage(peer, hello); + + assertRejected(ReasonCode.BAD_PROTOCOL); + } + + private HelloMessage hello(BlockId head) { + HelloMessage hello = new HelloMessage( + new Node(new byte[64], "127.0.0.1", null, 18888), 0, chain); + hello.setHelloMessage(hello.getInstance().toBuilder() + .setSolidBlockId(protoBlockId(genesis)) + .setHeadBlockId(protoBlockId(head)).build()); + return hello; + } + + private static BlockId blockId(long number, int seed) { + return new BlockId(Sha256Hash.of(true, new byte[] {(byte) seed}), number); + } + + private static Protocol.HelloMessage.BlockId protoBlockId(BlockId id) { + return Protocol.HelloMessage.BlockId.newBuilder() + .setNumber(id.getNum()).setHash(id.getByteString()).build(); + } + + private void assertAccepted(HelloMessage hello) { + verify(peer).setHelloMessageReceive(hello); + verify(peer).onConnect(); + verify(peer, never()).disconnect(any()); + peerManager.verify(PeerManager::sortPeers); + } + + private void assertRejected(ReasonCode reason) { + verify(peer).disconnect(reason); + verify(peer, never()).setHelloMessageReceive(any()); + verify(peer, never()).onConnect(); + peerManager.verifyNoInteractions(); + } +} From 183216c544ba17c0cf85be6e6b01844aab29dd04 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Tue, 29 Sep 2026 17:59:19 +0800 Subject: [PATCH 02/11] valid HelloMessage before toString --- .../org/tron/core/net/message/handshake/HelloMessage.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java index 68123c93db6..2976ee51486 100755 --- a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java +++ b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java @@ -121,6 +121,10 @@ public Class getAnswerMessage() { @Override public String toString() { + if (!valid()) { + return "P2P_HELLO: invalid hello message"; + } + StringBuilder builder = new StringBuilder(); builder.append(super.toString()) From 31861e5f55f014d9d297965d21997eaa26506249 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Thu, 1 Oct 2026 20:44:08 +0800 Subject: [PATCH 03/11] add validEndPoint for HelloMessage --- .../net/message/handshake/HelloMessage.java | 35 +++- .../message/handshake/HelloMessageTest.java | 174 ++++++++++++++++++ 2 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java diff --git a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java index 2976ee51486..7b3f7cd18da 100755 --- a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java +++ b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java @@ -4,7 +4,6 @@ import lombok.Getter; import org.apache.commons.lang3.StringUtils; import org.tron.common.utils.ByteArray; -import org.tron.common.utils.DecodeUtil; import org.tron.common.utils.Sha256Hash; import org.tron.common.utils.StringUtil; import org.tron.core.ChainBaseManager; @@ -13,6 +12,7 @@ import org.tron.core.net.message.MessageTypes; import org.tron.core.net.message.TronMessage; import org.tron.p2p.discover.Node; +import org.tron.p2p.utils.NetUtil; import org.tron.program.Version; import org.tron.protos.Discover.Endpoint; import org.tron.protos.Protocol; @@ -20,6 +20,8 @@ public class HelloMessage extends TronMessage { + private static final int MAX_BYTE_SIZE = 200; + @Getter private Protocol.HelloMessage helloMessage; @@ -160,6 +162,10 @@ public Protocol.HelloMessage getInstance() { } public boolean valid() { + if (!validEndPoint(this.helloMessage.getFrom())) { + return false; + } + byte[] genesisBlockByte = this.helloMessage.getGenesisBlockId().getHash().toByteArray(); if (genesisBlockByte.length != Sha256Hash.LENGTH) { return false; @@ -175,25 +181,44 @@ public boolean valid() { return false; } - int maxByteSize = 200; ByteString address = this.helloMessage.getAddress(); - if (!address.isEmpty() && address.toByteArray().length > maxByteSize) { + if (!address.isEmpty() && address.toByteArray().length > MAX_BYTE_SIZE) { return false; } ByteString sig = this.helloMessage.getSignature(); - if (!sig.isEmpty() && sig.toByteArray().length > maxByteSize) { + if (!sig.isEmpty() && sig.toByteArray().length > MAX_BYTE_SIZE) { return false; } ByteString codeVersion = this.helloMessage.getCodeVersion(); - if (!codeVersion.isEmpty() && codeVersion.toByteArray().length > maxByteSize) { + if (!codeVersion.isEmpty() && codeVersion.toByteArray().length > MAX_BYTE_SIZE) { return false; } return true; } + private static boolean validEndPoint(Endpoint from) { + ByteString ipv4 = from.getAddress(); + ByteString ipv6 = from.getAddressIpv6(); + if (from.getPort() <= 0 || from.getPort() > 0xFFFF + || ipv4.size() > MAX_BYTE_SIZE || ipv6.size() > MAX_BYTE_SIZE) { + return false; + } + if (ipv4.isEmpty() && ipv6.isEmpty()) { + return false; + } + // Validate raw literals before getFrom() constructs a Node during logging. + if (!ipv4.isEmpty() && !NetUtil.validIpV4(ByteArray.toStr(ipv4.toByteArray()))) { + return false; + } + if (!ipv6.isEmpty() && !NetUtil.validIpV6(ByteArray.toStr(ipv6.toByteArray()))) { + return false; + } + return true; + } + public static Endpoint getEndpointFromNode(Node node) { Endpoint.Builder builder = Endpoint.newBuilder() .setPort(node.getPort()); diff --git a/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java b/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java new file mode 100644 index 00000000000..39da306b931 --- /dev/null +++ b/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java @@ -0,0 +1,174 @@ +package org.tron.core.net.message.handshake; + +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import ch.qos.logback.classic.AsyncAppender; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.read.ListAppender; +import com.google.protobuf.ByteString; +import org.apache.commons.lang3.StringUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.tron.p2p.P2pConfig; +import org.tron.p2p.base.Parameter; +import org.tron.p2p.utils.NetUtil; +import org.tron.protos.Discover.Endpoint; +import org.tron.protos.Protocol; + +public class HelloMessageTest { + + private P2pConfig savedConfig; + + @Before + public void setUp() { + savedConfig = Parameter.p2pConfig; + Parameter.p2pConfig = new P2pConfig(); + Parameter.p2pConfig.setIp("127.0.0.1"); + Parameter.p2pConfig.setIpv6("::1"); + } + + @After + public void tearDown() { + Parameter.p2pConfig = savedConfig; + } + + @Test(timeout = 5000) + public void testAcceptIpLiteralsAndPortBoundaries() throws Exception { + for (String[] hosts : new String[][] { + {"192.0.2.1", ""}, {"", "2001:db8::1"}, {"192.0.2.1", "2001:db8::1"}, + {"", "::1"}, {"", "::ffff:192.0.2.1"}}) { + for (int port : new int[] {1, 18888, 65535}) { + HelloMessage message = spy(hello(hosts[0], hosts[1], port)); + Assert.assertTrue(message.valid()); + verify(message, never()).getFrom(); + } + } + } + + @Test(timeout = 5000) + public void testRejectHostnamesAndMalformedAddresses() throws Exception { + for (String[] hosts : new String[][] { + {"hello.invalid", ""}, {"", "hello.invalid"}, + {"hello.invalid", "2001:db8::1"}, {"192.0.2.1", "hello.invalid"}, + {"256.0.0.1", ""}, {"192.0.2.1\n", ""}, {" 192.0.2.1", ""}, + {"2001:db8::1", ""}, {"", "192.0.2.1"}, {"", "2001:db8:::1"}}) { + assertRejectedBeforeNodeConstruction(hello(hosts[0], hosts[1], 18888)); + } + } + + @Test(timeout = 5000) + public void testRejectMissingAddresses() throws Exception { + assertRejectedBeforeNodeConstruction(hello("", "", 18888)); + HelloMessage missingFrom = hello("192.0.2.1", "", 18888); + missingFrom.setHelloMessage(missingFrom.getInstance().toBuilder().clearFrom().build()); + assertRejectedBeforeNodeConstruction(missingFrom); + } + + @Test(timeout = 5000) + public void testRejectInvalidPorts() throws Exception { + for (int port : new int[] {Integer.MIN_VALUE, -1, 0, 65536, Integer.MAX_VALUE}) { + assertRejectedBeforeNodeConstruction(hello("192.0.2.1", "2001:db8::1", port)); + } + } + + @Test(timeout = 5000) + public void testRejectOversizedAddressesBeforeLiteralValidation() throws Exception { + String oversized = StringUtils.repeat('a', 201); + try (MockedStatic netUtil = Mockito.mockStatic(NetUtil.class)) { + assertRejectedBeforeNodeConstruction(hello(oversized, "", 18888)); + assertRejectedBeforeNodeConstruction(hello("192.0.2.1", oversized, 18888)); + netUtil.verifyNoInteractions(); + } + } + + @Test(timeout = 5000) + public void testIpv6AddressLengthBoundary() throws Exception { + String scoped = "fe80::1%" + StringUtils.repeat('a', 192); + Assert.assertEquals(200, scoped.length()); + Assert.assertTrue(NetUtil.validIpV6(scoped)); + HelloMessage message = spy(hello("", scoped, 18888)); + Assert.assertTrue(message.valid()); + verify(message, never()).getFrom(); + // The syntax remains valid, so rejection must come from the byte limit. + Assert.assertTrue(NetUtil.validIpV6(scoped + "a")); + assertRejectedBeforeNodeConstruction(hello("", scoped + "a", 18888)); + } + + @Test(timeout = 5000) + public void testValidAddressLogFormat() throws Exception { + for (String[] hosts : new String[][] { + {"192.0.2.1", "", "/192.0.2.1:18888"}, + {"", "2001:db8::1", "/[2001:db8:0:0:0:0:0:1]:18888"}, + {"192.0.2.1", "2001:db8::1", "/192.0.2.1:18888"}}) { + HelloMessage message = hello(hosts[0], hosts[1], 18888); + Assert.assertTrue(message.valid()); + String formatted = message.toString(); + Assert.assertTrue(formatted.contains("from: " + hosts[2] + "\n")); + Assert.assertTrue(formatted.contains("timestamp: 123\n")); + Assert.assertTrue(formatted.contains("headBlockId: " + + message.getHeadBlockId().getString() + "\n")); + Assert.assertTrue(formatted.contains("nodeType: 0\nlowestBlockNum: 0\n")); + } + } + + @Test(timeout = 5000) + public void testAsyncLoggingRejectsHostnameBeforeNodeConstruction() throws Exception { + HelloMessage message = spy(hello("hello.invalid", "", 18888)); + doThrow(new AssertionError("Invalid endpoint reached Node construction")) + .when(message).getFrom(); + LoggerContext context = new LoggerContext(); + context.setMDCAdapter(new LogbackMDCAdapter()); + ListAppender sink = new ListAppender<>(); + sink.setContext(context); + sink.start(); + AsyncAppender async = new AsyncAppender(); + async.setContext(context); + async.addAppender(sink); + async.setDiscardingThreshold(0); + async.start(); + ch.qos.logback.classic.Logger logger = context.getLogger("hello.validation.test"); + logger.setLevel(Level.INFO); + logger.setAdditive(false); + logger.addAppender(async); + try { + logger.info("Receive HELLO {}", message); + } finally { + async.stop(); + context.stop(); + } + Assert.assertEquals(1, sink.list.size()); + Assert.assertEquals("Receive HELLO P2P_HELLO: invalid hello message", + sink.list.get(0).getFormattedMessage()); + verify(message, never()).getFrom(); + } + + private static void assertRejectedBeforeNodeConstruction(HelloMessage message) { + HelloMessage checked = spy(message); + doThrow(new AssertionError("Invalid endpoint reached Node construction")) + .when(checked).getFrom(); + Assert.assertFalse(checked.valid()); + Assert.assertEquals("P2P_HELLO: invalid hello message", checked.toString()); + verify(checked, never()).getFrom(); + } + + private static HelloMessage hello(String ipv4, String ipv6, int port) throws Exception { + Protocol.HelloMessage.BlockId block = Protocol.HelloMessage.BlockId.newBuilder() + .setHash(ByteString.copyFrom(new byte[32])).build(); + Endpoint endpoint = Endpoint.newBuilder().setAddress(ByteString.copyFromUtf8(ipv4)) + .setAddressIpv6(ByteString.copyFromUtf8(ipv6)) + .setNodeId(ByteString.copyFrom(new byte[64])).setPort(port).build(); + return new HelloMessage(Protocol.HelloMessage.newBuilder().setFrom(endpoint) + .setGenesisBlockId(block).setSolidBlockId(block).setHeadBlockId(block) + .setTimestamp(123).build().toByteArray()); + } +} From efcb9b388d786231b864c6c1a79f89b044888c35 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Thu, 1 Oct 2026 21:35:15 +0800 Subject: [PATCH 04/11] remove fork detect --- .../service/handshake/HandshakeService.java | 11 --- .../handshake/HandshakeHeadCheckTest.java | 99 ++++++++++++------- 2 files changed, 65 insertions(+), 45 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java index 568f1f29a62..8928f8fbef4 100644 --- a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java +++ b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java @@ -6,7 +6,6 @@ import org.springframework.stereotype.Component; import org.tron.common.utils.ByteArray; import org.tron.core.ChainBaseManager; -import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.config.args.Args; import org.tron.core.net.TronNetService; import org.tron.core.net.message.handshake.HelloMessage; @@ -111,16 +110,6 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) { return; } - BlockId peerHeadBlockId = msg.getHeadBlockId(); - if (peerHeadBlockId.getNum() <= headBlockNum - && peerHeadBlockId.getNum() >= chainBaseManager.getLowestBlockNum() - && !chainBaseManager.containBlockInMainChain(peerHeadBlockId)) { - logger.info("Peer {} head block is not in my main chain, peer->{}", - peer.getInetSocketAddress(), peerHeadBlockId.getString()); - peer.disconnect(ReasonCode.FORKED); - return; - } - if (msg.getHeadBlockId().getNum() < chainBaseManager.getHeadBlockId().getNum() && peer.getInetSocketAddress().equals(effectiveCheckService.getCur())) { logger.info("Peer's head block {} is below than we, peer->{}, me->{}", diff --git a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java index 56ad4f1e751..9e7734aecb2 100644 --- a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java +++ b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java @@ -40,6 +40,7 @@ public class HandshakeHeadCheckTest { private MockedStatic netService; private MockedStatic peerManager; private BlockId genesis; + private BlockId solid; private BlockId localHead; @Before @@ -58,14 +59,16 @@ public void setUp() { when(relayService.checkHelloMessage(any(), any())).thenReturn(true); genesis = blockId(0, 1); + solid = blockId(80, 3); localHead = blockId(100, 2); when(chain.getGenesisBlockId()).thenReturn(genesis); - when(chain.getSolidBlockId()).thenReturn(blockId(80, 3)); + when(chain.getSolidBlockId()).thenReturn(solid); when(chain.getHeadBlockId()).thenReturn(localHead); when(chain.getHeadBlockNum()).thenReturn(100L); when(chain.getLowestBlockNum()).thenReturn(10L); when(chain.getNodeType()).thenReturn(NodeType.FULL); when(chain.containBlockInMainChain(genesis)).thenReturn(true); + when(chain.containBlockInMainChain(solid)).thenReturn(true); when(chain.containBlockInMainChain(localHead)).thenReturn(true); P2pService p2p = mock(P2pService.class); @@ -81,15 +84,30 @@ public void tearDown() { } @Test - public void testRejectUnknownHeadAtLocalHeight() { + public void testAcceptUnknownHeadAtLocalHeight() { BlockId unknown = blockId(100, 4); HelloMessage hello = hello(unknown); Assert.assertTrue(hello.valid()); + Assert.assertEquals(chain.getSolidBlockId(), hello.getSolidBlockId()); + Assert.assertNotEquals(localHead, unknown); + Assert.assertFalse(chain.containBlockInMainChain(unknown)); service.processHelloMessage(peer, hello); - verify(chain).containBlockInMainChain(unknown); - assertRejected(ReasonCode.FORKED); + assertAccepted(hello); + } + + @Test + public void testAcceptCachedForkHeadAtLocalHeight() { + BlockId forkHead = blockId(100, 12); + when(chain.containBlock(forkHead)).thenReturn(true); + Assert.assertTrue(chain.containBlock(forkHead)); + Assert.assertFalse(chain.containBlockInMainChain(forkHead)); + HelloMessage hello = hello(forkHead); + + service.processHelloMessage(peer, hello); + + assertAccepted(hello); } @Test @@ -98,7 +116,6 @@ public void testAcceptMainChainHeadAtLocalHeight() { service.processHelloMessage(peer, hello); - verify(chain).containBlockInMainChain(localHead); assertAccepted(hello); } @@ -110,41 +127,42 @@ public void testAcceptOlderMainChainHead() { service.processHelloMessage(peer, hello); - verify(chain).containBlockInMainChain(older); assertAccepted(hello); } @Test - public void testRejectForkHeadEvenWhenSolidBlockMatches() { + public void testAcceptForkHeadWhenSolidBlockMatches() { BlockId forkHead = blockId(90, 6); HelloMessage hello = hello(forkHead); - // A common solid block does not make a different, locally verifiable head acceptable. + Assert.assertEquals(chain.getSolidBlockId(), hello.getSolidBlockId()); + // Keep the connection available so a short-lived fork can converge through sync/broadcast. service.processHelloMessage(peer, hello); - verify(chain).containBlockInMainChain(genesis); - verify(chain).containBlockInMainChain(forkHead); - assertRejected(ReasonCode.FORKED); + verify(chain).containBlockInMainChain(solid); + assertAccepted(hello); } @Test - public void testCheckHeadAtLowestRetainedHeight() { - BlockId boundary = blockId(10, 7); + public void testAcceptOlderForkHeadBelowLocalSolid() { + when(chain.getSolidBlockId()).thenReturn(blockId(95, 13)); + HelloMessage hello = hello(blockId(90, 7)); + Assert.assertTrue(hello.getSolidBlockId().getNum() < hello.getHeadBlockId().getNum()); + Assert.assertTrue(hello.getHeadBlockId().getNum() < chain.getSolidBlockId().getNum()); - service.processHelloMessage(peer, hello(boundary)); + service.processHelloMessage(peer, hello); - verify(chain).containBlockInMainChain(boundary); - assertRejected(ReasonCode.FORKED); + verify(chain).containBlockInMainChain(solid); + assertAccepted(hello); } @Test public void testAcceptKnownHeadAtLowestRetainedHeight() { - BlockId boundary = blockId(10, 7); - when(chain.containBlockInMainChain(boundary)).thenReturn(true); - HelloMessage hello = hello(boundary); + when(chain.getLowestBlockNum()).thenReturn(solid.getNum()); + HelloMessage hello = hello(solid); service.processHelloMessage(peer, hello); - verify(chain).containBlockInMainChain(boundary); + verify(chain).containBlockInMainChain(solid); assertAccepted(hello); } @@ -160,25 +178,38 @@ public void testHigherHeadContinuesToSync() { } @Test - public void testPrunedHeadContinuesToSync() { - BlockId pruned = blockId(9, 9); - HelloMessage hello = hello(pruned); + public void testUnverifiableSolidBelowRetainedHistoryIsRejected() { + BlockId prunedSolid = blockId(9, 9); + HelloMessage hello = hello(blockId(90, 14)); + hello.setHelloMessage(hello.getInstance().toBuilder() + .setSolidBlockId(protoBlockId(prunedSolid)).build()); service.processHelloMessage(peer, hello); - verify(chain, never()).containBlockInMainChain(pruned); - assertAccepted(hello); + verify(chain).containBlockInMainChain(prunedSolid); + assertRejected(ReasonCode.LIGHT_NODE_SYNC_FAIL); } @Test - public void testFullNodeChecksUnknownHeadAtHeightZero() { - when(chain.getLowestBlockNum()).thenReturn(0L); - BlockId unknown = blockId(0, 10); + public void testGenesisMismatchIsStillRejected() { + HelloMessage hello = hello(localHead); + hello.setHelloMessage(hello.getInstance().toBuilder() + .setGenesisBlockId(protoBlockId(blockId(0, 10))).build()); - service.processHelloMessage(peer, hello(unknown)); + service.processHelloMessage(peer, hello); - verify(chain).containBlockInMainChain(unknown); - assertRejected(ReasonCode.FORKED); + assertRejected(ReasonCode.INCOMPATIBLE_CHAIN); + } + + @Test + public void testVersionMismatchIsStillRejected() { + HelloMessage hello = hello(localHead); + hello.setHelloMessage(hello.getInstance().toBuilder() + .setVersion(hello.getVersion() + 1).build()); + + service.processHelloMessage(peer, hello); + + assertRejected(ReasonCode.INCOMPATIBLE_VERSION); } @Test @@ -194,7 +225,7 @@ public void testEffectiveCheckStillRejectsOlderKnownHead() { } @Test - public void testSolidForkIsRejectedBeforeHeadCheck() { + public void testSolidForkIsStillRejected() { BlockId differentSolid = blockId(80, 11); HelloMessage hello = hello(localHead); hello.setHelloMessage(hello.getInstance().toBuilder() @@ -202,7 +233,7 @@ public void testSolidForkIsRejectedBeforeHeadCheck() { service.processHelloMessage(peer, hello); - verify(chain, never()).containBlockInMainChain(localHead); + verify(chain).containBlockInMainChain(differentSolid); assertRejected(ReasonCode.FORKED); } @@ -220,7 +251,7 @@ private HelloMessage hello(BlockId head) { HelloMessage hello = new HelloMessage( new Node(new byte[64], "127.0.0.1", null, 18888), 0, chain); hello.setHelloMessage(hello.getInstance().toBuilder() - .setSolidBlockId(protoBlockId(genesis)) + .setSolidBlockId(protoBlockId(solid)) .setHeadBlockId(protoBlockId(head)).build()); return hello; } From 639130f028c6c0b5c147538a4b0dd9c885ff9a0d Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Thu, 1 Oct 2026 22:26:05 +0800 Subject: [PATCH 05/11] don't print hash of hellomessage --- .../net/message/handshake/HelloMessage.java | 5 +- .../service/handshake/HandshakeService.java | 19 +-- .../message/handshake/HelloMessageTest.java | 14 +- .../net/services/HandShakeServiceTest.java | 128 ++++++++++++++++++ 4 files changed, 151 insertions(+), 15 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java index 7b3f7cd18da..c629ec91877 100755 --- a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java +++ b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java @@ -162,7 +162,7 @@ public Protocol.HelloMessage getInstance() { } public boolean valid() { - if (!validEndPoint(this.helloMessage.getFrom())) { + if (!validEndPoint()) { return false; } @@ -199,7 +199,8 @@ public boolean valid() { return true; } - private static boolean validEndPoint(Endpoint from) { + public boolean validEndPoint() { + Endpoint from = this.helloMessage.getFrom(); ByteString ipv4 = from.getAddress(); ByteString ipv6 = from.getAddressIpv6(); if (from.getPort() <= 0 || from.getPort() > 0xFFFF diff --git a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java index 8928f8fbef4..f8cec9b9d17 100644 --- a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java +++ b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java @@ -4,7 +4,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.tron.common.utils.ByteArray; import org.tron.core.ChainBaseManager; import org.tron.core.config.args.Args; import org.tron.core.net.TronNetService; @@ -48,15 +47,17 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) { } if (!msg.valid()) { - logger.warn("Peer {} invalid hello message parameters, GenesisBlockId: {}, SolidBlockId: {}, " - + "HeadBlockId: {}, address: {}, sig: {}, codeVersion: {}", + logger.warn("Peer {} invalid hello message parameters, genesisHashLength: {}, " + + "solidHashLength: {}, headHashLength: {}, address: {}, sig: {}, codeVersion: {}, " + + "endpointValid: {}", peer.getInetSocketAddress(), - ByteArray.toHexString(msg.getInstance().getGenesisBlockId().getHash().toByteArray()), - ByteArray.toHexString(msg.getInstance().getSolidBlockId().getHash().toByteArray()), - ByteArray.toHexString(msg.getInstance().getHeadBlockId().getHash().toByteArray()), - msg.getInstance().getAddress().toByteArray().length, - msg.getInstance().getSignature().toByteArray().length, - msg.getInstance().getCodeVersion().toByteArray().length); + msg.getInstance().getGenesisBlockId().getHash().size(), + msg.getInstance().getSolidBlockId().getHash().size(), + msg.getInstance().getHeadBlockId().getHash().size(), + msg.getInstance().getAddress().size(), + msg.getInstance().getSignature().size(), + msg.getInstance().getCodeVersion().size(), + msg.validEndPoint()); peer.disconnect(ReasonCode.INCOMPATIBLE_PROTOCOL); return; } diff --git a/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java b/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java index 39da306b931..feabc2160c9 100644 --- a/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java +++ b/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java @@ -12,6 +12,7 @@ import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.read.ListAppender; import com.google.protobuf.ByteString; +import java.net.InetSocketAddress; import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.Assert; @@ -49,6 +50,7 @@ public void testAcceptIpLiteralsAndPortBoundaries() throws Exception { {"", "::1"}, {"", "::ffff:192.0.2.1"}}) { for (int port : new int[] {1, 18888, 65535}) { HelloMessage message = spy(hello(hosts[0], hosts[1], port)); + Assert.assertTrue(message.validEndPoint()); Assert.assertTrue(message.valid()); verify(message, never()).getFrom(); } @@ -107,13 +109,16 @@ public void testIpv6AddressLengthBoundary() throws Exception { @Test(timeout = 5000) public void testValidAddressLogFormat() throws Exception { for (String[] hosts : new String[][] { - {"192.0.2.1", "", "/192.0.2.1:18888"}, - {"", "2001:db8::1", "/[2001:db8:0:0:0:0:0:1]:18888"}, - {"192.0.2.1", "2001:db8::1", "/192.0.2.1:18888"}}) { + {"192.0.2.1", "", "192.0.2.1"}, + {"", "2001:db8::1", "2001:db8::1"}, + {"192.0.2.1", "2001:db8::1", "192.0.2.1"}}) { HelloMessage message = hello(hosts[0], hosts[1], 18888); Assert.assertTrue(message.valid()); + // InetSocketAddress adds IPv6 brackets on JDK 17, but not on JDK 8. + InetSocketAddress expectedEndpoint = new InetSocketAddress(hosts[2], 18888); + Assert.assertFalse(expectedEndpoint.isUnresolved()); String formatted = message.toString(); - Assert.assertTrue(formatted.contains("from: " + hosts[2] + "\n")); + Assert.assertTrue(formatted, formatted.contains("from: " + expectedEndpoint + "\n")); Assert.assertTrue(formatted.contains("timestamp: 123\n")); Assert.assertTrue(formatted.contains("headBlockId: " + message.getHeadBlockId().getString() + "\n")); @@ -156,6 +161,7 @@ private static void assertRejectedBeforeNodeConstruction(HelloMessage message) { HelloMessage checked = spy(message); doThrow(new AssertionError("Invalid endpoint reached Node construction")) .when(checked).getFrom(); + Assert.assertFalse(checked.validEndPoint()); Assert.assertFalse(checked.valid()); Assert.assertEquals("P2P_HELLO: invalid hello message", checked.toString()); verify(checked, never()).getFrom(); diff --git a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java index dce5ccb851f..94a89ae5ec3 100644 --- a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java @@ -1,13 +1,29 @@ package org.tron.core.net.services; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.tron.core.net.message.handshake.HelloMessage.getEndpointFromNode; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.read.ListAppender; import com.google.protobuf.ByteString; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; import java.util.Random; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; @@ -15,7 +31,9 @@ import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.mockito.MockedStatic; import org.mockito.Mockito; +import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.tron.common.TestConstants; import org.tron.common.application.TronApplicationContext; @@ -33,6 +51,7 @@ import org.tron.core.net.peer.PeerManager; import org.tron.core.net.service.handshake.HandshakeService; import org.tron.p2p.P2pConfig; +import org.tron.p2p.P2pService; import org.tron.p2p.base.Parameter; import org.tron.p2p.connection.Channel; import org.tron.p2p.discover.Node; @@ -41,6 +60,7 @@ import org.tron.protos.Discover.Endpoint; import org.tron.protos.Protocol; import org.tron.protos.Protocol.HelloMessage.Builder; +import org.tron.protos.Protocol.ReasonCode; public class HandShakeServiceTest { @@ -316,6 +336,114 @@ public void testProcessHelloMessage() { } } + @Test + public void testInvalidHelloLogsHashLengths() throws Exception { + int largeHashLength = Parameter.MAX_MESSAGE_LENGTH - 1024; + Endpoint validEndpoint = endpoint("127.0.0.1", "", 18888); + for (int[] lengths : new int[][] { + {32, 32, 32}, {0, 32, 32}, {32, 31, 32}, {32, 32, 33}, {0, 31, 33}, + {largeHashLength, 32, 32}, {32, largeHashLength, 32}, {32, 32, largeHashLength}}) { + // The oversized signature also exercises logging when all three hashes are 32 bytes. + assertInvalidHelloLog(lengths[0], lengths[1], lengths[2], validEndpoint, 201, true); + } + } + + @Test + public void testInvalidHelloLogsEndpointValidity() throws Exception { + for (Endpoint invalidEndpoint : new Endpoint[] { + endpoint("127.0.0.1", "", 0), endpoint("127.0.0.1", "", 65536), + Endpoint.getDefaultInstance(), endpoint("", "", 18888), + endpoint("hello.invalid", "", 18888), endpoint("127.0.0.1", "2001:db8:::1", 18888), + endpoint("127.0.0.1\nforged", "", 18888), + endpoint(StringUtils.repeat('a', Parameter.MAX_MESSAGE_LENGTH - 1024), "", 18888), + endpoint("127.0.0.1", StringUtils.repeat('a', 201), 18888)}) { + // Keep all other HELLO fields valid so the endpoint alone causes rejection. + assertInvalidHelloLog(32, 32, 32, invalidEndpoint, 0, false); + } + assertInvalidHelloLog(32, 32, 32, endpoint("", "2001:db8::1", 18888), 201, true); + } + + private void assertInvalidHelloLog(int genesisLength, int solidLength, int headLength, + Endpoint endpoint, int signatureLength, boolean expectedEndpointValid) throws Exception { + Protocol.HelloMessage proto = Protocol.HelloMessage.newBuilder() + .setFrom(endpoint) + .setGenesisBlockId(blockId(genesisLength, (byte) 0x11)) + .setSolidBlockId(blockId(solidLength, (byte) 0x22)) + .setHeadBlockId(blockId(headLength, (byte) 0x33)) + .setSignature(ByteString.copyFrom(new byte[signatureLength])) + .build(); + Assert.assertTrue(proto.getSerializedSize() + 1 < Parameter.MAX_MESSAGE_LENGTH); + HelloMessage hello = new HelloMessage(proto.toByteArray()); + Assert.assertEquals(expectedEndpointValid, hello.validEndPoint()); + Assert.assertFalse(hello.valid()); + + PeerConnection testPeer = mock(PeerConnection.class); + when(testPeer.getChannel()).thenReturn(mock(Channel.class)); + when(testPeer.getInetSocketAddress()).thenReturn(new InetSocketAddress("127.0.0.1", 18888)); + P2pService p2p = mock(P2pService.class); + Logger logger = (Logger) LoggerFactory.getLogger("net"); + Level originalLevel = logger.getLevel(); + boolean originalAdditive = logger.isAdditive(); + List> originalAppenders = new ArrayList<>(); + Iterator> iterator = logger.iteratorForAppenders(); + while (iterator.hasNext()) { + originalAppenders.add(iterator.next()); + } + ListAppender sink = new ListAppender<>(); + // The shared application context can also log from background threads. + sink.list = new CopyOnWriteArrayList<>(); + sink.setContext(logger.getLoggerContext()); + sink.start(); + try (MockedStatic netService = Mockito.mockStatic(TronNetService.class)) { + netService.when(TronNetService::getP2pService).thenReturn(p2p); + for (Appender appender : originalAppenders) { + logger.detachAppender(appender); + } + logger.setAdditive(false); + logger.setLevel(Level.WARN); + logger.addAppender(sink); + + new HandshakeService().processHelloMessage(testPeer, hello); + + verify(testPeer).disconnect(ReasonCode.INCOMPATIBLE_PROTOCOL); + verify(testPeer, never()).setHelloMessageReceive(any()); + verify(testPeer, never()).onConnect(); + List warnings = sink.list.stream() + .filter(event -> event.getMessage() + .startsWith("Peer {} invalid hello message parameters")) + .collect(Collectors.toList()); + Assert.assertEquals(1, warnings.size()); + ILoggingEvent event = warnings.get(0); + Assert.assertEquals(Level.WARN, event.getLevel()); + String text = event.getFormattedMessage(); + Assert.assertTrue("Invalid HELLO warning must remain bounded", text.length() < 512); + Assert.assertEquals("Peer /127.0.0.1:18888 invalid hello message parameters, " + + "genesisHashLength: " + genesisLength + ", solidHashLength: " + solidLength + + ", headHashLength: " + headLength + ", address: 0, sig: " + signatureLength + + ", codeVersion: 0, endpointValid: " + expectedEndpointValid, text); + } finally { + logger.detachAppender(sink); + sink.stop(); + for (Appender appender : originalAppenders) { + logger.addAppender(appender); + } + logger.setLevel(originalLevel); + logger.setAdditive(originalAdditive); + } + } + + private static Endpoint endpoint(String ipv4, String ipv6, int port) { + return Endpoint.newBuilder().setNodeId(ByteString.copyFrom(new byte[64])) + .setAddress(ByteString.copyFromUtf8(ipv4)).setAddressIpv6(ByteString.copyFromUtf8(ipv6)) + .setPort(port).build(); + } + + private static Protocol.HelloMessage.BlockId blockId(int length, byte value) { + byte[] hash = new byte[length]; + Arrays.fill(hash, value); + return Protocol.HelloMessage.BlockId.newBuilder().setHash(ByteString.copyFrom(hash)).build(); + } + private Protocol.HelloMessage.Builder getHelloMessageBuilder(Node from, long timestamp, ChainBaseManager chainBaseManager) { Endpoint fromEndpoint = getEndpointFromNode(from); From 0f7ccda05c4e4d5a0d0a1d7b7ffa8f15d421390b Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Thu, 1 Oct 2026 22:48:19 +0800 Subject: [PATCH 06/11] remove PeerHelloTimeoutTest --- .../core/net/peer/PeerHelloTimeoutTest.java | 109 ------------------ .../net/peer/PeerStatusCheckMockTest.java | 88 +++++++++++++- 2 files changed, 85 insertions(+), 112 deletions(-) delete mode 100644 framework/src/test/java/org/tron/core/net/peer/PeerHelloTimeoutTest.java diff --git a/framework/src/test/java/org/tron/core/net/peer/PeerHelloTimeoutTest.java b/framework/src/test/java/org/tron/core/net/peer/PeerHelloTimeoutTest.java deleted file mode 100644 index fad645714ed..00000000000 --- a/framework/src/test/java/org/tron/core/net/peer/PeerHelloTimeoutTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.tron.core.net.peer; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Collections; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.tron.common.utils.ReflectUtils; -import org.tron.core.capsule.BlockCapsule.BlockId; -import org.tron.core.config.Parameter.NetConstants; -import org.tron.core.net.TronNetDelegate; -import org.tron.core.net.message.handshake.HelloMessage; -import org.tron.p2p.connection.Channel; -import org.tron.protos.Protocol.ReasonCode; - -public class PeerHelloTimeoutTest { - - private PeerStatusCheck service; - private PeerConnection peer; - private Channel channel; - - @Before - public void setUp() { - service = new PeerStatusCheck(); - peer = spy(new PeerConnection()); - channel = mock(Channel.class); - ReflectUtils.setFieldValue(peer, "channel", channel); - doNothing().when(peer).disconnect(any()); - TronNetDelegate delegate = mock(TronNetDelegate.class); - when(delegate.getActivePeer()).thenReturn(Collections.singletonList(peer)); - ReflectUtils.setFieldValue(service, "tronNetDelegate", delegate); - } - - @After - public void tearDown() { - service.close(); - } - - @Test - public void testIncompleteHelloTimesOutAtTenSecondsWithoutSync() { - peer.setNeedSyncFromPeer(false); - peer.setNeedSyncFromUs(false); - when(channel.getStartTime()).thenReturn(System.currentTimeMillis() - 10_000); - - service.statusCheck(); - - verify(peer).disconnect(ReasonCode.TIME_OUT); - } - - @Test - public void testRecentInteractionDoesNotExtendHelloTimeout() { - when(channel.getStartTime()).thenReturn(System.currentTimeMillis() - 20_000); - peer.setLastInteractiveTime(System.currentTimeMillis()); - peer.setBlockBothHave(new BlockId()); - - service.statusCheck(); - - verify(peer).disconnect(ReasonCode.TIME_OUT); - } - - @Test - public void testIncompleteHelloWithinDeadlineDoesNotTimeOut() { - when(channel.getStartTime()).thenReturn(System.currentTimeMillis()); - - service.statusCheck(); - - verify(peer, never()).disconnect(any()); - } - - @Test - public void testIncompleteHelloStillChecksExistingSyncTimeout() { - when(channel.getStartTime()).thenReturn(System.currentTimeMillis()); - ReflectUtils.setFieldValue(peer, "blockBothHaveUpdateTime", 0L); - - service.statusCheck(); - - verify(peer).disconnect(ReasonCode.TIME_OUT); - } - - @Test - public void testCompletedHelloDoesNotTimeOut() { - when(channel.getStartTime()).thenReturn(System.currentTimeMillis() - 60_000); - peer.setHelloMessageReceive(mock(HelloMessage.class)); - peer.setNeedSyncFromPeer(false); - - service.statusCheck(); - - verify(peer, never()).disconnect(any()); - } - - @Test - public void testCompletedHelloStillChecksRequestTimeout() { - peer.setHelloMessageReceive(mock(HelloMessage.class)); - peer.setNeedSyncFromPeer(false); - peer.getSyncBlockRequested().put(new BlockId(), - System.currentTimeMillis() - NetConstants.SYNC_TIME_OUT - 1_000); - - service.statusCheck(); - - verify(peer).disconnect(ReasonCode.TIME_OUT); - } -} diff --git a/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java b/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java index d2ee4be5b87..ac8462df604 100644 --- a/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java +++ b/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java @@ -2,26 +2,60 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import java.util.Collections; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.tron.common.utils.ReflectUtils; +import org.tron.core.capsule.BlockCapsule.BlockId; +import org.tron.core.config.Parameter.NetConstants; +import org.tron.core.net.TronNetDelegate; +import org.tron.core.net.message.handshake.HelloMessage; +import org.tron.p2p.connection.Channel; +import org.tron.protos.Protocol.ReasonCode; public class PeerStatusCheckMockTest { + + private PeerStatusCheck peerStatusCheck; + private PeerConnection peer; + private Channel channel; + + @Before + public void setUp() { + peerStatusCheck = spy(new PeerStatusCheck()); + peer = spy(new PeerConnection()); + channel = mock(Channel.class); + ReflectUtils.setFieldValue(peer, "channel", channel); + doNothing().when(peer).disconnect(any()); + TronNetDelegate delegate = mock(TronNetDelegate.class); + when(delegate.getActivePeer()).thenReturn(Collections.singletonList(peer)); + ReflectUtils.setFieldValue(peerStatusCheck, "tronNetDelegate", delegate); + } + @After - public void clearMocks() { - Mockito.framework().clearInlineMocks(); + public void tearDown() { + try { + peerStatusCheck.close(); + } finally { + Mockito.framework().clearInlineMocks(); + } } @Test public void testInitException() { - PeerStatusCheck peerStatusCheck = spy(new PeerStatusCheck()); + peerStatusCheck.close(); ScheduledExecutorService executor = mock(ScheduledExecutorService.class); ReflectUtils.setFieldValue(peerStatusCheck, "peerStatusCheckExecutor", executor); doThrow(new RuntimeException("test exception")).when(peerStatusCheck).statusCheck(); @@ -41,4 +75,52 @@ public void testInitException() { Mockito.verify(peerStatusCheck).statusCheck(); } + @Test + public void testHelloTimeoutWithoutSync() { + peer.setNeedSyncFromPeer(false); + peer.setNeedSyncFromUs(false); + when(channel.getStartTime()).thenReturn(System.currentTimeMillis()); + assertTimeout(false); + + when(channel.getStartTime()).thenReturn(System.currentTimeMillis() - 10_000); + assertTimeout(true); + } + + @Test + public void testHelloTimeoutDependsOnHandshakeCompletion() { + when(channel.getStartTime()).thenReturn(System.currentTimeMillis() - 60_000); + peer.setLastInteractiveTime(System.currentTimeMillis()); + peer.setBlockBothHave(new BlockId()); + // Recent traffic and block progress must not extend the HELLO deadline. + assertTimeout(true); + + peer.setHelloMessageReceive(mock(HelloMessage.class)); + peer.setNeedSyncFromPeer(false); + assertTimeout(false); + } + + @Test + public void testHelloCheckPreservesExistingTimeouts() { + when(channel.getStartTime()).thenReturn(System.currentTimeMillis()); + ReflectUtils.setFieldValue(peer, "blockBothHaveUpdateTime", 0L); + // A fresh connection must still be subject to the existing sync timeout. + assertTimeout(true); + + peer.setHelloMessageReceive(mock(HelloMessage.class)); + peer.setNeedSyncFromPeer(false); + peer.getSyncBlockRequested().put(new BlockId(), + System.currentTimeMillis() - NetConstants.SYNC_TIME_OUT - 1_000); + assertTimeout(true); + } + + private void assertTimeout(boolean expected) { + clearInvocations(peer); + peerStatusCheck.statusCheck(); + if (expected) { + verify(peer).disconnect(ReasonCode.TIME_OUT); + } else { + verify(peer, never()).disconnect(any()); + } + } + } From f8e310d5be457a5a642a1c01d875981416730914 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Wed, 7 Oct 2026 14:49:24 +0800 Subject: [PATCH 07/11] add validation of node id's length; move HandshakeService's valid before updateNodeId --- .../net/message/handshake/HelloMessage.java | 4 +- .../service/handshake/HandshakeService.java | 14 ++--- .../message/handshake/HelloMessageTest.java | 23 ++++++- .../handshake/HandshakeHeadCheckTest.java | 63 ++++++++++++++++++- .../net/services/HandShakeServiceTest.java | 15 ++++- 5 files changed, 108 insertions(+), 11 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java index c629ec91877..a1f490500b2 100755 --- a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java +++ b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java @@ -11,6 +11,7 @@ import org.tron.core.config.args.Args; import org.tron.core.net.message.MessageTypes; import org.tron.core.net.message.TronMessage; +import org.tron.p2p.base.Constant; import org.tron.p2p.discover.Node; import org.tron.p2p.utils.NetUtil; import org.tron.program.Version; @@ -203,7 +204,8 @@ public boolean validEndPoint() { Endpoint from = this.helloMessage.getFrom(); ByteString ipv4 = from.getAddress(); ByteString ipv6 = from.getAddressIpv6(); - if (from.getPort() <= 0 || from.getPort() > 0xFFFF + if (from.getNodeId().size() != Constant.NODE_ID_LEN + || from.getPort() <= 0 || from.getPort() > 0xFFFF || ipv4.size() > MAX_BYTE_SIZE || ipv6.size() > MAX_BYTE_SIZE) { return false; } diff --git a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java index f8cec9b9d17..744c66585e5 100644 --- a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java +++ b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java @@ -39,13 +39,6 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) { return; } - TronNetService.getP2pService().updateNodeId(peer.getChannel(), msg.getFrom().getHexId()); - if (peer.isDisconnect()) { - logger.info("Duplicate Peer {}", peer.getInetSocketAddress()); - peer.disconnect(ReasonCode.DUPLICATE_PEER); - return; - } - if (!msg.valid()) { logger.warn("Peer {} invalid hello message parameters, genesisHashLength: {}, " + "solidHashLength: {}, headHashLength: {}, address: {}, sig: {}, codeVersion: {}, " @@ -62,6 +55,13 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) { return; } + TronNetService.getP2pService().updateNodeId(peer.getChannel(), msg.getFrom().getHexId()); + if (peer.isDisconnect()) { + logger.info("Duplicate Peer {}", peer.getInetSocketAddress()); + peer.disconnect(ReasonCode.DUPLICATE_PEER); + return; + } + peer.setAddress(msg.getHelloMessage().getAddress()); if (!relayService.checkHelloMessage(msg, peer.getChannel())) { diff --git a/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java b/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java index feabc2160c9..a9effd25c84 100644 --- a/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java +++ b/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java @@ -21,6 +21,7 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.tron.p2p.P2pConfig; +import org.tron.p2p.base.Constant; import org.tron.p2p.base.Parameter; import org.tron.p2p.utils.NetUtil; import org.tron.protos.Discover.Endpoint; @@ -57,6 +58,26 @@ public void testAcceptIpLiteralsAndPortBoundaries() throws Exception { } } + @Test(timeout = 5000) + public void testNodeIdLengthBoundaries() throws Exception { + for (int length : new int[] {0, 63, 64, 65}) { + HelloMessage message = hello("192.0.2.1", "", 18888); + Assert.assertTrue(message.valid()); + message.setHelloMessage(message.getInstance().toBuilder() + .setFrom(message.getInstance().getFrom().toBuilder() + .setNodeId(ByteString.copyFrom(new byte[length]))) + .build()); + if (length == 64) { + HelloMessage checked = spy(message); + Assert.assertTrue(checked.validEndPoint()); + Assert.assertTrue(checked.valid()); + verify(checked, never()).getFrom(); + } else { + assertRejectedBeforeNodeConstruction(message); + } + } + } + @Test(timeout = 5000) public void testRejectHostnamesAndMalformedAddresses() throws Exception { for (String[] hosts : new String[][] { @@ -172,7 +193,7 @@ private static HelloMessage hello(String ipv4, String ipv6, int port) throws Exc .setHash(ByteString.copyFrom(new byte[32])).build(); Endpoint endpoint = Endpoint.newBuilder().setAddress(ByteString.copyFromUtf8(ipv4)) .setAddressIpv6(ByteString.copyFromUtf8(ipv6)) - .setNodeId(ByteString.copyFrom(new byte[64])).setPort(port).build(); + .setNodeId(ByteString.copyFrom(new byte[Constant.NODE_ID_LEN])).setPort(port).build(); return new HelloMessage(Protocol.HelloMessage.newBuilder().setFrom(endpoint) .setGenesisBlockId(block).setSolidBlockId(block).setHeadBlockId(block) .setTimestamp(123).build().toByteArray()); diff --git a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java index 9e7734aecb2..d71eb8803ff 100644 --- a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java +++ b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java @@ -6,13 +6,17 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.protobuf.ByteString; import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; +import org.tron.common.utils.ByteArray; import org.tron.common.utils.ReflectUtils; import org.tron.common.utils.Sha256Hash; import org.tron.core.ChainBaseManager; @@ -24,8 +28,12 @@ import org.tron.core.net.peer.PeerManager; import org.tron.core.net.service.effective.EffectiveCheckService; import org.tron.core.net.service.relay.RelayService; +import org.tron.p2p.P2pConfig; import org.tron.p2p.P2pService; +import org.tron.p2p.base.Parameter; import org.tron.p2p.connection.Channel; +import org.tron.p2p.connection.ChannelManager; +import org.tron.p2p.connection.message.base.P2pDisconnectMessage; import org.tron.p2p.discover.Node; import org.tron.protos.Protocol; import org.tron.protos.Protocol.ReasonCode; @@ -36,6 +44,7 @@ public class HandshakeHeadCheckTest { private ChainBaseManager chain; private PeerConnection peer; private Channel channel; + private P2pService p2p; private EffectiveCheckService effectiveCheckService; private MockedStatic netService; private MockedStatic peerManager; @@ -71,7 +80,7 @@ public void setUp() { when(chain.containBlockInMainChain(solid)).thenReturn(true); when(chain.containBlockInMainChain(localHead)).thenReturn(true); - P2pService p2p = mock(P2pService.class); + p2p = mock(P2pService.class); netService = Mockito.mockStatic(TronNetService.class); netService.when(TronNetService::getP2pService).thenReturn(p2p); peerManager = Mockito.mockStatic(PeerManager.class); @@ -247,6 +256,58 @@ public void testDuplicateHelloIsStillRejected() { assertRejected(ReasonCode.BAD_PROTOCOL); } + @Test + public void testInvalidHelloDoesNotCloseDuplicateConnection() { + HelloMessage hello = Mockito.spy(hello(localHead)); + Assert.assertTrue(hello.valid()); + String nodeId = ByteArray.toHexString(hello.getInstance().getFrom().getNodeId().toByteArray()); + hello.setHelloMessage(hello.getInstance().toBuilder() + .setHeadBlockId(hello.getInstance().getHeadBlockId().toBuilder() + .setHash(ByteString.copyFrom(new byte[31]))) + .build()); + // A valid endpoint must not bypass rejection of other malformed HELLO fields. + Assert.assertTrue(hello.validEndPoint()); + Assert.assertFalse(hello.valid()); + + Channel other = mock(Channel.class); + InetSocketAddress address = peer.getInetSocketAddress(); + InetSocketAddress otherAddress = new InetSocketAddress("127.0.0.2", 18888); + when(channel.getInetSocketAddress()).thenReturn(address); + when(channel.getNodeId()).thenReturn(nodeId); + when(channel.getStartTime()).thenReturn(100L); + when(other.getInetSocketAddress()).thenReturn(otherAddress); + when(other.getNodeId()).thenReturn(nodeId); + when(other.getStartTime()).thenReturn(200L); + Mockito.doCallRealMethod().when(p2p).updateNodeId(any(), any()); + Map channels = ChannelManager.getChannels(); + Map savedChannels = new HashMap<>(channels); + P2pConfig savedConfig = Parameter.p2pConfig; + try { + Parameter.p2pConfig = new P2pConfig(); + channels.clear(); + channels.put(channel.getInetSocketAddress(), channel); + channels.put(otherAddress, other); + + service.processHelloMessage(peer, hello); + + assertRejected(ReasonCode.INCOMPATIBLE_PROTOCOL); + verify(hello, never()).getFrom(); + verify(p2p, never()).updateNodeId(any(), any()); + verify(channel, never()).setNodeId(any()); + verify(other, never()).send(any(P2pDisconnectMessage.class)); + verify(other, never()).close(); + + // Prove the fixture would close the other connection if deduplication ran. + p2p.updateNodeId(channel, nodeId); + verify(other).send(any(P2pDisconnectMessage.class)); + verify(other).close(); + } finally { + channels.clear(); + channels.putAll(savedChannels); + Parameter.p2pConfig = savedConfig; + } + } + private HelloMessage hello(BlockId head) { HelloMessage hello = new HelloMessage( new Node(new byte[64], "127.0.0.1", null, 18888), 0, chain); diff --git a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java index 94a89ae5ec3..08321eae88a 100644 --- a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java @@ -363,6 +363,15 @@ public void testInvalidHelloLogsEndpointValidity() throws Exception { assertInvalidHelloLog(32, 32, 32, endpoint("", "2001:db8::1", 18888), 201, true); } + @Test(timeout = 5000) + public void testInvalidHelloLogsNodeIdLengths() throws Exception { + for (int length : new int[] {0, 63, 65}) { + Endpoint invalidEndpoint = endpoint("127.0.0.1", "", 18888).toBuilder() + .setNodeId(ByteString.copyFrom(new byte[length])).build(); + assertInvalidHelloLog(32, 32, 32, invalidEndpoint, 0, false); + } + } + private void assertInvalidHelloLog(int genesisLength, int solidLength, int headLength, Endpoint endpoint, int signatureLength, boolean expectedEndpointValid) throws Exception { Protocol.HelloMessage proto = Protocol.HelloMessage.newBuilder() @@ -373,7 +382,9 @@ private void assertInvalidHelloLog(int genesisLength, int solidLength, int headL .setSignature(ByteString.copyFrom(new byte[signatureLength])) .build(); Assert.assertTrue(proto.getSerializedSize() + 1 < Parameter.MAX_MESSAGE_LENGTH); - HelloMessage hello = new HelloMessage(proto.toByteArray()); + HelloMessage hello = Mockito.spy(new HelloMessage(proto.toByteArray())); + Mockito.doThrow(new AssertionError("Invalid HELLO reached Node construction")) + .when(hello).getFrom(); Assert.assertEquals(expectedEndpointValid, hello.validEndPoint()); Assert.assertFalse(hello.valid()); @@ -405,6 +416,8 @@ private void assertInvalidHelloLog(int genesisLength, int solidLength, int headL new HandshakeService().processHelloMessage(testPeer, hello); + verify(hello, never()).getFrom(); + Mockito.verifyNoInteractions(p2p); verify(testPeer).disconnect(ReasonCode.INCOMPATIBLE_PROTOCOL); verify(testPeer, never()).setHelloMessageReceive(any()); verify(testPeer, never()).onConnect(); From 266846ae7006d0dc450fd1317c9b716d33d0c8e0 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Fri, 16 Oct 2026 11:34:03 +0800 Subject: [PATCH 08/11] drop validate of HelloMessage --- .../net/message/handshake/HelloMessage.java | 41 +----- .../service/handshake/HandshakeService.java | 31 ++-- .../message/handshake/HelloMessageTest.java | 139 ------------------ .../handshake/HandshakeHeadCheckTest.java | 2 - .../net/services/HandShakeServiceTest.java | 31 +--- 5 files changed, 22 insertions(+), 222 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java index a1f490500b2..a36714ede8f 100755 --- a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java +++ b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java @@ -11,9 +11,7 @@ import org.tron.core.config.args.Args; import org.tron.core.net.message.MessageTypes; import org.tron.core.net.message.TronMessage; -import org.tron.p2p.base.Constant; import org.tron.p2p.discover.Node; -import org.tron.p2p.utils.NetUtil; import org.tron.program.Version; import org.tron.protos.Discover.Endpoint; import org.tron.protos.Protocol; @@ -21,8 +19,6 @@ public class HelloMessage extends TronMessage { - private static final int MAX_BYTE_SIZE = 200; - @Getter private Protocol.HelloMessage helloMessage; @@ -124,10 +120,6 @@ public Class getAnswerMessage() { @Override public String toString() { - if (!valid()) { - return "P2P_HELLO: invalid hello message"; - } - StringBuilder builder = new StringBuilder(); builder.append(super.toString()) @@ -163,10 +155,6 @@ public Protocol.HelloMessage getInstance() { } public boolean valid() { - if (!validEndPoint()) { - return false; - } - byte[] genesisBlockByte = this.helloMessage.getGenesisBlockId().getHash().toByteArray(); if (genesisBlockByte.length != Sha256Hash.LENGTH) { return false; @@ -182,46 +170,25 @@ public boolean valid() { return false; } + int maxByteSize = 200; ByteString address = this.helloMessage.getAddress(); - if (!address.isEmpty() && address.toByteArray().length > MAX_BYTE_SIZE) { + if (!address.isEmpty() && address.toByteArray().length > maxByteSize) { return false; } ByteString sig = this.helloMessage.getSignature(); - if (!sig.isEmpty() && sig.toByteArray().length > MAX_BYTE_SIZE) { + if (!sig.isEmpty() && sig.toByteArray().length > maxByteSize) { return false; } ByteString codeVersion = this.helloMessage.getCodeVersion(); - if (!codeVersion.isEmpty() && codeVersion.toByteArray().length > MAX_BYTE_SIZE) { + if (!codeVersion.isEmpty() && codeVersion.toByteArray().length > maxByteSize) { return false; } return true; } - public boolean validEndPoint() { - Endpoint from = this.helloMessage.getFrom(); - ByteString ipv4 = from.getAddress(); - ByteString ipv6 = from.getAddressIpv6(); - if (from.getNodeId().size() != Constant.NODE_ID_LEN - || from.getPort() <= 0 || from.getPort() > 0xFFFF - || ipv4.size() > MAX_BYTE_SIZE || ipv6.size() > MAX_BYTE_SIZE) { - return false; - } - if (ipv4.isEmpty() && ipv6.isEmpty()) { - return false; - } - // Validate raw literals before getFrom() constructs a Node during logging. - if (!ipv4.isEmpty() && !NetUtil.validIpV4(ByteArray.toStr(ipv4.toByteArray()))) { - return false; - } - if (!ipv6.isEmpty() && !NetUtil.validIpV6(ByteArray.toStr(ipv6.toByteArray()))) { - return false; - } - return true; - } - public static Endpoint getEndpointFromNode(Node node) { Endpoint.Builder builder = Endpoint.newBuilder() .setPort(node.getPort()); diff --git a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java index cd42c7419f1..357eea7f727 100644 --- a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java +++ b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.tron.common.utils.ByteArray; import org.tron.core.ChainBaseManager; import org.tron.core.config.args.Args; import org.tron.core.net.TronNetService; @@ -44,22 +45,6 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) { return; } - if (!msg.valid()) { - logger.warn("Peer {} invalid hello message parameters, genesisHashLength: {}, " - + "solidHashLength: {}, headHashLength: {}, address: {}, sig: {}, codeVersion: {}, " - + "endpointValid: {}", - peer.getInetSocketAddress(), - msg.getInstance().getGenesisBlockId().getHash().size(), - msg.getInstance().getSolidBlockId().getHash().size(), - msg.getInstance().getHeadBlockId().getHash().size(), - msg.getInstance().getAddress().size(), - msg.getInstance().getSignature().size(), - msg.getInstance().getCodeVersion().size(), - msg.validEndPoint()); - peer.disconnect(ReasonCode.INCOMPATIBLE_PROTOCOL); - return; - } - if (!Objects.equals(peer.getChannel().getNodeId(), msg.getFrom().getHexId())) { logger.warn("Peer {} sent an application HELLO with a different node ID", peer.getInetSocketAddress()); @@ -67,6 +52,20 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) { return; } + if (!msg.valid()) { + logger.warn("Peer {} invalid hello message parameters, GenesisBlockId: {}, SolidBlockId: {}, " + + "HeadBlockId: {}, address: {}, sig: {}, codeVersion: {}", + peer.getInetSocketAddress(), + ByteArray.toHexString(msg.getInstance().getGenesisBlockId().getHash().toByteArray()), + ByteArray.toHexString(msg.getInstance().getSolidBlockId().getHash().toByteArray()), + ByteArray.toHexString(msg.getInstance().getHeadBlockId().getHash().toByteArray()), + msg.getInstance().getAddress().toByteArray().length, + msg.getInstance().getSignature().toByteArray().length, + msg.getInstance().getCodeVersion().toByteArray().length); + peer.disconnect(ReasonCode.INCOMPATIBLE_PROTOCOL); + return; + } + peer.setAddress(msg.getHelloMessage().getAddress()); if (!relayService.checkHelloMessage(msg, peer.getChannel())) { diff --git a/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java b/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java index a9effd25c84..a0e05d2fa66 100644 --- a/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java +++ b/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java @@ -1,29 +1,14 @@ package org.tron.core.net.message.handshake; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import ch.qos.logback.classic.AsyncAppender; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.util.LogbackMDCAdapter; -import ch.qos.logback.core.read.ListAppender; import com.google.protobuf.ByteString; import java.net.InetSocketAddress; -import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.mockito.MockedStatic; -import org.mockito.Mockito; import org.tron.p2p.P2pConfig; import org.tron.p2p.base.Constant; import org.tron.p2p.base.Parameter; -import org.tron.p2p.utils.NetUtil; import org.tron.protos.Discover.Endpoint; import org.tron.protos.Protocol; @@ -44,89 +29,6 @@ public void tearDown() { Parameter.p2pConfig = savedConfig; } - @Test(timeout = 5000) - public void testAcceptIpLiteralsAndPortBoundaries() throws Exception { - for (String[] hosts : new String[][] { - {"192.0.2.1", ""}, {"", "2001:db8::1"}, {"192.0.2.1", "2001:db8::1"}, - {"", "::1"}, {"", "::ffff:192.0.2.1"}}) { - for (int port : new int[] {1, 18888, 65535}) { - HelloMessage message = spy(hello(hosts[0], hosts[1], port)); - Assert.assertTrue(message.validEndPoint()); - Assert.assertTrue(message.valid()); - verify(message, never()).getFrom(); - } - } - } - - @Test(timeout = 5000) - public void testNodeIdLengthBoundaries() throws Exception { - for (int length : new int[] {0, 63, 64, 65}) { - HelloMessage message = hello("192.0.2.1", "", 18888); - Assert.assertTrue(message.valid()); - message.setHelloMessage(message.getInstance().toBuilder() - .setFrom(message.getInstance().getFrom().toBuilder() - .setNodeId(ByteString.copyFrom(new byte[length]))) - .build()); - if (length == 64) { - HelloMessage checked = spy(message); - Assert.assertTrue(checked.validEndPoint()); - Assert.assertTrue(checked.valid()); - verify(checked, never()).getFrom(); - } else { - assertRejectedBeforeNodeConstruction(message); - } - } - } - - @Test(timeout = 5000) - public void testRejectHostnamesAndMalformedAddresses() throws Exception { - for (String[] hosts : new String[][] { - {"hello.invalid", ""}, {"", "hello.invalid"}, - {"hello.invalid", "2001:db8::1"}, {"192.0.2.1", "hello.invalid"}, - {"256.0.0.1", ""}, {"192.0.2.1\n", ""}, {" 192.0.2.1", ""}, - {"2001:db8::1", ""}, {"", "192.0.2.1"}, {"", "2001:db8:::1"}}) { - assertRejectedBeforeNodeConstruction(hello(hosts[0], hosts[1], 18888)); - } - } - - @Test(timeout = 5000) - public void testRejectMissingAddresses() throws Exception { - assertRejectedBeforeNodeConstruction(hello("", "", 18888)); - HelloMessage missingFrom = hello("192.0.2.1", "", 18888); - missingFrom.setHelloMessage(missingFrom.getInstance().toBuilder().clearFrom().build()); - assertRejectedBeforeNodeConstruction(missingFrom); - } - - @Test(timeout = 5000) - public void testRejectInvalidPorts() throws Exception { - for (int port : new int[] {Integer.MIN_VALUE, -1, 0, 65536, Integer.MAX_VALUE}) { - assertRejectedBeforeNodeConstruction(hello("192.0.2.1", "2001:db8::1", port)); - } - } - - @Test(timeout = 5000) - public void testRejectOversizedAddressesBeforeLiteralValidation() throws Exception { - String oversized = StringUtils.repeat('a', 201); - try (MockedStatic netUtil = Mockito.mockStatic(NetUtil.class)) { - assertRejectedBeforeNodeConstruction(hello(oversized, "", 18888)); - assertRejectedBeforeNodeConstruction(hello("192.0.2.1", oversized, 18888)); - netUtil.verifyNoInteractions(); - } - } - - @Test(timeout = 5000) - public void testIpv6AddressLengthBoundary() throws Exception { - String scoped = "fe80::1%" + StringUtils.repeat('a', 192); - Assert.assertEquals(200, scoped.length()); - Assert.assertTrue(NetUtil.validIpV6(scoped)); - HelloMessage message = spy(hello("", scoped, 18888)); - Assert.assertTrue(message.valid()); - verify(message, never()).getFrom(); - // The syntax remains valid, so rejection must come from the byte limit. - Assert.assertTrue(NetUtil.validIpV6(scoped + "a")); - assertRejectedBeforeNodeConstruction(hello("", scoped + "a", 18888)); - } - @Test(timeout = 5000) public void testValidAddressLogFormat() throws Exception { for (String[] hosts : new String[][] { @@ -147,47 +49,6 @@ public void testValidAddressLogFormat() throws Exception { } } - @Test(timeout = 5000) - public void testAsyncLoggingRejectsHostnameBeforeNodeConstruction() throws Exception { - HelloMessage message = spy(hello("hello.invalid", "", 18888)); - doThrow(new AssertionError("Invalid endpoint reached Node construction")) - .when(message).getFrom(); - LoggerContext context = new LoggerContext(); - context.setMDCAdapter(new LogbackMDCAdapter()); - ListAppender sink = new ListAppender<>(); - sink.setContext(context); - sink.start(); - AsyncAppender async = new AsyncAppender(); - async.setContext(context); - async.addAppender(sink); - async.setDiscardingThreshold(0); - async.start(); - ch.qos.logback.classic.Logger logger = context.getLogger("hello.validation.test"); - logger.setLevel(Level.INFO); - logger.setAdditive(false); - logger.addAppender(async); - try { - logger.info("Receive HELLO {}", message); - } finally { - async.stop(); - context.stop(); - } - Assert.assertEquals(1, sink.list.size()); - Assert.assertEquals("Receive HELLO P2P_HELLO: invalid hello message", - sink.list.get(0).getFormattedMessage()); - verify(message, never()).getFrom(); - } - - private static void assertRejectedBeforeNodeConstruction(HelloMessage message) { - HelloMessage checked = spy(message); - doThrow(new AssertionError("Invalid endpoint reached Node construction")) - .when(checked).getFrom(); - Assert.assertFalse(checked.validEndPoint()); - Assert.assertFalse(checked.valid()); - Assert.assertEquals("P2P_HELLO: invalid hello message", checked.toString()); - verify(checked, never()).getFrom(); - } - private static HelloMessage hello(String ipv4, String ipv6, int port) throws Exception { Protocol.HelloMessage.BlockId block = Protocol.HelloMessage.BlockId.newBuilder() .setHash(ByteString.copyFrom(new byte[32])).build(); diff --git a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java index 09961b815dd..84f0bfacdd3 100644 --- a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java +++ b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java @@ -266,8 +266,6 @@ public void testInvalidHelloDoesNotCloseDuplicateConnection() { .setHeadBlockId(hello.getInstance().getHeadBlockId().toBuilder() .setHash(ByteString.copyFrom(new byte[31]))) .build()); - // A valid endpoint must not bypass rejection of other malformed HELLO fields. - Assert.assertTrue(hello.validEndPoint()); Assert.assertFalse(hello.valid()); Channel other = mock(Channel.class); diff --git a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java index ee464270ca6..fab9c21b6d6 100644 --- a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java @@ -352,36 +352,12 @@ public void testInvalidHelloLogsHashLengths() throws Exception { {32, 32, 32}, {0, 32, 32}, {32, 31, 32}, {32, 32, 33}, {0, 31, 33}, {largeHashLength, 32, 32}, {32, largeHashLength, 32}, {32, 32, largeHashLength}}) { // The oversized signature also exercises logging when all three hashes are 32 bytes. - assertInvalidHelloLog(lengths[0], lengths[1], lengths[2], validEndpoint, 201, true); - } - } - - @Test - public void testInvalidHelloLogsEndpointValidity() throws Exception { - for (Endpoint invalidEndpoint : new Endpoint[] { - endpoint("127.0.0.1", "", 0), endpoint("127.0.0.1", "", 65536), - Endpoint.getDefaultInstance(), endpoint("", "", 18888), - endpoint("hello.invalid", "", 18888), endpoint("127.0.0.1", "2001:db8:::1", 18888), - endpoint("127.0.0.1\nforged", "", 18888), - endpoint(StringUtils.repeat('a', Parameter.MAX_MESSAGE_LENGTH - 1024), "", 18888), - endpoint("127.0.0.1", StringUtils.repeat('a', 201), 18888)}) { - // Keep all other HELLO fields valid so the endpoint alone causes rejection. - assertInvalidHelloLog(32, 32, 32, invalidEndpoint, 0, false); - } - assertInvalidHelloLog(32, 32, 32, endpoint("", "2001:db8::1", 18888), 201, true); - } - - @Test(timeout = 5000) - public void testInvalidHelloLogsNodeIdLengths() throws Exception { - for (int length : new int[] {0, 63, 65}) { - Endpoint invalidEndpoint = endpoint("127.0.0.1", "", 18888).toBuilder() - .setNodeId(ByteString.copyFrom(new byte[length])).build(); - assertInvalidHelloLog(32, 32, 32, invalidEndpoint, 0, false); + assertInvalidHelloLog(lengths[0], lengths[1], lengths[2], validEndpoint, 201); } } private void assertInvalidHelloLog(int genesisLength, int solidLength, int headLength, - Endpoint endpoint, int signatureLength, boolean expectedEndpointValid) throws Exception { + Endpoint endpoint, int signatureLength) throws Exception { Protocol.HelloMessage proto = Protocol.HelloMessage.newBuilder() .setFrom(endpoint) .setGenesisBlockId(blockId(genesisLength, (byte) 0x11)) @@ -393,7 +369,6 @@ private void assertInvalidHelloLog(int genesisLength, int solidLength, int headL HelloMessage hello = Mockito.spy(new HelloMessage(proto.toByteArray())); Mockito.doThrow(new AssertionError("Invalid HELLO reached Node construction")) .when(hello).getFrom(); - Assert.assertEquals(expectedEndpointValid, hello.validEndPoint()); Assert.assertFalse(hello.valid()); PeerConnection testPeer = mock(PeerConnection.class); @@ -441,7 +416,7 @@ private void assertInvalidHelloLog(int genesisLength, int solidLength, int headL Assert.assertEquals("Peer /127.0.0.1:18888 invalid hello message parameters, " + "genesisHashLength: " + genesisLength + ", solidHashLength: " + solidLength + ", headHashLength: " + headLength + ", address: 0, sig: " + signatureLength - + ", codeVersion: 0, endpointValid: " + expectedEndpointValid, text); + + ", codeVersion: 0", text); } finally { logger.detachAppender(sink); sink.stop(); From 07370c69d6862a15f8d39dd54ddc8fa67b9e7e10 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Fri, 16 Oct 2026 12:08:09 +0800 Subject: [PATCH 09/11] fix testcase --- .../core/net/Base58MessageLoggingTest.java | 2 + .../handshake/HandshakeHeadCheckTest.java | 1 - .../net/services/HandShakeServiceTest.java | 111 ------------------ 3 files changed, 2 insertions(+), 112 deletions(-) diff --git a/framework/src/test/java/org/tron/core/net/Base58MessageLoggingTest.java b/framework/src/test/java/org/tron/core/net/Base58MessageLoggingTest.java index 8bc34312e26..dfb48660f7c 100644 --- a/framework/src/test/java/org/tron/core/net/Base58MessageLoggingTest.java +++ b/framework/src/test/java/org/tron/core/net/Base58MessageLoggingTest.java @@ -139,6 +139,8 @@ public void pbftSignatureFailureLogIsSafeAndDisabledPbftStillReturnsEarly() thro .addSrAddress(bytes(16384)).build().toByteString()); for (boolean enabled : new boolean[]{false, true}) { PeerConnection peer = peer(); + // PBFT messages reach this handler only after the HELLO handshake. + when(peer.getHelloMessageReceive()).thenReturn(mock(HelloMessage.class)); Channel channel = peer.getChannel(); P2pEventHandlerImpl handler = new P2pEventHandlerImpl(); PbftMsgHandler pbftHandler = new PbftMsgHandler(); diff --git a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java index 84f0bfacdd3..459f28e5c4c 100644 --- a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java +++ b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java @@ -289,7 +289,6 @@ public void testInvalidHelloDoesNotCloseDuplicateConnection() { service.processHelloMessage(peer, hello); assertRejected(ReasonCode.INCOMPATIBLE_PROTOCOL); - verify(hello, never()).getFrom(); verify(p2p, never()).updateNodeId(any(), any()); verify(channel, never()).setNodeId(any()); verify(other, never()).send(any(P2pDisconnectMessage.class)); diff --git a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java index fab9c21b6d6..9f7a1d7d834 100644 --- a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java @@ -7,23 +7,14 @@ import static org.mockito.Mockito.when; import static org.tron.core.net.message.handshake.HelloMessage.getEndpointFromNode; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.read.ListAppender; import com.google.protobuf.ByteString; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; import java.util.Random; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.AfterClass; @@ -32,9 +23,7 @@ import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.tron.common.TestConstants; import org.tron.common.application.TronApplicationContext; @@ -52,7 +41,6 @@ import org.tron.core.net.peer.PeerManager; import org.tron.core.net.service.handshake.HandshakeService; import org.tron.p2p.P2pConfig; -import org.tron.p2p.P2pService; import org.tron.p2p.base.Parameter; import org.tron.p2p.connection.Channel; import org.tron.p2p.connection.ChannelManager; @@ -62,7 +50,6 @@ import org.tron.protos.Discover.Endpoint; import org.tron.protos.Protocol; import org.tron.protos.Protocol.HelloMessage.Builder; -import org.tron.protos.Protocol.ReasonCode; public class HandShakeServiceTest { @@ -344,102 +331,6 @@ public void testProcessHelloMessage() { } } - @Test - public void testInvalidHelloLogsHashLengths() throws Exception { - int largeHashLength = Parameter.MAX_MESSAGE_LENGTH - 1024; - Endpoint validEndpoint = endpoint("127.0.0.1", "", 18888); - for (int[] lengths : new int[][] { - {32, 32, 32}, {0, 32, 32}, {32, 31, 32}, {32, 32, 33}, {0, 31, 33}, - {largeHashLength, 32, 32}, {32, largeHashLength, 32}, {32, 32, largeHashLength}}) { - // The oversized signature also exercises logging when all three hashes are 32 bytes. - assertInvalidHelloLog(lengths[0], lengths[1], lengths[2], validEndpoint, 201); - } - } - - private void assertInvalidHelloLog(int genesisLength, int solidLength, int headLength, - Endpoint endpoint, int signatureLength) throws Exception { - Protocol.HelloMessage proto = Protocol.HelloMessage.newBuilder() - .setFrom(endpoint) - .setGenesisBlockId(blockId(genesisLength, (byte) 0x11)) - .setSolidBlockId(blockId(solidLength, (byte) 0x22)) - .setHeadBlockId(blockId(headLength, (byte) 0x33)) - .setSignature(ByteString.copyFrom(new byte[signatureLength])) - .build(); - Assert.assertTrue(proto.getSerializedSize() + 1 < Parameter.MAX_MESSAGE_LENGTH); - HelloMessage hello = Mockito.spy(new HelloMessage(proto.toByteArray())); - Mockito.doThrow(new AssertionError("Invalid HELLO reached Node construction")) - .when(hello).getFrom(); - Assert.assertFalse(hello.valid()); - - PeerConnection testPeer = mock(PeerConnection.class); - when(testPeer.getChannel()).thenReturn(mock(Channel.class)); - when(testPeer.getInetSocketAddress()).thenReturn(new InetSocketAddress("127.0.0.1", 18888)); - P2pService p2p = mock(P2pService.class); - Logger logger = (Logger) LoggerFactory.getLogger("net"); - Level originalLevel = logger.getLevel(); - boolean originalAdditive = logger.isAdditive(); - List> originalAppenders = new ArrayList<>(); - Iterator> iterator = logger.iteratorForAppenders(); - while (iterator.hasNext()) { - originalAppenders.add(iterator.next()); - } - ListAppender sink = new ListAppender<>(); - // The shared application context can also log from background threads. - sink.list = new CopyOnWriteArrayList<>(); - sink.setContext(logger.getLoggerContext()); - sink.start(); - try (MockedStatic netService = Mockito.mockStatic(TronNetService.class)) { - netService.when(TronNetService::getP2pService).thenReturn(p2p); - for (Appender appender : originalAppenders) { - logger.detachAppender(appender); - } - logger.setAdditive(false); - logger.setLevel(Level.WARN); - logger.addAppender(sink); - - new HandshakeService().processHelloMessage(testPeer, hello); - - verify(hello, never()).getFrom(); - Mockito.verifyNoInteractions(p2p); - verify(testPeer).disconnect(ReasonCode.INCOMPATIBLE_PROTOCOL); - verify(testPeer, never()).setHelloMessageReceive(any()); - verify(testPeer, never()).onConnect(); - List warnings = sink.list.stream() - .filter(event -> event.getMessage() - .startsWith("Peer {} invalid hello message parameters")) - .collect(Collectors.toList()); - Assert.assertEquals(1, warnings.size()); - ILoggingEvent event = warnings.get(0); - Assert.assertEquals(Level.WARN, event.getLevel()); - String text = event.getFormattedMessage(); - Assert.assertTrue("Invalid HELLO warning must remain bounded", text.length() < 512); - Assert.assertEquals("Peer /127.0.0.1:18888 invalid hello message parameters, " - + "genesisHashLength: " + genesisLength + ", solidHashLength: " + solidLength - + ", headHashLength: " + headLength + ", address: 0, sig: " + signatureLength - + ", codeVersion: 0", text); - } finally { - logger.detachAppender(sink); - sink.stop(); - for (Appender appender : originalAppenders) { - logger.addAppender(appender); - } - logger.setLevel(originalLevel); - logger.setAdditive(originalAdditive); - } - } - - private static Endpoint endpoint(String ipv4, String ipv6, int port) { - return Endpoint.newBuilder().setNodeId(ByteString.copyFrom(new byte[64])) - .setAddress(ByteString.copyFromUtf8(ipv4)).setAddressIpv6(ByteString.copyFromUtf8(ipv6)) - .setPort(port).build(); - } - - private static Protocol.HelloMessage.BlockId blockId(int length, byte value) { - byte[] hash = new byte[length]; - Arrays.fill(hash, value); - return Protocol.HelloMessage.BlockId.newBuilder().setHash(ByteString.copyFrom(hash)).build(); - } - @Test public void testMismatchedHelloNodeIdPreservesEstablishedIdentity() throws Exception { String establishedNodeId = StringUtils.repeat("01", 64); @@ -451,7 +342,6 @@ public void testMismatchedHelloNodeIdPreservesEstablishedIdentity() throws Excep PeerConnection connection = mock(PeerConnection.class); Mockito.when(connection.getChannel()).thenReturn(channel); HelloMessage message = mock(HelloMessage.class); - Mockito.when(message.valid()).thenReturn(true); byte[] helloNodeIdBytes = new byte[64]; Arrays.fill(helloNodeIdBytes, (byte) 2); Mockito.when(message.getFrom()).thenReturn(new Node(helloNodeIdBytes, null, null, 0)); @@ -466,7 +356,6 @@ public void testMismatchedHelloNodeIdPreservesEstablishedIdentity() throws Excep Mockito.verify(connection).disconnect(Protocol.ReasonCode.UNEXPECTED_IDENTITY); Mockito.verify(connection, Mockito.never()).setHelloMessageReceive(Mockito.any()); - Mockito.verify(message).valid(); Assert.assertEquals(establishedNodeId, channel.getNodeId()); Assert.assertEquals(helloNodeId, otherChannel.getNodeId()); Assert.assertFalse(otherChannel.isDisconnect()); From d1dc60bbb4fc539a5bdc3fa045c6629cd933c7a7 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Fri, 16 Oct 2026 14:03:32 +0800 Subject: [PATCH 10/11] simply testcases --- .../org/tron/core/net/P2pHelloGateTest.java | 109 +----- .../message/handshake/HelloMessageTest.java | 62 ---- .../net/peer/PeerStatusCheckMockTest.java | 95 ++--- .../handshake/HandshakeHeadCheckTest.java | 335 ------------------ .../net/services/HandShakeServiceTest.java | 21 +- 5 files changed, 41 insertions(+), 581 deletions(-) delete mode 100644 framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java delete mode 100644 framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java diff --git a/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java b/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java index 64a1a3aaf1f..55a84e10001 100644 --- a/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java +++ b/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java @@ -6,96 +6,53 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; -import java.util.Collections; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.tron.common.utils.Sha256Hash; -import org.tron.consensus.pbft.message.PbftMessage; +import org.tron.common.utils.ReflectUtils; import org.tron.core.ChainBaseManager; import org.tron.core.ChainBaseManager.NodeType; import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.net.message.MessageTypes; import org.tron.core.net.message.PbftMessageFactory; import org.tron.core.net.message.TronMessageFactory; -import org.tron.core.net.message.adv.InventoryMessage; import org.tron.core.net.message.base.DisconnectMessage; import org.tron.core.net.message.handshake.HelloMessage; -import org.tron.core.net.message.keepalive.PingMessage; -import org.tron.core.net.messagehandler.BlockMsgHandler; -import org.tron.core.net.messagehandler.ChainInventoryMsgHandler; -import org.tron.core.net.messagehandler.FetchInvDataMsgHandler; -import org.tron.core.net.messagehandler.InventoryMsgHandler; -import org.tron.core.net.messagehandler.PbftDataSyncHandler; -import org.tron.core.net.messagehandler.PbftMsgHandler; -import org.tron.core.net.messagehandler.SyncBlockChainMsgHandler; -import org.tron.core.net.messagehandler.TransactionsMsgHandler; import org.tron.core.net.peer.PeerConnection; import org.tron.core.net.peer.PeerManager; import org.tron.core.net.service.handshake.HandshakeService; -import org.tron.core.net.service.keepalive.KeepAliveService; import org.tron.core.net.service.statistics.NodeStatistics; import org.tron.core.net.service.statistics.PeerStatistics; import org.tron.p2p.connection.Channel; import org.tron.p2p.discover.Node; -import org.tron.protos.Protocol.Inventory.InventoryType; import org.tron.protos.Protocol.ReasonCode; public class P2pHelloGateTest { - @InjectMocks - private P2pEventHandlerImpl handler; - @Mock - private HandshakeService handshakeService; - @Mock - private KeepAliveService keepAliveService; - @Mock - private SyncBlockChainMsgHandler syncBlockChainMsgHandler; - @Mock - private ChainInventoryMsgHandler chainInventoryMsgHandler; - @Mock - private InventoryMsgHandler inventoryMsgHandler; - @Mock - private FetchInvDataMsgHandler fetchInvDataMsgHandler; - @Mock - private BlockMsgHandler blockMsgHandler; - @Mock - private TransactionsMsgHandler transactionsMsgHandler; - @Mock - private PbftDataSyncHandler pbftDataSyncHandler; - @Mock - private PbftMsgHandler pbftMsgHandler; - @Mock - private PeerConnection peer; - @Mock - private Channel channel; - - private AutoCloseable mocks; + private final P2pEventHandlerImpl handler = new P2pEventHandlerImpl(); + private final Channel channel = mock(Channel.class); + private final PeerConnection peer = mock(PeerConnection.class); + private final HandshakeService handshakeService = mock(HandshakeService.class); private MockedStatic peerManager; @Before public void setUp() { - mocks = MockitoAnnotations.openMocks(this); + ReflectUtils.setFieldValue(handler, "handshakeService", handshakeService); peerManager = Mockito.mockStatic(PeerManager.class); peerManager.when(() -> PeerManager.getPeerConnection(channel)).thenReturn(peer); } @After - public void tearDown() throws Exception { + public void tearDown() { peerManager.close(); - mocks.close(); } @Test - public void testRejectAllOtherMessageTypesBeforeParsing() { + public void testRejectMessagesBeforeHelloWithoutParsing() { try (MockedStatic tronFactory = Mockito.mockStatic(TronMessageFactory.class); MockedStatic pbftFactory = @@ -111,26 +68,15 @@ public void testRejectAllOtherMessageTypesBeforeParsing() { tronFactory.verifyNoInteractions(); pbftFactory.verifyNoInteractions(); } - verify(peer, never()).getPeerStatistics(); - verifyNoInteractions(handshakeService, keepAliveService, syncBlockChainMsgHandler, - chainInventoryMsgHandler, inventoryMsgHandler, fetchInvDataMsgHandler, blockMsgHandler, - transactionsMsgHandler, pbftDataSyncHandler, pbftMsgHandler); } @Test - public void testEmptyFramesAreRejected() { + public void testRejectEmptyFrames() { handler.onMessage(channel, new byte[0]); handler.onMessage(channel, null); verify(peer, times(2)).disconnect(ReasonCode.BAD_PROTOCOL); } - @Test - public void testUnknownPeerIsIgnored() { - peerManager.when(() -> PeerManager.getPeerConnection(channel)).thenReturn(null); - handler.onMessage(channel, new PingMessage().getSendBytes()); - verifyNoInteractions(peer, keepAliveService); - } - @Test public void testHelloIsDispatchedBeforeHandshake() throws Exception { ChainBaseManager chain = mock(ChainBaseManager.class); @@ -162,41 +108,4 @@ public void testDisconnectIsDispatchedBeforeHandshake() { verify(stats).nodeDisconnectedRemote(ReasonCode.PEER_QUITING); verify(peer, never()).disconnect(any()); } - - @Test - public void testKeepAliveIsDispatchedAfterHello() throws Exception { - when(peer.getHelloMessageReceive()).thenReturn(mock(HelloMessage.class)); - when(peer.getPeerStatistics()).thenReturn(new PeerStatistics()); - - handler.onMessage(channel, new PingMessage().getSendBytes()); - - verify(keepAliveService).processMessage(eq(peer), any(PingMessage.class)); - verify(peer, never()).disconnect(any()); - } - - @Test - public void testInventoryIsDispatchedAfterHello() throws Exception { - when(peer.getHelloMessageReceive()).thenReturn(mock(HelloMessage.class)); - when(peer.getPeerStatistics()).thenReturn(new PeerStatistics()); - InventoryMessage inventory = new InventoryMessage( - Collections.singletonList(Sha256Hash.ZERO_HASH), InventoryType.BLOCK); - - handler.onMessage(channel, inventory.getSendBytes()); - - verify(inventoryMsgHandler).processMessage(eq(peer), any(InventoryMessage.class)); - verify(peer, never()).disconnect(any()); - } - - @Test - public void testPbftIsDispatchedAfterHello() throws Exception { - when(peer.getHelloMessageReceive()).thenReturn(mock(HelloMessage.class)); - byte[] data = new byte[] {MessageTypes.PBFT_MSG.asByte()}; - PbftMessage message = mock(PbftMessage.class); - try (MockedStatic factory = Mockito.mockStatic(PbftMessageFactory.class)) { - factory.when(() -> PbftMessageFactory.create(data)).thenReturn(message); - handler.onMessage(channel, data); - verify(pbftMsgHandler).processMessage(peer, message); - verify(peer, never()).disconnect(any()); - } - } } diff --git a/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java b/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java deleted file mode 100644 index a0e05d2fa66..00000000000 --- a/framework/src/test/java/org/tron/core/net/message/handshake/HelloMessageTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.tron.core.net.message.handshake; - -import com.google.protobuf.ByteString; -import java.net.InetSocketAddress; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.tron.p2p.P2pConfig; -import org.tron.p2p.base.Constant; -import org.tron.p2p.base.Parameter; -import org.tron.protos.Discover.Endpoint; -import org.tron.protos.Protocol; - -public class HelloMessageTest { - - private P2pConfig savedConfig; - - @Before - public void setUp() { - savedConfig = Parameter.p2pConfig; - Parameter.p2pConfig = new P2pConfig(); - Parameter.p2pConfig.setIp("127.0.0.1"); - Parameter.p2pConfig.setIpv6("::1"); - } - - @After - public void tearDown() { - Parameter.p2pConfig = savedConfig; - } - - @Test(timeout = 5000) - public void testValidAddressLogFormat() throws Exception { - for (String[] hosts : new String[][] { - {"192.0.2.1", "", "192.0.2.1"}, - {"", "2001:db8::1", "2001:db8::1"}, - {"192.0.2.1", "2001:db8::1", "192.0.2.1"}}) { - HelloMessage message = hello(hosts[0], hosts[1], 18888); - Assert.assertTrue(message.valid()); - // InetSocketAddress adds IPv6 brackets on JDK 17, but not on JDK 8. - InetSocketAddress expectedEndpoint = new InetSocketAddress(hosts[2], 18888); - Assert.assertFalse(expectedEndpoint.isUnresolved()); - String formatted = message.toString(); - Assert.assertTrue(formatted, formatted.contains("from: " + expectedEndpoint + "\n")); - Assert.assertTrue(formatted.contains("timestamp: 123\n")); - Assert.assertTrue(formatted.contains("headBlockId: " - + message.getHeadBlockId().getString() + "\n")); - Assert.assertTrue(formatted.contains("nodeType: 0\nlowestBlockNum: 0\n")); - } - } - - private static HelloMessage hello(String ipv4, String ipv6, int port) throws Exception { - Protocol.HelloMessage.BlockId block = Protocol.HelloMessage.BlockId.newBuilder() - .setHash(ByteString.copyFrom(new byte[32])).build(); - Endpoint endpoint = Endpoint.newBuilder().setAddress(ByteString.copyFromUtf8(ipv4)) - .setAddressIpv6(ByteString.copyFromUtf8(ipv6)) - .setNodeId(ByteString.copyFrom(new byte[Constant.NODE_ID_LEN])).setPort(port).build(); - return new HelloMessage(Protocol.HelloMessage.newBuilder().setFrom(endpoint) - .setGenesisBlockId(block).setSolidBlockId(block).setHeadBlockId(block) - .setTimestamp(123).build().toByteArray()); - } -} diff --git a/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java b/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java index ac8462df604..f201a087f3f 100644 --- a/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java +++ b/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java @@ -3,7 +3,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -15,11 +14,9 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.tron.common.utils.ReflectUtils; -import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.config.Parameter.NetConstants; import org.tron.core.net.TronNetDelegate; import org.tron.core.net.message.handshake.HelloMessage; @@ -27,35 +24,14 @@ import org.tron.protos.Protocol.ReasonCode; public class PeerStatusCheckMockTest { - - private PeerStatusCheck peerStatusCheck; - private PeerConnection peer; - private Channel channel; - - @Before - public void setUp() { - peerStatusCheck = spy(new PeerStatusCheck()); - peer = spy(new PeerConnection()); - channel = mock(Channel.class); - ReflectUtils.setFieldValue(peer, "channel", channel); - doNothing().when(peer).disconnect(any()); - TronNetDelegate delegate = mock(TronNetDelegate.class); - when(delegate.getActivePeer()).thenReturn(Collections.singletonList(peer)); - ReflectUtils.setFieldValue(peerStatusCheck, "tronNetDelegate", delegate); - } - @After - public void tearDown() { - try { - peerStatusCheck.close(); - } finally { - Mockito.framework().clearInlineMocks(); - } + public void clearMocks() { + Mockito.framework().clearInlineMocks(); } @Test public void testInitException() { - peerStatusCheck.close(); + PeerStatusCheck peerStatusCheck = spy(new PeerStatusCheck()); ScheduledExecutorService executor = mock(ScheduledExecutorService.class); ReflectUtils.setFieldValue(peerStatusCheck, "peerStatusCheckExecutor", executor); doThrow(new RuntimeException("test exception")).when(peerStatusCheck).statusCheck(); @@ -76,51 +52,32 @@ public void testInitException() { } @Test - public void testHelloTimeoutWithoutSync() { - peer.setNeedSyncFromPeer(false); - peer.setNeedSyncFromUs(false); - when(channel.getStartTime()).thenReturn(System.currentTimeMillis()); - assertTimeout(false); - - when(channel.getStartTime()).thenReturn(System.currentTimeMillis() - 10_000); - assertTimeout(true); - } - - @Test - public void testHelloTimeoutDependsOnHandshakeCompletion() { - when(channel.getStartTime()).thenReturn(System.currentTimeMillis() - 60_000); - peer.setLastInteractiveTime(System.currentTimeMillis()); - peer.setBlockBothHave(new BlockId()); - // Recent traffic and block progress must not extend the HELLO deadline. - assertTimeout(true); - - peer.setHelloMessageReceive(mock(HelloMessage.class)); - peer.setNeedSyncFromPeer(false); - assertTimeout(false); - } - - @Test - public void testHelloCheckPreservesExistingTimeouts() { - when(channel.getStartTime()).thenReturn(System.currentTimeMillis()); - ReflectUtils.setFieldValue(peer, "blockBothHaveUpdateTime", 0L); - // A fresh connection must still be subject to the existing sync timeout. - assertTimeout(true); - - peer.setHelloMessageReceive(mock(HelloMessage.class)); - peer.setNeedSyncFromPeer(false); - peer.getSyncBlockRequested().put(new BlockId(), - System.currentTimeMillis() - NetConstants.SYNC_TIME_OUT - 1_000); - assertTimeout(true); - } + public void testHelloTimeout() { + PeerStatusCheck check = new PeerStatusCheck(); + PeerConnection peer = mock(PeerConnection.class); + Channel channel = mock(Channel.class); + TronNetDelegate delegate = mock(TronNetDelegate.class); + when(delegate.getActivePeer()).thenReturn(Collections.singletonList(peer)); + when(peer.getChannel()).thenReturn(channel); + when(peer.getAdvInvRequest()).thenReturn(Collections.emptyMap()); + when(peer.getSyncBlockRequested()).thenReturn(Collections.emptyMap()); + ReflectUtils.setFieldValue(check, "tronNetDelegate", delegate); + try { + long now = System.currentTimeMillis(); + when(channel.getStartTime()).thenReturn(now); + check.statusCheck(); + verify(peer, never()).disconnect(any()); - private void assertTimeout(boolean expected) { - clearInvocations(peer); - peerStatusCheck.statusCheck(); - if (expected) { + when(channel.getStartTime()).thenReturn(now - NetConstants.HELLO_TIME_OUT - 1); + check.statusCheck(); verify(peer).disconnect(ReasonCode.TIME_OUT); - } else { + + clearInvocations(peer); + when(peer.getHelloMessageReceive()).thenReturn(mock(HelloMessage.class)); + check.statusCheck(); verify(peer, never()).disconnect(any()); + } finally { + check.close(); } } - } diff --git a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java b/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java deleted file mode 100644 index 459f28e5c4c..00000000000 --- a/framework/src/test/java/org/tron/core/net/service/handshake/HandshakeHeadCheckTest.java +++ /dev/null @@ -1,335 +0,0 @@ -package org.tron.core.net.service.handshake; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.google.protobuf.ByteString; -import java.net.InetSocketAddress; -import java.util.HashMap; -import java.util.Map; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.tron.common.utils.ByteArray; -import org.tron.common.utils.ReflectUtils; -import org.tron.common.utils.Sha256Hash; -import org.tron.core.ChainBaseManager; -import org.tron.core.ChainBaseManager.NodeType; -import org.tron.core.capsule.BlockCapsule.BlockId; -import org.tron.core.net.TronNetService; -import org.tron.core.net.message.handshake.HelloMessage; -import org.tron.core.net.peer.PeerConnection; -import org.tron.core.net.peer.PeerManager; -import org.tron.core.net.service.effective.EffectiveCheckService; -import org.tron.core.net.service.relay.RelayService; -import org.tron.p2p.P2pConfig; -import org.tron.p2p.P2pService; -import org.tron.p2p.base.Parameter; -import org.tron.p2p.connection.Channel; -import org.tron.p2p.connection.ChannelManager; -import org.tron.p2p.connection.message.base.P2pDisconnectMessage; -import org.tron.p2p.discover.Node; -import org.tron.protos.Protocol; -import org.tron.protos.Protocol.ReasonCode; - -public class HandshakeHeadCheckTest { - - private HandshakeService service; - private ChainBaseManager chain; - private PeerConnection peer; - private Channel channel; - private P2pService p2p; - private EffectiveCheckService effectiveCheckService; - private MockedStatic netService; - private MockedStatic peerManager; - private BlockId genesis; - private BlockId solid; - private BlockId localHead; - - @Before - public void setUp() { - service = new HandshakeService(); - chain = mock(ChainBaseManager.class); - peer = mock(PeerConnection.class); - channel = mock(Channel.class); - effectiveCheckService = mock(EffectiveCheckService.class); - RelayService relayService = mock(RelayService.class); - ReflectUtils.setFieldValue(service, "chainBaseManager", chain); - ReflectUtils.setFieldValue(service, "relayService", relayService); - ReflectUtils.setFieldValue(service, "effectiveCheckService", effectiveCheckService); - when(peer.getChannel()).thenReturn(channel); - when(peer.getInetSocketAddress()).thenReturn(new InetSocketAddress("127.0.0.1", 18888)); - when(channel.getNodeId()).thenReturn(ByteArray.toHexString(new byte[64])); - when(relayService.checkHelloMessage(any(), any())).thenReturn(true); - - genesis = blockId(0, 1); - solid = blockId(80, 3); - localHead = blockId(100, 2); - when(chain.getGenesisBlockId()).thenReturn(genesis); - when(chain.getSolidBlockId()).thenReturn(solid); - when(chain.getHeadBlockId()).thenReturn(localHead); - when(chain.getHeadBlockNum()).thenReturn(100L); - when(chain.getLowestBlockNum()).thenReturn(10L); - when(chain.getNodeType()).thenReturn(NodeType.FULL); - when(chain.containBlockInMainChain(genesis)).thenReturn(true); - when(chain.containBlockInMainChain(solid)).thenReturn(true); - when(chain.containBlockInMainChain(localHead)).thenReturn(true); - - p2p = mock(P2pService.class); - netService = Mockito.mockStatic(TronNetService.class); - netService.when(TronNetService::getP2pService).thenReturn(p2p); - peerManager = Mockito.mockStatic(PeerManager.class); - } - - @After - public void tearDown() { - peerManager.close(); - netService.close(); - } - - @Test - public void testAcceptUnknownHeadAtLocalHeight() { - BlockId unknown = blockId(100, 4); - HelloMessage hello = hello(unknown); - Assert.assertTrue(hello.valid()); - Assert.assertEquals(chain.getSolidBlockId(), hello.getSolidBlockId()); - Assert.assertNotEquals(localHead, unknown); - Assert.assertFalse(chain.containBlockInMainChain(unknown)); - - service.processHelloMessage(peer, hello); - - assertAccepted(hello); - } - - @Test - public void testAcceptCachedForkHeadAtLocalHeight() { - BlockId forkHead = blockId(100, 12); - when(chain.containBlock(forkHead)).thenReturn(true); - Assert.assertTrue(chain.containBlock(forkHead)); - Assert.assertFalse(chain.containBlockInMainChain(forkHead)); - HelloMessage hello = hello(forkHead); - - service.processHelloMessage(peer, hello); - - assertAccepted(hello); - } - - @Test - public void testAcceptMainChainHeadAtLocalHeight() { - HelloMessage hello = hello(localHead); - - service.processHelloMessage(peer, hello); - - assertAccepted(hello); - } - - @Test - public void testAcceptOlderMainChainHead() { - BlockId older = blockId(90, 5); - when(chain.containBlockInMainChain(older)).thenReturn(true); - HelloMessage hello = hello(older); - - service.processHelloMessage(peer, hello); - - assertAccepted(hello); - } - - @Test - public void testAcceptForkHeadWhenSolidBlockMatches() { - BlockId forkHead = blockId(90, 6); - HelloMessage hello = hello(forkHead); - Assert.assertEquals(chain.getSolidBlockId(), hello.getSolidBlockId()); - // Keep the connection available so a short-lived fork can converge through sync/broadcast. - service.processHelloMessage(peer, hello); - - verify(chain).containBlockInMainChain(solid); - assertAccepted(hello); - } - - @Test - public void testAcceptOlderForkHeadBelowLocalSolid() { - when(chain.getSolidBlockId()).thenReturn(blockId(95, 13)); - HelloMessage hello = hello(blockId(90, 7)); - Assert.assertTrue(hello.getSolidBlockId().getNum() < hello.getHeadBlockId().getNum()); - Assert.assertTrue(hello.getHeadBlockId().getNum() < chain.getSolidBlockId().getNum()); - - service.processHelloMessage(peer, hello); - - verify(chain).containBlockInMainChain(solid); - assertAccepted(hello); - } - - @Test - public void testAcceptKnownHeadAtLowestRetainedHeight() { - when(chain.getLowestBlockNum()).thenReturn(solid.getNum()); - HelloMessage hello = hello(solid); - - service.processHelloMessage(peer, hello); - - verify(chain).containBlockInMainChain(solid); - assertAccepted(hello); - } - - @Test - public void testHigherHeadContinuesToSync() { - BlockId higher = blockId(101, 8); - HelloMessage hello = hello(higher); - - service.processHelloMessage(peer, hello); - - verify(chain, never()).containBlockInMainChain(higher); - assertAccepted(hello); - } - - @Test - public void testUnverifiableSolidBelowRetainedHistoryIsRejected() { - BlockId prunedSolid = blockId(9, 9); - HelloMessage hello = hello(blockId(90, 14)); - hello.setHelloMessage(hello.getInstance().toBuilder() - .setSolidBlockId(protoBlockId(prunedSolid)).build()); - - service.processHelloMessage(peer, hello); - - verify(chain).containBlockInMainChain(prunedSolid); - assertRejected(ReasonCode.LIGHT_NODE_SYNC_FAIL); - } - - @Test - public void testGenesisMismatchIsStillRejected() { - HelloMessage hello = hello(localHead); - hello.setHelloMessage(hello.getInstance().toBuilder() - .setGenesisBlockId(protoBlockId(blockId(0, 10))).build()); - - service.processHelloMessage(peer, hello); - - assertRejected(ReasonCode.INCOMPATIBLE_CHAIN); - } - - @Test - public void testVersionMismatchIsStillRejected() { - HelloMessage hello = hello(localHead); - hello.setHelloMessage(hello.getInstance().toBuilder() - .setVersion(hello.getVersion() + 1).build()); - - service.processHelloMessage(peer, hello); - - assertRejected(ReasonCode.INCOMPATIBLE_VERSION); - } - - @Test - public void testEffectiveCheckStillRejectsOlderKnownHead() { - BlockId older = blockId(90, 5); - when(chain.containBlockInMainChain(older)).thenReturn(true); - InetSocketAddress address = peer.getInetSocketAddress(); - when(effectiveCheckService.getCur()).thenReturn(address); - - service.processHelloMessage(peer, hello(older)); - - assertRejected(ReasonCode.BELOW_THAN_ME); - } - - @Test - public void testSolidForkIsStillRejected() { - BlockId differentSolid = blockId(80, 11); - HelloMessage hello = hello(localHead); - hello.setHelloMessage(hello.getInstance().toBuilder() - .setSolidBlockId(protoBlockId(differentSolid)).build()); - - service.processHelloMessage(peer, hello); - - verify(chain).containBlockInMainChain(differentSolid); - assertRejected(ReasonCode.FORKED); - } - - @Test - public void testDuplicateHelloIsStillRejected() { - HelloMessage hello = hello(localHead); - when(peer.getHelloMessageReceive()).thenReturn(hello); - - service.processHelloMessage(peer, hello); - - assertRejected(ReasonCode.BAD_PROTOCOL); - } - - @Test - public void testInvalidHelloDoesNotCloseDuplicateConnection() { - HelloMessage hello = Mockito.spy(hello(localHead)); - Assert.assertTrue(hello.valid()); - String nodeId = ByteArray.toHexString(hello.getInstance().getFrom().getNodeId().toByteArray()); - hello.setHelloMessage(hello.getInstance().toBuilder() - .setHeadBlockId(hello.getInstance().getHeadBlockId().toBuilder() - .setHash(ByteString.copyFrom(new byte[31]))) - .build()); - Assert.assertFalse(hello.valid()); - - Channel other = mock(Channel.class); - InetSocketAddress address = peer.getInetSocketAddress(); - InetSocketAddress otherAddress = new InetSocketAddress("127.0.0.2", 18888); - when(channel.getInetSocketAddress()).thenReturn(address); - when(channel.getNodeId()).thenReturn(nodeId); - when(channel.getStartTime()).thenReturn(100L); - when(other.getInetSocketAddress()).thenReturn(otherAddress); - when(other.getNodeId()).thenReturn(nodeId); - when(other.getStartTime()).thenReturn(200L); - Map channels = ChannelManager.getChannels(); - Map savedChannels = new HashMap<>(channels); - P2pConfig savedConfig = Parameter.p2pConfig; - try { - Parameter.p2pConfig = new P2pConfig(); - channels.clear(); - channels.put(channel.getInetSocketAddress(), channel); - channels.put(otherAddress, other); - - service.processHelloMessage(peer, hello); - - assertRejected(ReasonCode.INCOMPATIBLE_PROTOCOL); - verify(p2p, never()).updateNodeId(any(), any()); - verify(channel, never()).setNodeId(any()); - verify(other, never()).send(any(P2pDisconnectMessage.class)); - verify(other, never()).close(); - - } finally { - channels.clear(); - channels.putAll(savedChannels); - Parameter.p2pConfig = savedConfig; - } - } - - private HelloMessage hello(BlockId head) { - HelloMessage hello = new HelloMessage( - new Node(new byte[64], "127.0.0.1", null, 18888), 0, chain); - hello.setHelloMessage(hello.getInstance().toBuilder() - .setSolidBlockId(protoBlockId(solid)) - .setHeadBlockId(protoBlockId(head)).build()); - return hello; - } - - private static BlockId blockId(long number, int seed) { - return new BlockId(Sha256Hash.of(new byte[] {(byte) seed}), number); - } - - private static Protocol.HelloMessage.BlockId protoBlockId(BlockId id) { - return Protocol.HelloMessage.BlockId.newBuilder() - .setNumber(id.getNum()).setHash(id.getByteString()).build(); - } - - private void assertAccepted(HelloMessage hello) { - verify(peer).setHelloMessageReceive(hello); - verify(peer).onConnect(); - verify(peer, never()).disconnect(any()); - peerManager.verify(PeerManager::sortPeers); - } - - private void assertRejected(ReasonCode reason) { - verify(peer).disconnect(reason); - verify(peer, never()).setHelloMessageReceive(any()); - verify(peer, never()).onConnect(); - peerManager.verifyNoInteractions(); - } -} diff --git a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java index 9f7a1d7d834..13407b446c2 100644 --- a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java @@ -1,10 +1,6 @@ package org.tron.core.net.services; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import static org.tron.core.net.message.handshake.HelloMessage.getEndpointFromNode; import com.google.protobuf.ByteString; @@ -12,10 +8,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; -import java.util.Arrays; import java.util.List; import java.util.Random; -import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; @@ -333,18 +327,14 @@ public void testProcessHelloMessage() { @Test public void testMismatchedHelloNodeIdPreservesEstablishedIdentity() throws Exception { - String establishedNodeId = StringUtils.repeat("01", 64); - String helloNodeId = StringUtils.repeat("02", 64); Channel channel = new Channel(); - channel.setNodeId(establishedNodeId); + channel.setNodeId("010203"); Channel otherChannel = new Channel(); - otherChannel.setNodeId(helloNodeId); + otherChannel.setNodeId("020304"); PeerConnection connection = mock(PeerConnection.class); Mockito.when(connection.getChannel()).thenReturn(channel); HelloMessage message = mock(HelloMessage.class); - byte[] helloNodeIdBytes = new byte[64]; - Arrays.fill(helloNodeIdBytes, (byte) 2); - Mockito.when(message.getFrom()).thenReturn(new Node(helloNodeIdBytes, null, null, 0)); + Mockito.when(message.getFrom()).thenReturn(new Node(new byte[]{2, 3, 4}, null, null, 0)); Field channelsField = ChannelManager.class.getDeclaredField("channels"); channelsField.setAccessible(true); @@ -356,8 +346,9 @@ public void testMismatchedHelloNodeIdPreservesEstablishedIdentity() throws Excep Mockito.verify(connection).disconnect(Protocol.ReasonCode.UNEXPECTED_IDENTITY); Mockito.verify(connection, Mockito.never()).setHelloMessageReceive(Mockito.any()); - Assert.assertEquals(establishedNodeId, channel.getNodeId()); - Assert.assertEquals(helloNodeId, otherChannel.getNodeId()); + Mockito.verify(message, Mockito.never()).valid(); + Assert.assertEquals("010203", channel.getNodeId()); + Assert.assertEquals("020304", otherChannel.getNodeId()); Assert.assertFalse(otherChannel.isDisconnect()); } finally { channels.removeIf(registered -> registered == channel || registered == otherChannel); From 992d57f8ac37ba2aa453d2a983c4b02cb90b2799 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Fri, 16 Oct 2026 16:30:27 +0800 Subject: [PATCH 11/11] fix(net): remove redundant empty-frame handling Rely on ChannelManager.processMessage to reject null and empty frames before application dispatch. Remove the direct-callback test that bypasses this upstream entry point while retaining the HELLO-state gate. --- .../main/java/org/tron/core/net/P2pEventHandlerImpl.java | 5 ----- .../src/test/java/org/tron/core/net/P2pHelloGateTest.java | 7 ------- 2 files changed, 12 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java b/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java index 075332b8f46..67751230367 100644 --- a/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java +++ b/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java @@ -127,11 +127,6 @@ public void onMessage(Channel c, byte[] data) { return; } - if (data == null || data.length == 0) { - peerConnection.disconnect(Protocol.ReasonCode.BAD_PROTOCOL); - return; - } - if (peerConnection.getHelloMessageReceive() == null && data[0] != MessageTypes.P2P_HELLO.asByte() && data[0] != MessageTypes.P2P_DISCONNECT.asByte()) { diff --git a/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java b/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java index 55a84e10001..02c5e7747d2 100644 --- a/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java +++ b/framework/src/test/java/org/tron/core/net/P2pHelloGateTest.java @@ -70,13 +70,6 @@ public void testRejectMessagesBeforeHelloWithoutParsing() { } } - @Test - public void testRejectEmptyFrames() { - handler.onMessage(channel, new byte[0]); - handler.onMessage(channel, null); - verify(peer, times(2)).disconnect(ReasonCode.BAD_PROTOCOL); - } - @Test public void testHelloIsDispatchedBeforeHandshake() throws Exception { ChainBaseManager chain = mock(ChainBaseManager.class);