chore(p2p): internalize libp2p v2.2.9 as a local p2p module - #15
barbatos2011 wants to merge 12 commits into
Conversation
There was a problem hiding this comment.
40 issues found across 167 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="p2p/src/main/java/org/tron/p2p/discover/message/kad/NeighborsMessage.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/discover/message/kad/NeighborsMessage.java:73">
P1: A neighbour with an out-of-range port passes `valid()` and crashes discovery processing when `getNodeHandler` constructs its socket address. Reject ports outside `1..65535` here before accepting the datagram.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/connection/ChannelManager.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/connection/ChannelManager.java:66">
P2: After one service closes, a later `ChannelManager.init()` leaves `isShutdown` true, so all subsequent outbound connections and reconnection tasks are rejected. Reset the shutdown state when initializing a new manager.</violation>
<violation number="2" location="p2p/src/main/java/org/tron/p2p/connection/ChannelManager.java:258">
P1: When an inbound peer sends a registered application message before Hello, this branch admits it and marks the handshake complete without network/version validation. Require `finishHandshake` before dispatching application data instead of setting it here.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/discover/Node.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/discover/Node.java:135">
P2: Arbitrary binary node IDs are converted through the default charset before equality checks, so distinct peers can occasionally compare equal. Compare the byte arrays directly or use a lossless representation such as the existing hexadecimal encoding.</violation>
<violation number="2" location="p2p/src/main/java/org/tron/p2p/discover/Node.java:155">
P1: Nodes with the same ID but different addresses compare equal yet hash differently, so the `HashSet`-based node aggregation can retain duplicates and hash-based lookups can miss an equal node. Hash the same identity fields used by `equals()` instead of `format()`.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/sync/Client.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/sync/Client.java:87">
P1: When a refresh fails after partially resolving a changed root, the next refresh treats that root as unchanged and discards entries fetched before the failure. Keep the previous `ClientTree` state until the complete snapshot succeeds, or rebuild the sync state from the new root after a failure.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/update/PublishService.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/update/PublishService.java:44">
P2: When `staticNodes` is configured, this branch performs one deployment and never schedules another. A transient first failure leaves DNS unpublished forever because `startPublish` catches the error; schedule retries after the initial publish.</violation>
<violation number="2" location="p2p/src/main/java/org/tron/p2p/dns/update/PublishService.java:120">
P1: When a caller enables publishing without `dnsPrivate`, `checkConfig` still passes and this line builds an unsigned tree. The provider can receive an unsigned root that DNS clients reject; require a nonempty private key before accepting the publish configuration.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/update/AliClient.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/update/AliClient.java:199">
P1: When a DNS deletion returns a non-200 response, `submitChanges` still logs success and `deploy` completes because this boolean is discarded. Check the return value and throw before incrementing `deleteCount`.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/tree/Algorithm.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/tree/Algorithm.java:30">
P1: When the public-key integer starts with a zero nibble, `toString(16)` shortens the 128-digit coordinate pair before this substring, so the compressed key contains part of Y instead of the full X and DNS consumers reject the tree. Left-pad the public-key representation to 128 hex digits before extracting X.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/update/AwsClient.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/update/AwsClient.java:127">
P2: When the configured domain has a trailing dot, `collectRecords` and `tree.toTXT` use different key forms, so every existing record appears missing and Route53 rejects the CREATEs. Normalize the domain before collecting and rendering records.</violation>
<violation number="2" location="p2p/src/main/java/org/tron/p2p/dns/update/AwsClient.java:330">
P1: When an existing TXT value differs only by letter case, this comparison suppresses the UPSERT. Route53 TXT RDATA is case-sensitive, so compare the joined values exactly.</violation>
<violation number="3" location="p2p/src/main/java/org/tron/p2p/dns/update/AwsClient.java:498">
P2: When Route53 returns a mixed-case name, `isSubdomain` rejects an otherwise valid record and deployment can issue CREATEs for records that already exist. Use a case-insensitive suffix comparison for DNS names.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/connection/socket/PeerServer.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/connection/socket/PeerServer.java:29">
P1: When shutdown begins before the asynchronous bind completes, `close()` returns because `listening` is still false, and `start()` subsequently binds a server that shutdown no longer closes. Synchronize startup and shutdown, or record shutdown before binding and close the channel as soon as it becomes available.</violation>
<violation number="2" location="p2p/src/main/java/org/tron/p2p/connection/socket/PeerServer.java:71">
P2: When the configured TCP port cannot be bound, this handler only logs the error and leaves the p2p service running without a listener. Propagate the startup failure or expose it to the service so a node cannot report successful startup while accepting no TCP peers.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/discover/protocol/kad/table/DistanceComparator.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/discover/protocol/kad/table/DistanceComparator.java:15">
P1: When more than 16 peers share a leading XOR bucket, this comparator returns 0 for peers with different XOR distances, so `getClosestNodes` can omit closer peers and return arbitrary farther peers. Compare the full XOR distance after the bucket metric, or use the full XOR value directly.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/discover/protocol/kad/table/TimeComparator.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/discover/protocol/kad/table/TimeComparator.java:12">
P1: When a bucket is full, this ordering makes `NodeBucket.getLastSeen()` select the newest entry instead of the least-recently-seen node. Reverse the comparator ordering or select the final sorted entry so stale peers are challenged rather than the most recently seen peer.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/discover/protocol/kad/NodeHandler.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/discover/protocol/kad/NodeHandler.java:110">
P2: When an active peer reports a different network ID, this path marks the handler dead but leaves the peer in `NodeTable`, so discovery keeps querying an incompatible node. Drop the node from the table before transitioning it to `DEAD`.</violation>
<violation number="2" location="p2p/src/main/java/org/tron/p2p/discover/protocol/kad/NodeHandler.java:113">
P1: When a peer has no address in the local IP family, the constructor leaves `state` null, so a matching `handlePing` crashes on `state.equals` and can close the UDP channel. Treat the null state as discovery (or compare the enum null-safely) before restarting the ping.</violation>
<violation number="3" location="p2p/src/main/java/org/tron/p2p/discover/protocol/kad/NodeHandler.java:125">
P2: After a handler recovers from a timeout, `pingTrials` remains exhausted, so the next bucket challenge can evict a healthy node after one lost ping. Reset the retry counter after every successful pong.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/discover/socket/DiscoverServer.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/discover/socket/DiscoverServer.java:72">
P1: When `close()` races with the initial bind, it can observe `channel == null` and return before this assignment. The start thread then waits forever on the newly bound channel's `closeFuture`, leaving discovery listening after `NodeManager.close()`; coordinate bind and close, safely publish the channel, and close a channel bound after shutdown.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/sync/SubtreeSync.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/sync/SubtreeSync.java:57">
P1: When DNS lookup fails, `resolveEntry` returns `null`, but this method passes it through and callers discard the hash. Full sync can commit an incomplete tree, and random discovery skips the entry until the root changes; reject `null` with `NO_ENTRY_FOUND` before removing it.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/tree/RootEntry.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/tree/RootEntry.java:67">
P2: When a DNS root value is shorter than `rootPrefix` or lacks that prefix, `parseEntry` throws `StringIndexOutOfBoundsException` instead of `DnsException`. `AwsClient.computeChanges` catches only `DnsException` around root parsing, so one malformed existing or new root aborts the publish; validate the prefix before calling `substring`.</violation>
<violation number="2" location="p2p/src/main/java/org/tron/p2p/dns/tree/RootEntry.java:70">
P2: When a DNS root payload or its signature is not valid URL-safe base64, `Algorithm.decode64` throws unchecked `IllegalArgumentException` outside `parseEntry`'s declared `DnsException` contract. A single corrupt root record therefore aborts `AwsClient`/`AliClient` record collection; catch decoder failures and convert them to `DnsException` like other invalid roots.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/tree/NodesEntry.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/tree/NodesEntry.java:29">
P2: When a DNS provider returns a malformed Base64 `nodes:` value, `Algorithm.decode64` throws `IllegalArgumentException` before this catch can create `DnsException`. Catch `IllegalArgumentException` here so one corrupt TXT record is skipped by the existing `DnsException` handlers.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/sync/RandomIterator.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/sync/RandomIterator.java:90">
P2: When no DNS tree URLs are configured, `pickTree()` calls `random.nextInt(0)` and `DnsManager.getRandomNodes()` throws instead of returning no node. Return `null` before choosing a random index when `size == 0`.</violation>
</file>
<file name="p2p/src/main/java/org/web3j/utils/Numeric.java">
<violation number="1" location="p2p/src/main/java/org/web3j/utils/Numeric.java:211">
P2: When `Hash.sha3(String)` receives malformed hex such as `0xzz`, `hexStringToByteArray` silently hashes substituted bytes instead of rejecting the input. Validate both nibbles, including the odd-length first nibble, and throw a decoding exception for invalid characters.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/DnsNode.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/DnsNode.java:29">
P2: Every `DnsNode` discards the supplied node ID before serialization. `PublishService` passes real IDs, but `compress` therefore emits endpoints without `nodeId`, losing peer identity across DNS publication; pass `id` to the superclass.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/example/StartApp.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/example/StartApp.java:210">
P2: When `--publish --change-threshold 0` or a negative value is supplied, this condition stores the invalid threshold and the DNS clients publish every non-empty change. Validate that the value is finite and strictly between zero and one before storing it.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/base/Parameter.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/base/Parameter.java:42">
P2: After `P2pService.start()` launches the channel threads, `TronNetService.start()` registers the application handler, so this mutation can race with callbacks. Use a thread-safe registry and make duplicate checking plus insertion atomic; otherwise callbacks can throw `ConcurrentModificationException`, lose handlers, or allow duplicate message types.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/connection/message/handshake/HelloMessage.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/connection/message/handshake/HelloMessage.java:62">
P2: A malformed peer endpoint with port `-1` or `0` passes handshake validation because `NetUtil.validNode` does not check ports. Reject ports outside `1..65535` here before storing the peer node.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/connection/business/handshake/HandshakeService.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/connection/business/handshake/HandshakeService.java:70">
P2: When an inbound peer sends a non-`NORMAL` hello code with the correct network ID, this branch accepts it and fires `onConnect`. Reject non-normal codes before the network-ID check, as the active-channel branch already does.</violation>
</file>
<file name="p2p/build.gradle">
<violation number="1" location="p2p/build.gradle:92">
P2: Consumers compiling directly against `:p2p` cannot resolve the Netty types in its public API because all Netty dependencies are implementation-only. Declare the Netty modules required by public signatures as `api` dependencies, or hide those Netty types behind the p2p API.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/connection/business/pool/ConnPoolService.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/connection/business/pool/ConnPoolService.java:236">
P2: When discovery returns multiple endpoints on one IP, the pool only deduplicates exact socket addresses before selecting the batch. Track selected and in-flight `InetAddress` values as well, otherwise the pool dials beyond `maxConnectionsWithSameIp` and relies on handshake rejection to clean up the excess.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/discover/protocol/kad/KadService.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/discover/protocol/kad/KadService.java:136">
P2: When a valid discovery packet arrives from an address family not configured locally, `getNodeHandler` returns a handler with null state and `handlePing` throws a `NullPointerException`. Ignore nodes without a preferred local-stack address before dispatching the event so one packet does not close and restart the discovery UDP channel.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/sync/LinkCache.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/sync/LinkCache.java:77">
P2: When a removed link leads to a downstream cycle, the cycle remains in `backrefs` because `stk` enqueues a node only after its parent set becomes empty. Traverse reachability from configured roots so `RandomIterator.rebuildTrees` drops stale client trees.</violation>
</file>
<file name="p2p/src/main/java/org/web3j/crypto/Sign.java">
<violation number="1" location="p2p/src/main/java/org/web3j/crypto/Sign.java:50">
P2: On a JVM whose default charset is not UTF-8, `getEthereumMessageHash` encodes the EIP-191 prefix differently from other Ethereum implementations, so prefixed signatures are not interoperable. Encode this prefix with UTF-8 explicitly.</violation>
<violation number="2" location="p2p/src/main/java/org/web3j/crypto/Sign.java:127">
P2: When an input signature has `s=0` or `s=n`, this nonnegative-only check accepts an invalid ECDSA component and recovery can return a computed public key for it. Require `1 <= r,s < CURVE.getN()` before recovering the key.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/utils/NetUtil.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/utils/NetUtil.java:120">
P2: When an IP-echo service returns a resolvable hostname such as `localhost`, `getExternalIp` validates the resolved address but returns the hostname. Validate `ip` itself as the requested literal family, or return the canonical address, before storing it in `P2pConfig`.</violation>
</file>
<file name="p2p/src/main/java/org/tron/p2p/dns/lookup/LookUpTxt.java">
<violation number="1" location="p2p/src/main/java/org/tron/p2p/dns/lookup/LookUpTxt.java:139">
P2: When concurrent or previously timed-out OS lookups fill this bounded executor, `submit` throws `RejectedExecutionException` and `lookUpIp` fails instead of falling back to public DNS. Treat executor saturation as an OS-resolution miss and continue with the fallback path.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| return false; | ||
| } | ||
| for (Node node : getNodes()) { | ||
| if (!NetUtil.validNode(node)) { |
There was a problem hiding this comment.
P1: A neighbour with an out-of-range port passes valid() and crashes discovery processing when getNodeHandler constructs its socket address. Reject ports outside 1..65535 here before accepting the datagram.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At p2p/src/main/java/org/tron/p2p/discover/message/kad/NeighborsMessage.java, line 73:
<comment>A neighbour with an out-of-range port passes `valid()` and crashes discovery processing when `getNodeHandler` constructs its socket address. Reject ports outside `1..65535` here before accepting the datagram.</comment>
<file context>
@@ -0,0 +1,80 @@
+ return false;
+ }
+ for (Node node : getNodes()) {
+ if (!NetUtil.validNode(node)) {
+ return false;
+ }
</file context>
| if (!NetUtil.validNode(node)) { | |
| if (!NetUtil.validNode(node) || node.getPort() <= 0 || node.getPort() > 65535) { |
| return; | ||
| } | ||
|
|
||
| if (!channel.isFinishHandshake()) { |
There was a problem hiding this comment.
P1: When an inbound peer sends a registered application message before Hello, this branch admits it and marks the handshake complete without network/version validation. Require finishHandshake before dispatching application data instead of setting it here.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At p2p/src/main/java/org/tron/p2p/connection/ChannelManager.java, line 258:
<comment>When an inbound peer sends a registered application message before Hello, this branch admits it and marks the handshake complete without network/version validation. Require `finishHandshake` before dispatching application data instead of setting it here.</comment>
<file context>
@@ -0,0 +1,307 @@
+ return;
+ }
+
+ if (!channel.isFinishHandshake()) {
+ channel.setFinishHandshake(true);
+ DisconnectCode code = processPeer(channel);
</file context>
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return this.format().hashCode(); |
There was a problem hiding this comment.
P1: Nodes with the same ID but different addresses compare equal yet hash differently, so the HashSet-based node aggregation can retain duplicates and hash-based lookups can miss an equal node. Hash the same identity fields used by equals() instead of format().
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At p2p/src/main/java/org/tron/p2p/discover/Node.java, line 155:
<comment>Nodes with the same ID but different addresses compare equal yet hash differently, so the `HashSet`-based node aggregation can retain duplicates and hash-based lookups can miss an equal node. Hash the same identity fields used by `equals()` instead of `format()`.</comment>
<file context>
@@ -0,0 +1,197 @@
+
+ @Override
+ public int hashCode() {
+ return this.format().hashCode();
+ }
+
</file context>
| clientTree.syncAll(tree.getEntries()); | ||
| } else { | ||
| Map<String, Entry> tmpEntries = new HashMap<>(); | ||
| boolean[] isRootUpdate = clientTree.syncAll(tmpEntries); |
There was a problem hiding this comment.
P1: When a refresh fails after partially resolving a changed root, the next refresh treats that root as unchanged and discards entries fetched before the failure. Keep the previous ClientTree state until the complete snapshot succeeds, or rebuild the sync state from the new root after a failure.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At p2p/src/main/java/org/tron/p2p/dns/sync/Client.java, line 87:
<comment>When a refresh fails after partially resolving a changed root, the next refresh treats that root as unchanged and discards entries fetched before the failure. Keep the previous `ClientTree` state until the complete snapshot succeeds, or rebuild the sync state from the new root after a failure.</comment>
<file context>
@@ -0,0 +1,188 @@
+ clientTree.syncAll(tree.getEntries());
+ } else {
+ Map<String, Entry> tmpEntries = new HashMap<>();
+ boolean[] isRootUpdate = clientTree.syncAll(tmpEntries);
+ if (!isRootUpdate[0]) {
+ tmpEntries.putAll(tree.getLinksMap());
</file context>
| "The dns server type must be specified when enabling the dns publishing service"); | ||
| return false; | ||
| } | ||
| if (StringUtils.isEmpty(config.getDnsDomain())) { |
There was a problem hiding this comment.
P1: When a caller enables publishing without dnsPrivate, checkConfig still passes and this line builds an unsigned tree. The provider can receive an unsigned root that DNS clients reject; require a nonempty private key before accepting the publish configuration.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At p2p/src/main/java/org/tron/p2p/dns/update/PublishService.java, line 120:
<comment>When a caller enables publishing without `dnsPrivate`, `checkConfig` still passes and this line builds an unsigned tree. The provider can receive an unsigned root that DNS clients reject; require a nonempty private key before accepting the publish configuration.</comment>
<file context>
@@ -0,0 +1,146 @@
+ "The dns server type must be specified when enabling the dns publishing service");
+ return false;
+ }
+ if (StringUtils.isEmpty(config.getDnsDomain())) {
+ logger.error("The dns domain must be specified when enabling the dns publishing service");
+ return false;
</file context>
| if (StringUtils.isEmpty(config.getDnsDomain())) { | |
| if (StringUtils.isEmpty(config.getDnsPrivate())) { | |
| logger.error( | |
| "The dns private key must be specified when enabling the dns publishing service"); | |
| return false; | |
| } | |
| if (StringUtils.isEmpty(config.getDnsDomain())) { |
| } | ||
|
|
||
| if (publishConfig.getStaticNodes() != null && !publishConfig.getStaticNodes().isEmpty()) { | ||
| startPublish(); |
There was a problem hiding this comment.
P2: When staticNodes is configured, this branch performs one deployment and never schedules another. A transient first failure leaves DNS unpublished forever because startPublish catches the error; schedule retries after the initial publish.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At p2p/src/main/java/org/tron/p2p/dns/update/PublishService.java, line 44:
<comment>When `staticNodes` is configured, this branch performs one deployment and never schedules another. A transient first failure leaves DNS unpublished forever because `startPublish` catches the error; schedule retries after the initial publish.</comment>
<file context>
@@ -0,0 +1,146 @@
+ }
+
+ if (publishConfig.getStaticNodes() != null && !publishConfig.getStaticNodes().isEmpty()) {
+ startPublish();
+ } else {
+ publisher.scheduleWithFixedDelay(this::startPublish, 300, publishDelay, TimeUnit.SECONDS);
</file context>
| startPublish(); | |
| startPublish(); | |
| publisher.scheduleWithFixedDelay(this::startPublish, publishDelay, publishDelay, | |
| TimeUnit.SECONDS); |
| if (ip == null || ip.trim().isEmpty()) { | ||
| throw new IOException("Invalid address: " + ip); | ||
| } | ||
| InetAddress inetAddress = InetAddress.getByName(ip); |
There was a problem hiding this comment.
P2: When an IP-echo service returns a resolvable hostname such as localhost, getExternalIp validates the resolved address but returns the hostname. Validate ip itself as the requested literal family, or return the canonical address, before storing it in P2pConfig.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At p2p/src/main/java/org/tron/p2p/utils/NetUtil.java, line 120:
<comment>When an IP-echo service returns a resolvable hostname such as `localhost`, `getExternalIp` validates the resolved address but returns the hostname. Validate `ip` itself as the requested literal family, or return the canonical address, before storing it in `P2pConfig`.</comment>
<file context>
@@ -0,0 +1,294 @@
+ if (ip == null || ip.trim().isEmpty()) {
+ throw new IOException("Invalid address: " + ip);
+ }
+ InetAddress inetAddress = InetAddress.getByName(ip);
+ if (isAskIpv4 && !validIpV4(inetAddress.getHostAddress())) {
+ throw new IOException("Invalid address: " + ip);
</file context>
| logger.debug("LookUp {} for domain: {}", useIPv4 ? "IPv4" : "IPv6", domain); | ||
|
|
||
| // Step 1: OS name resolver — honours /etc/hosts, so LAN mappings work without a DNS query. | ||
| Future<InetAddress[]> future = OS_RESOLVER_EXECUTOR.submit( |
There was a problem hiding this comment.
P2: When concurrent or previously timed-out OS lookups fill this bounded executor, submit throws RejectedExecutionException and lookUpIp fails instead of falling back to public DNS. Treat executor saturation as an OS-resolution miss and continue with the fallback path.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At p2p/src/main/java/org/tron/p2p/dns/lookup/LookUpTxt.java, line 139:
<comment>When concurrent or previously timed-out OS lookups fill this bounded executor, `submit` throws `RejectedExecutionException` and `lookUpIp` fails instead of falling back to public DNS. Treat executor saturation as an OS-resolution miss and continue with the fallback path.</comment>
<file context>
@@ -0,0 +1,204 @@
+ logger.debug("LookUp {} for domain: {}", useIPv4 ? "IPv4" : "IPv6", domain);
+
+ // Step 1: OS name resolver — honours /etc/hosts, so LAN mappings work without a DNS query.
+ Future<InetAddress[]> future = OS_RESOLVER_EXECUTOR.submit(
+ () -> InetAddress.getAllByName(domain));
+ try {
</file context>
| // uploads the given tree to Route53. | ||
| @Override | ||
| public void deploy(String domain, Tree tree) throws Exception { | ||
| checkZone(domain); |
There was a problem hiding this comment.
P2: When the configured domain has a trailing dot, collectRecords and tree.toTXT use different key forms, so every existing record appears missing and Route53 rejects the CREATEs. Normalize the domain before collecting and rendering records.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At p2p/src/main/java/org/tron/p2p/dns/update/AwsClient.java, line 127:
<comment>When the configured domain has a trailing dot, `collectRecords` and `tree.toTXT` use different key forms, so every existing record appears missing and Route53 rejects the CREATEs. Normalize the domain before collecting and rendering records.</comment>
<file context>
@@ -0,0 +1,511 @@
+ // uploads the given tree to Route53.
+ @Override
+ public void deploy(String domain, Tree tree) throws Exception {
+ checkZone(domain);
+
+ Map<String, RecordSet> existing = collectRecords(domain);
</file context>
| public static boolean isSubdomain(String sub, String root) { | ||
| String subNoSuffix = postfix + StringUtils.strip(sub, postfix); | ||
| String rootNoSuffix = postfix + StringUtils.strip(root, postfix); | ||
| return subNoSuffix.endsWith(rootNoSuffix); |
There was a problem hiding this comment.
P2: When Route53 returns a mixed-case name, isSubdomain rejects an otherwise valid record and deployment can issue CREATEs for records that already exist. Use a case-insensitive suffix comparison for DNS names.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At p2p/src/main/java/org/tron/p2p/dns/update/AwsClient.java, line 498:
<comment>When Route53 returns a mixed-case name, `isSubdomain` rejects an otherwise valid record and deployment can issue CREATEs for records that already exist. Use a case-insensitive suffix comparison for DNS names.</comment>
<file context>
@@ -0,0 +1,511 @@
+ public static boolean isSubdomain(String sub, String root) {
+ String subNoSuffix = postfix + StringUtils.strip(sub, postfix);
+ String rootNoSuffix = postfix + StringUtils.strip(root, postfix);
+ return subNoSuffix.endsWith(rootNoSuffix);
+ }
+
</file context>
| return subNoSuffix.endsWith(rootNoSuffix); | |
| return StringUtils.endsWithIgnoreCase(subNoSuffix, rootNoSuffix); |
9302d22 to
2e50f85
Compare
Vendors tronprotocol/libp2p tag v2.2.9 (c564f263d310d7a964035d3b597634aba6bda86d) into a local `p2p` Gradle module, ahead of switching `common` off the external io.github.tronprotocol:libp2p Maven artifact. The source in this commit is byte-identical to `git archive v2.2.9 src/main`, so a reviewer can diff it directly against the upstream tag and confirm nothing was altered on the way in. Everything we change about it lands in the next commit, separately and for exactly that reason. Two arrangements differ from the upstream layout: - The example code moves out of src/main into its own `example` sourceSet. It still compiles, so an API change in main surfaces here too, but it is not packaged into p2p.jar and is not run as tests. - Generated protobuf sources are gitignored and rebuilt by :p2p:generateProto, so they stay out of the diff. p2p tracks rootProject.grpcVersion rather than pinning libp2p's own gRPC version, so the module cannot drift from the Netty the rest of the build resolves.
Three mechanical rewrites plus formatting, applied on top of the pristine v2.2.9 source added in the previous commit. Kept separate so commit 1 stays diffable against the upstream tag. - log. -> logger. (158 call sites). The root lombok.config sets lombok.log.fieldName=logger, so @slf4j generates `logger`, not `log`. - Math. -> StrictMath. (6 sites). CI enforces a check-math rule that rejects java.lang.Math anywhere in the tree, to keep arithmetic deterministic across JVMs and architectures. - toLowerCase()/toUpperCase() -> Locale.ROOT (4 sites). The root build enables errorprone StringCaseLocaleUsage as ERROR on every subproject except protocol and errorprone, so this is compile-forced. It is the one change here that is not purely cosmetic: behaviour is identical for ASCII but differs under a Turkish locale. Matches the project's own idiom in Args.java:1273. BasicThreadFactory is deliberately left as upstream writes it -- twelve `builder()` calls and the one `new BasicThreadFactory.Builder()` in DiscoverServer. The root build provides commons-lang3 3.20.0, where `builder()` exists and the no-arg `Builder()` constructor is @deprecated, so rewriting either way would add deviation from the tag and, in one direction, thirteen deprecation warnings. Formatting: google-java-format over the 9 vendored org/web3j/** files, which came in AOSP 4-space style, plus import reordering and hand fixes for the remainder. This takes :p2p:checkstyleMain from 605 violations to 0. No functional change beyond the Locale.ROOT note above.
Replaces io.github.tronprotocol:libp2p:2.2.9 with `api project(":p2p")`,
collapsing 17 lines of dependency plus excludes into one.
The dom4j exclusion tail (jaxen, stax-api, msv, xsdlib, relaxngDatatype,
pull-parser, xpp3) that used to sit on the libp2p dependency here does not
disappear: it arrives via the Aliyun and Route53 SDKs, which are now p2p's own
dependencies. The exclusions move with them, into a configurations.configureEach
block in p2p/build.gradle. Dropping them would silently re-admit artifacts the
project has excluded for years.
Removing a dependency also removes it as a version requester, so every version
the libp2p POM declared was checked against what :p2p now declares. All twelve
match, except one deliberate difference:
- grpc-netty: libp2p pinned 1.81.0; :p2p tracks rootProject.grpcVersion (1.83.1)
so it cannot drift from the Netty the rest of the build resolves.
commons-lang3 needs no declaration in :p2p at all. The root build already hands
every subproject `implementation "org.apache.commons:commons-lang3:3.20.0"`,
and an `implementation` dependency is on the declaring project's own compile
classpath -- it is only hidden from that project's consumers. Verified by
compiling :p2p main and test sources with no commons-lang3 line in
p2p/build.gradle.
Adding a project to the dependency graph also needs three task-dependency edges
that an external jar did not, each of which Gradle reported as an
implicit_dependency and answered by disabling execution optimizations:
- framework's buildFullNodeJar and plugins' binaryRelease both zip up
runtimeClasspath and maintain a hand-written dependsOn list of project jars.
:common now exposes p2p via `api`, so p2p-1.0.0.jar is on both classpaths;
without the edge a parallel build could assemble the shipped fat jar before
:p2p:jar exists.
- p2p's own processExampleResources reads src/example/resources, which the
protobuf plugin claims as an output of generateExampleProto because
generatedFilesBaseDir points at $projectDir/src.
verification-metadata.xml gains three components that resolve once p2p compiles
in-tree: bcutil-jdk18on:1.84, gson:2.9.0 and gson-parent:2.9.0. Checksums were
taken from Maven Central and cross-checked against the published .sha1.
gson 2.9.0 is older than the 2.14.0 used elsewhere, and that is fine: it only
appears on :p2p's isolated compile classpath. :framework's runtimeClasspath
still resolves gson:2.9.0 -> 2.14.0.
Verified with :framework:dependencies and :framework:dependencyInsight on
runtimeClasspath: `project :p2p` present, no external libp2p artifact, gson at
2.14.0 and commons-lang3 at 3.20.0 with :p2p among the requesters -- the same
versions the node resolved before.
A full build adds no implicit_dependency warning. The one it still reports,
:chainbase:jacocoTestReport, is present on release_v4.8.3 without this change.
Netty 4.2 split io.netty.handler.codec.protobuf out of netty-codec into its own artifact, so both :framework and :p2p have to declare it explicitly -- each puts the varint32 framing codecs on its channel pipelines. Both carried the literal 4.2.15.Final. Netty itself is not declared anywhere; it arrives transitively through grpc-netty, which :p2p tracks as rootProject.grpcVersion. So a grpc bump moves Netty while these two literals stay put -- exactly the mismatch that broke p2p's pipeline when develop moved to Netty 4.2 in the first place. Extract nettyVersion next to grpcVersion so the coupling is visible in one place. Resolution is unchanged: netty-codec-protobuf still resolves to 4.2.15.Final on both :framework:compileClasspath and :p2p:compileClasspath.
:framework uses org.tron.p2p in 17 files under src/main/java, but declared no
dependency on it. The types arrive three hops away, through
:common -> :crypto -> :chainbase, because common exposes p2p with
`api project(":p2p")`.
That export is not a mistake and is not removable here: CommonParameter
publishes `P2pConfig p2pConfig` and `PublishConfig dnsPublishConfig` as public
@Getter fields, so p2p types are part of :common's own API surface. Narrowing
it to `implementation` would break every caller of getP2pConfig(). Actually
de-coupling the graph means moving those fields out of CommonParameter, which
is a functional refactor and out of scope for this PR.
What is fixable now is the undeclared direct use. Declare it, so framework does
not depend on an unrelated module's export choice for code it uses itself.
api rather than implementation, because framework re-exports p2p types itself:
P2pEventHandlerImpl extends org.tron.p2p.P2pEventHandler, HelloMessage.getFrom()
returns org.tron.p2p.discover.Node, PeerManager.add/remove take
org.tron.p2p.connection.Channel, and Args.loadDnsPublishConfig returns
PublishConfig. implementation would compile today only because the transitive
api chain still supplies those types to consumers -- the moment that chain is
narrowed, it breaks.
No resolution change -- p2p was already on framework's compile and runtime
classpaths via the transitive api.
All 23 of v2.2.9's own test files, plus new ones, in p2p/src/test/java next to the code they cover -- the same place `common` and `plugins` keep theirs. Two upstream tests were unreliable by construction and are fixed rather than carried over as-is: - NetUtilTest.testGetIP called three public IP-echo services and asserted all three returned the same string: a network dependency, and a coin flip on any host with more than one egress address. It now runs against a loopback HttpServer, which exercises the same fetch/parse/validate path deterministically and covers the rejection branches too. libp2p's own CI never ran its tests, so this had not surfaced. - ConnPoolServiceTest.getNodes_orderByUpdateTimeDesc asserted that getNodes() returns nodes ordered by updateTime descending. getNodes() sorts, truncates to max(limit * 10, 50) candidates, then calls Collections.shuffle() -- so with two nodes that assertion passes about half the time. It now asserts membership, and getNodes_prefersNewestAboveCandidateSize covers the descending sort where it is actually observable: above the candidate bound. Two further defects made coverage depend on fork scheduling rather than on what the tests assert: - ConnPoolServiceTest and SocketTest bound fixed ports (10000 / 10001). PeerServer.start only logs on bind failure, so a collision let them pass while exercising nothing. Both now take a free port from PublicMethod.chooseRandomPort(), which is what java-tron's own tests use. - NodeTableTest read Parameter.p2pConfig without ever setting it, so it depended on an earlier class in the same fork having done so. Running the class on its own failed all eleven methods -- on this branch and on the unmodified one alike. It now sets up and restores its own config, and no longer shares one id array between two nodes. The added tests target what needs no live connection: the web3j copy (Numeric, Strings, Hash, Sign, ECKeyPair, ECDSASignature), every kad discovery message through its own wire bytes, P2pPacketDecoder's drop paths for hostile datagrams, Channel's send guard and exception classification, ChannelManager.processPeer's admission branches (ban list, global cap, per-IP cap, duplicate node id), Tree's signing and TXT output, AwsClient's Route53 batching limits, AliClient.deploy's threshold decision, and P2pService, which had no test at all. Where a collaborator is genuinely external -- the Aliyun SDK, process-wide ChannelManager state -- it is mocked, so the logic under test is real and only the transport is faked. This takes the module from the ~35% its own tests reach to 79.15% instruction coverage (12,580/15,893) and 78.44% line coverage (2,983/3,803). What remains uncovered needs a real socket: ConnPoolService.onConnect/onDisconnect/onMessage, NodeDetectService, PeerClient, Channel.init/send. Upstream's own SocketTest for exactly that is entirely commented out, so closing the gap means integration tests with real channels rather than more unit tests. One import was dropped: NodeHandlerTest had an unused org.checkerframework.checker.units.qual.N import, an IDE auto-import artifact that does not resolve on this classpath.
`DnsExample1`, `DnsExample2` and `ImportUsing` documented how an embedder configures and drives this module, but they only ever compiled. Each ended in a `while (true)` loop, bound a fixed port and pointed at live seed nodes, so nothing they demonstrated was checked -- and cubic found real defects sitting in them: `TestMessage` is not serializable so `ByteArray.fromObject` returns null and `Channel.send` closes the channel, and `DnsExample1` carried a signing private key in copyable code. Porting them line by line would produce three slow, network-dependent, port-bound tests. What is worth pinning is the contract they advertised: those configuration shapes are still accepted and still mean what the comments said. External embedders copy them, so a renamed setter or tightened validation is a breaking change even though nothing in this repo calls them. `ExampleUsageTest` covers all three shapes -- the connection-tuning surface, the register/start/query/close lifecycle on a free port with discovery off, the duplicate-message-type rejection, the AwsRoute53 publish config, and the discovery-off + tree-urls sync config. The signing key moves into the test as a fixture; it is upstream's well-known test key, already used by AlgorithmTest, and an embedder has to supply their own. `StartApp` moves to `src/main/java` and stays, as the entry point for debugging the module without starting java-tron. Moving it out of the exempt sourceSet subjects it to the project's checkstyle for the first time: three over-long lines, wrapped. It also carried a real bug. `--trust-ips` is declared as `ip[,ip[...]]` but resolved the whole comma-separated value as a single hostname, so with more than one address none of the listed peers became trusted. It now splits and resolves each, skipping and logging any that do not resolve. The `example` sourceSet and all of its build wiring are gone: the sourceSet block, the two extendsFrom configurations, the checkstyle opt-out, the encoding override, the Lombok wiring, and the `processExampleResources` task edge that only existed because `generatedFilesBaseDir` points into `src/`. 351 tests, 0 failures, 3 skipped. `:p2p:build` clean.
…mple The README is promoted from src/example/resources/README.md, where no reader would find it. It still read as upstream's document: four source links pointed at github.com/tronprotocol/libp2p, the invocations named libp2p.jar, and the prose described libp2p as a standalone project. Links are module-relative now, the prose is about this module, and the header states the provenance once -- the one remaining upstream link. StartApp gets the fixes that made it worth documenting. It left the checkstyle-exempt sourceSet, so it is linted for the first time: three over-long lines, wrapped. It also carried a real bug -- --trust-ips is declared ip[,ip[...]] but resolved the whole comma-separated value as a single hostname, so with more than one address none of the listed peers became trusted. logback.xml.example came from libp2p as a standalone project. Inside java-tron framework/src/main/resources/logback.xml is what applies, and the sample was being packaged into the jar for no reason.
…face Two adjustments to what this module's jacoco report measures. Neither is the code getting worse; both follow from where StartApp now lives. **StartApp, 981 instructions at 0%.** Moving it out of the `example` sourceSet into src/main/java put it in the coverage denominator for the first time -- that sourceSet was exempt from both checkstyle and coverage. It is argument parsing, option declarations and a main() that starts services and blocks: not module logic, and not code the node runs. `org/tron/p2p/example/**` is now excluded from this module's report alongside `**/protos/**`, which keeps the measured surface the same as before the move rather than hiding newly counted logic. The two parsing helpers it does own are real logic, and one of them shipped the `--trust-ips` bug fixed in the previous commit, so they are package-private now and `StartAppArgsTest` covers them: comma splitting, whitespace, unresolvable entries, and the bracketed-IPv6 form of `parseInetSocketAddressList`. The exclusion does not take regression protection with it. **Coverage that :framework's tests contribute.** :framework's own tests -- org.tron.core.net and friends -- execute a good deal of this module's code, and that coverage is real. :p2p:jacocoTestReport reads framework's exec data so it is counted where the classes actually live. It recovers only 70 instructions, not the ~700 an earlier measurement suggested: the tests this PR adds already reach most of what framework's tests were covering. The fileTree is empty when :framework:test has not run, so :p2p:build on its own still works. p2p instruction coverage after this commit: 79.15% (12,580/15,893).
Three follow-ups from review.
**`java -jar` failed.** The commit that moved StartApp into src/main put
`Main-Class` on the plain jar,
which is thin: the entry point resolved and then died on the first dependency it
touched, `NoClassDefFoundError: org/apache/commons/cli/ParseException`. That is
worse than declaring nothing -- it advertises support that cannot work. I had
only verified the `-cp` form documented in the README, not `java -jar` itself.
The plain jar drops `Main-Class` again and now fails honestly with "no main
manifest attribute". `buildStandaloneJar` produces `p2p-standalone.jar` with the
runtime classpath bundled, following :framework's FullNode.jar and :plugins'
Toolkit.jar -- same `artifacts { archives(...) }` wiring, the same
`-PbinaryRelease=false` opt-out, and the same exclusions for Bouncy Castle's
signatures and dnsjava's resolver SPI. Verified by running it: `java -jar
p2p/build/libs/p2p-standalone.jar --help` prints the help.
The `printRuntimeClasspath` helper is gone; it existed only to work around the
thin jar.
**The README still read as upstream's.** Four source links pointed at
github.com/tronprotocol/libp2p and the prose described libp2p as a standalone
project. Links are module-relative now, the prose talks about this module, and
the header states the provenance once and explains which of the two jars to use.
The one remaining upstream link is that attribution.
**Duplicated versions.** `protobufVersion` 3.25.8 was declared in both
:protocol and :p2p; checkstyle 8.7 sat in a local `versions` map in :framework
and :plugins and as a literal in :p2p. Both move to the root `ext` beside
`grpcVersion` and `nettyVersion`, and all four modules reference them, so the
duplication is removed rather than relocated. Resolution is unchanged:
`protobuf-java:3.25.8` on :p2p's compile classpath.
`src/main/protos` with an explicit `srcDir` override builds fine, but IDEA's protobuf plugin does not read that override -- it resolves imports against the plugin's default path -- so `import "Discover.proto"` in Connect.proto and every type it brings in showed as unresolved in the editor. Renamed to `src/main/proto`, the default, and dropped the sourceSet override. Generated sources still land in `src/main/java/org/tron/p2p/protos` via `generatedFilesBaseDir` and are still gitignored; `clean` still removes them. Worth flagging for whoever picks this up: `:protocol` has the identical setup -- `src/main/protos` plus the same explicit `srcDir`, with imports relative to that root -- so it presumably shows the same red in IDEA. This commit leaves it alone, which means the two modules now differ. Aligning `:protocol` is a one-directory rename too, but it is core java-tron with a `src/main/gen` interplay and does not belong in this PR. 357 tests, 0 failures, 3 skipped. `:p2p:build` clean.
2e50f85 to
99c6344
Compare
What
Internalize
io.github.tronprotocol:libp2p:2.2.9source as a newp2p/Gradle submodule, replacing the external Maven dependency. No functional change to P2P behaviour, protocol, or consensus.tronprotocol/java-tronrelease_v4.8.3@34d00c5b0tronprotocol/libp2ptagv2.2.9@c564f263d310d7a964035d3b597634aba6bda86dCommit 1 is byte-comparable to the upstream tag. It contains upstream's
src/mainverbatim — all 94 Java files, 90 undersrc/main/javaand the 4 reference classes split into a separatesrc/examplesourceSet — plus the new build files. A reviewer can diff it directly againstc564f263and see that no vendored line was altered. Everything this PR changes about that source lands in commit 2, separately, for exactly this reason.Why
Upstream
tronprotocol/libp2pis maintained in bursts. Over the last 25 months (2024-09 through 2026-09) 11 months had no commits at all, including four consecutive months (2025-12 through 2026-03), and there has been no commit since 2026-07:Cross-repo maintenance creates friction that shows up on every P2P change:
p2p/package in the monorepo; this follows the same patternInternalizing makes a P2P change a single PR instead of a cross-repo dance, and does not preclude back-porting individual commits to upstream libp2p when appropriate.
Changes
1.
chore(p2p): add libp2p v2.2.9 source as p2p moduleUpstream
src/mainverbatim: 90 files underp2p/src/main/java(including the embeddedorg.web3jcrypto utilities), 2.protofiles, the 4 reference classes underp2p/src/example, plusp2p/build.gradle,p2p/.gitignoreand thesettings.gradleregistration. Protobuf-generated sources are gitignored and regenerated at build time, so they are not in the diff.2.
style(p2p): conform vendored source to project conventionsThree mechanical rewrites plus formatting, kept separate so commit 1 stays diffable against the tag:
log.→logger.(178 call sites, 158 undersrc/mainand 20 in the example sourceSet) — the rootlombok.configsetslombok.log.fieldName=loggerMath.→StrictMath.(6 sites) — CI'scheck-mathrule rejectsjava.lang.Mathtree-widetoLowerCase()/toUpperCase()→Locale.ROOT(4 sites) — compile-forced by errorprone, see Key design decisionsFormatting:
google-java-formatover the 9 vendoredorg/web3j/**files, which came in AOSP 4-space style, plus import reordering.:p2p:checkstyleMainpasses with 0 violations.BasicThreadFactoryis deliberately left as upstream writes it — twelvebuilder()calls and the onenew BasicThreadFactory.Builder()inDiscoverServer. The root provides commons-lang3 3.20.0, wherebuilder()exists and the no-argBuilder()constructor is@Deprecated, so rewriting either way would add deviation from the tag and, in one direction, thirteen deprecation warnings.3.
build(common): switch from external libp2p to local p2p moduleReplaces the Maven dependency with
api project(":p2p"), collapsing 17 lines of dependency plus excludes into one.The dom4j exclusion tail that used to sit on the libp2p dependency here (
jaxen,stax-api,xsdlib,pull-parser,xpp3) does not disappear: it arrives via the Aliyun and Route53 SDKs, which are now:p2p's own dependencies, so aconfigurations.configureEachblock inp2p/build.gradle(added with the module in commit 1) carries it. Dropping it would silently re-admit artifacts the project has excluded for years. That block also namesmsvandrelaxngDatatype, whichcommon/build.gradledid not — the same dom4j tail, excluded completely rather than partially.Adding a project to the dependency graph also needs task-dependency edges that an external jar did not.
:framework'sbuildFullNodeJarand:plugins'binaryReleaseboth zip upruntimeClasspathand maintain a hand-writtendependsOnlist of project jars;p2p-1.0.0.jaris now on both classpaths, and without the edge a parallel build could assemble the shipped fat jar before:p2p:jarexists.gradle/verification-metadata.xmlgains three components that resolve once p2p compiles in-tree:bcutil-jdk18on:1.84,gson:2.9.0andgson-parent:2.9.0. Checksums taken from Maven Central and cross-checked against the published.sha1.4.
build: tracknetty-codec-protobufvia a rootnettyVersionNetty 4.2 split
io.netty.handler.codec.protobufout ofnetty-codec, so:frameworkand:p2pboth declare it explicitly — each puts the varint32 framing codecs on its channel pipelines. Netty itself is declared nowhere; it arrives transitively throughgrpc-netty, which:p2ptracks asrootProject.grpcVersion. ExtractingnettyVersionnext togrpcVersionmakes that coupling visible in one place instead of leaving two literals to drift.5.
build(framework): declare the direct:p2pdependency:frameworkusesorg.tron.p2pin 17 files undersrc/main/javabut declared nothing, relying on a three-hop transitive through:common -> :crypto -> :chainbase. Declared asapi, notimplementation, because framework re-exports p2p types:P2pEventHandlerImpl extends org.tron.p2p.P2pEventHandler,HelloMessage.getFrom()returnsorg.tron.p2p.discover.Node,PeerManager.add/removetakeorg.tron.p2p.connection.Channel, andArgs.loadDnsPublishConfigreturnsPublishConfig. No resolution change either way.6.
test(p2p): port and extend the module's test suiteAll 23 of v2.2.9's own test files, plus new ones, in
p2p/src/test/javanext to the code they cover — 64 files in total.Two upstream tests were unreliable by construction and are fixed rather than carried over as-is:
NetUtilTest.testGetIPcalled three public IP-echo services and asserted all three returned the same string: a network dependency, and a coin flip on any host with more than one egress address. It now runs against a loopbackHttpServer, covering the same fetch/parse/validate path plus the rejection branches. (libp2p's own CI never ran its tests, so this had not surfaced.)ConnPoolServiceTest.getNodes_orderByUpdateTimeDescassertedgetNodes()returns nodes ordered byupdateTimedescending.getNodes()sorts, truncates tomax(limit * 10, 50)candidates, then callsCollections.shuffle()— so with two nodes that assertion passes about half the time. It now asserts membership, and a new test covers the descending sort above the candidate bound, where it is actually observable.Two further defects made coverage depend on fork scheduling rather than on what the tests assert:
ConnPoolServiceTestandSocketTestbound fixed ports (10000 / 10001).PeerServer.startonly logs on bind failure, so a collision let them pass while exercising nothing. Both now take a free port fromTestPort.choose(). framework's own tests useorg.tron.common.utils.PublicMethod.chooseRandomPortfor this, but:p2pcannot depend on:frameworkwithout creating a cycle, so the same few lines live in the module.NodeTableTestreadParameter.p2pConfigwithout ever setting it, so it depended on an earlier class in the same fork having done so. Running the class on its own failed all eleven methods. It now sets up and restores its own config.7.
test(p2p): replace the example sourceSet with a usage contract testDnsExample1,DnsExample2andImportUsingdocumented how an embedder configures the module, but they only compiled — each ended in awhile (true)loop, bound a fixed port and pointed at live seed nodes, so nothing they showed was checked. Two carried real defects (TestMessageis not serializable, soByteArray.fromObjectreturns null andChannel.sendcloses the channel;DnsExample1held a signing private key in copyable code).ExampleUsageTestpins the contract they advertised instead: those configuration shapes are still accepted and still mean what the comments said.8.
docs(p2p): add a README, drop the vendored logback sampleThe README is promoted from
src/example/resources/README.md, where no reader would find it, and rewritten — it still read as upstream's document, with four source links pointing at github.com/tronprotocol/libp2p and prose describing libp2p as a standalone project.StartAppmoves tosrc/main/javaas the standalone entry point and gets the fixes that made it worth documenting, including a real bug:--trust-ipsis declaredip[,ip[...]]but resolved the whole comma-separated value as a single hostname, so with more than one address none of the listed peers became trusted.logback.xml.examplecame from libp2p as a standalone project; inside java-tronframework/src/main/resources/logback.xmlis what applies, and the sample was being packaged into the jar for no reason.9.
test(p2p): cover StartApp's arg parsing, correct the coverage surfaceMoving
StartAppout of the checkstyle- and coverage-exemptexamplesourceSet puts 981 instructions at 0% into the denominator. It is argument parsing, option declarations and amain()that starts services and blocks — not module logic, and not code the node runs — soorg/tron/p2p/example/**is excluded from this module's jacoco report alongside**/protos/**. The two parsing helpers it does own are package-private now and covered byStartAppArgsTest, so the exclusion does not take regression protection with it.:p2p:jacocoTestReportalso reads:framework's exec data, because framework's own tests execute p2p code and that coverage is real.10.
build(p2p): ship a runnable standalone jar, stop repeating versionsThe plain jar is thin, so declaring
Main-Classon it would be worse than declaring nothing — the entry point resolves and then dies onNoClassDefFoundError: org/apache/commons/cli/ParseException. It declares none and fails honestly with "no main manifest attribute";buildStandaloneJarproducesp2p-standalone.jarwith the runtime classpath bundled, wired exactly like:framework'sFullNode.jarand:plugins'Toolkit.jar— sameartifacts { archives(...) }, same-PbinaryRelease=falseopt-out, same Bouncy Castle signature and dnsjava SPI exclusions. Verified by running both jars.protobufVersion3.25.8 was declared in both:protocoland:p2p; checkstyle 8.7 sat in a localversionsmap in:frameworkand:pluginsand as a literal in:p2p. Both move to the rootextbesidegrpcVersionandnettyVersion, and all four modules reference them — removed rather than relocated.11.
build(p2p): put.protofiles where the protobuf plugin looks for themsrc/main/protoswith an explicitsrcDiroverride builds fine, but IDEA's protobuf plugin does not read that override — it resolves against the plugin's default path — soimport "Discover.proto"and every type it brings in showed as unresolved in the editor. The override is gone; generated sources still land insrc/main/java/org/tron/p2p/protosand are still gitignored.Flagging for a follow-up:
:protocolhas the identical setup and presumably the identical symptom. Aligning it is the same one-directory rename, but it is core java-tron with asrc/main/geninterplay and does not belong here.Key design decisions
p2porg.tron.p2p, geth'sp2p/convention, and the project's short-name style (common,crypto):p2p:protocolwould make:p2pdepend on:protocoland cost the module its leaf position — it currently has zero project dependencies, which is what keeps vendored code from reaching back into java-tron internals and keeps upstream re-contribution possiblep2p/src/testcommonandplugins. Keeping them in the module also avoids a:frameworktest-classpath SDK re-declaration plus dom4j mirror, and a jacocoadditionalClassDirsbolt-onExampleUsageTest:p2pimplementation "org.apache.commons:commons-lang3:3.20.0", and animplementationdependency is on the declaring project's own compile classpath. Confirmed by compiling:p2pmain and test sources with nocommons-lang3lineBasicThreadFactorybuilder()exists at 3.20.0 and the no-argBuilder()constructor is@Deprecated; rewriting either way adds deviation from the taggrpc-nettyrootProject.grpcVersionnetty-codec-protobufnettyVersion:frameworkand:p2p; a shared version makes the coupling togrpcVersionvisible:framework -> :p2papiMath→StrictMathcheck-mathrule; integer results identicalLocale.ROOTStringCaseLocaleUsageis ERROR on every subproject exceptprotocol/errorprone. The one non-mechanical change in this PR — behaviour is identical for ASCII but differs under a Turkish locale. Matches the project's own idiom inArgs.java:1273**/protos/**,**/example/**Scope
.protofiles move verbatimorg.tron.p2p.**), so imports in dependent code are untouchedgit pulland rebuild:p2premains usable standalone —java -jar p2p-standalone.jarruns the module on its own; seep2p/README.mdDefects found in the vendored source (not fixed here)
Two instances of the same shape: a DNS parse helper throws an unchecked exception past a
catch (DnsException)that was clearly written to tolerate unparseable input, so one malformed TXT record aborts the whole operation instead of being skipped.Short root entry.
RootEntry.parseEntry(dns/tree/RootEntry.java:67) doese.substring(rootPrefix.length())with no length guard, so any value shorter than the 13-charactertree-root-v1:prefix throwsStringIndexOutOfBoundsException. The caller atdns/update/AwsClient.java:334catches onlyDnsException, so it escapes and abortscomputeChanges, failing the entire publish.Malformed base64 in a nodes entry.
Algorithm.decode64(dns/tree/Algorithm.java:121) callsBase64.getUrlDecoder().decode()directly, which throwsIllegalArgumentException.NodesEntry.parseEntryconverts onlyInvalidProtocolBufferExceptionandUnknownHostExceptionintoDnsException, so it escapes both that and theDnsException-only catch atdns/update/AliClient.java:138, aborting the wholecollectRecordsand thedeploy()that called it.A third, latent instance:
BranchEntry.parseEntry(dns/tree/BranchEntry.java:19) does the same unguardedsubstring, and unlike its siblings does not declarethrows DnsException. Its only caller checks the prefix first, so it is not currently reachable — a hazard for the next caller, not a live bug.LinkEntry.parseEntryshows the correct shape: prefix check, length check, andcatch (RuntimeException)around the decode.Reachability — operator-side, not peer-reachable
Message.parsecatch (Exception)P2pPacketDecodercatch (Exception)RandomIterator.next()catch (Exception)aroundsyncRandom()catch (DnsException)No remote peer can trigger these. Both live instances are on the operator's own publish path: they abort a DNS publish for whoever runs it, they do not give an attacker anything.
All three are pre-existing in libp2p and out of scope for a no-functional-changes PR, so they are reported rather than fixed. Instance 2 is pinned by a test that asserts the defect —
AwsClientRecordsTest.malformedBase64InANodesEntryAbortsTheWholeCollectionfails loudly if the escape is ever closed.Known security issues — pre-existing, deferred
These exist in libp2p today and are unchanged by this PR. Internalizing the source is what makes them fixable in-tree; each gets its own follow-up:
compressPubKeydrops leading zerosAwsClientswallowsInterruptedExceptionwithout restoring the interrupt flagMixing any of them in would break the "no functional changes" claim, which is the only thing that makes a diff this size reviewable.
Test
:p2p:test:framework:test:p2p:checkstyleMain/checkstyleTestcheck-mathjava -jar p2p-standalone.jar --helpproject :p2pon frameworkruntimeClasspathlibp2partifactsgson:2.9.0 -> 2.14.0,commons-lang3at 3.20.0implicit_dependencywarning vs baseCoverage
Vendoring puts 15,893 instructions into the repo-wide denominator that were not there before, and the overall-delta gate is sensitive to exactly that. The choice is between hiding vendored code from the metric and actually testing it; this PR does the latter.
The tests added here target what needs no live connection: the web3j copy, every kad discovery message through its own wire bytes,
P2pPacketDecoder's drop paths for hostile datagrams,Channel's send guard and exception classification,ChannelManager.processPeer's admission branches,Tree's signing and TXT output,AwsClient's Route53 batching limits,AliClient.deploy's threshold decision, andP2pService, which had no test at all.What remains uncovered needs a real socket:
ConnPoolService.onConnect/onDisconnect/onMessage,NodeDetectService,PeerClient,Channel.init/send. Upstream's ownSocketTestfor exactly that is entirely commented out, so closing the gap means integration tests with real channels rather than more unit tests.Known flakiness
Three
org.tron.common.runtime.vmtests fail on their first attempt in a full local run and pass on retry —ValidateMultiSignContractTest.testTip854RejectsMalformedCalldata,AllowTvmLondonTest.testBaseFee,AllowTvmLondonTest.testStartWithEF. A full./gradlew teston an unmodifiedrelease_v4.8.3checkout produces the same three; both runs execute an identical set of 3,307 tests, and this PR adds none to:framework.Two ported upstream tests need live DNS against a third-party zone and are left as-is rather than disabled, since they exercise real discovery behaviour:
RandomTest.testRandomIteratorandSyncTestboth synctree://…@nile.trondisco.netthrough hard-coded public resolvers. On a runner with restricted egress, or if that DNS tree is re-published or retired, they fail permanently.LookUpTxtTestin the same package already@Ignores its network tests, so that is the precedent if reviewers would rather these be skipped than retried; disabling them costs roughly 1.5 points of coverage, which still clears both gates.A
BindExceptioninMetrics.initcan cascade through four framework test classes (SRMetricsTest,PrometheusApiServiceTest,JsonrpcServiceTest,RpcApiServicesTest) that each bind the same hard-coded Prometheus port 9527 while the test task runs up to 4 parallel forks. Verified pre-existing by running those four classes together with no p2p tests in the run: it reproduces, and hits a different class each time. The race is java-tron's, not p2p's.