Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ The java-tron project comes with several runnable artifacts and helper scripts f
| :---------------------- | :---------- |
| **`FullNode.jar`** | Main TRON node executable (generated in `build/libs/` after a successful build following the above guidance). Runs as a full node by default. `java -jar FullNode.jar --help` for command line options|
| **`Toolkit.jar`** | Node management utility (generated in `build/libs/`): partition, prune, copy, convert DBs; shadow-fork tool. [Usage](https://tronprotocol.github.io/documentation-en/using_javatron/toolkit/#toolkit-a-java-tron-node-maintenance-suite) |
| **`start.sh`** | Quick start script (x86_64, JDK 8) to download/build/run `FullNode.jar`. See the tool [guide](./shell.md). |
| **`start.sh.simple`** | Quick start script template (ARM64, JDK 17). See usage notes inside the script. |
| **`start.sh`** | Quick start script to download/build/run `FullNode.jar` on x86_64 (JDK 8) and ARM64 (JDK 17). See the tool [guide](./shell.md). |

# Running java-tron

Expand Down
31 changes: 0 additions & 31 deletions chainbase/src/main/java/org/tron/core/db/KhaosDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -205,37 +205,6 @@ private void checkNull(Object o) throws NonCommonBlockException {
}
}

/**
* Find two block's most recent common parent block.
*/
@Deprecated
public Pair<LinkedList<BlockCapsule>, LinkedList<BlockCapsule>> getBranch(
BlockId block1, BlockId block2) {
LinkedList<BlockCapsule> list1 = new LinkedList<>();
LinkedList<BlockCapsule> list2 = new LinkedList<>();
KhaosBlock kblk1 = miniStore.getByHash(block1);
KhaosBlock kblk2 = miniStore.getByHash(block2);

if (kblk1 != null && kblk2 != null) {
while (!Objects.equals(kblk1, kblk2)) {
if (kblk1.num > kblk2.num) {
list1.add(kblk1.blk);
kblk1 = kblk1.getParent();
} else if (kblk1.num < kblk2.num) {
list2.add(kblk2.blk);
kblk2 = kblk2.getParent();
} else {
list1.add(kblk1.blk);
list2.add(kblk2.blk);
kblk1 = kblk1.getParent();
kblk2 = kblk2.getParent();
}
}
}

return new Pair<>(list1, list2);
}

// only for unit test
public BlockCapsule getParentBlock(Sha256Hash hash) {
return Stream.of(miniStore.getByHash(hash), miniUnlinkedStore.getByHash(hash))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public abstract class HttpService extends AbstractService {

protected long maxRequestSize = 4 * 1024 * 1024; // 4MB

// Once maxHttpConnectNumber is reached, open connections time out after this much
// inactivity, so connections that send nothing cannot hold every slot.
private static final long CONNECTION_LIMIT_IDLE_TIMEOUT_MS = 10_000;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NIT] While saturated, the 10s idle timeout applies to all established connections, not just inactive ones

When maxHttpConnectNumber is reached, Jetty's ConnectionLimit.limit() applies this idle timeout to every connected endpoint, not only to connections that have sent nothing (verified against jetty-server 9.4.58 sources). During saturation, a keep-alive client with more than 10s between two requests on the same connection will be disconnected and must reconnect. Request processing time does not count as idle, so normal API calls are unaffected.

This is inherent to the upstream API and a reasonable price for freeing slots with a minimal change, but the PR description's "idle connections" phrasing reads narrower than the actual behavior.

Suggestion: add one sentence to the PR description or the http/shell documentation describing the saturated-state effect on keep-alive clients; introduce a config key for the timeout only if field evidence shows real clients being hurt.


@VisibleForTesting
public long getMaxRequestSize() {
return this.maxRequestSize;
Expand Down Expand Up @@ -80,7 +84,9 @@ protected void initServer() {
this.apiServer = new Server(this.port);
int maxHttpConnectNumber = Args.getInstance().getMaxHttpConnectNumber();
if (maxHttpConnectNumber > 0) {
this.apiServer.addBean(new ConnectionLimit(maxHttpConnectNumber, this.apiServer));
ConnectionLimit connectionLimit = new ConnectionLimit(maxHttpConnectNumber, this.apiServer);
connectionLimit.setIdleTimeout(CONNECTION_LIMIT_IDLE_TIMEOUT_MS);
this.apiServer.addBean(connectionLimit);
}
this.apiServer.setErrorHandler(new OversizedRequestErrorHandler());
}
Expand Down
6 changes: 4 additions & 2 deletions framework/src/main/java/org/tron/core/config/args/Args.java
Original file line number Diff line number Diff line change
Expand Up @@ -1017,14 +1017,16 @@ private static void loadDnsPublishParameters(NodeConfig.DnsConfig dns,
if (dns.getChangeThreshold() > 0) {
publishConfig.setChangeThreshold(dns.getChangeThreshold());
} else if (Double.compare(dns.getChangeThreshold(), 0.0) != 0) {
logger.error("Check node.dns.changeThreshold, should be bigger than 0, default 0.1");
throw new TronError("Check node.dns.changeThreshold, should be bigger than 0, default 0.1",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NIT] Fail-fast on invalid dns parameters also fires when node.dns.publish=false

loadDnsPublishParameters now throws TronError(PARAMETER_INIT) on an invalid node.dns.changeThreshold / node.dns.maxMergeSize, and this validation runs unconditionally on the startup path. An existing node that carries an invalid value in its config but has node.dns.publish=false (i.e. never uses DNS publishing) previously only logged an error and will now fail to start at all after upgrading.

The error message is clear and the fix is a one-line config edit, so this is not blocking — but the behavior-change surface is wider than "validation for the DNS publish feature" suggests.

Suggestion: either (a) skip the validation when dns.isPublish() is false, or (b) keep the current behavior and explicitly disclose in the PR description / release notes that invalid dns parameters now fail startup even with publish disabled.

TronError.ErrCode.PARAMETER_INIT);
}

int maxMergeSize = dns.getMaxMergeSize();
if (maxMergeSize >= 1 && maxMergeSize <= 5) {
publishConfig.setMaxMergeSize(maxMergeSize);
} else if (maxMergeSize != 0) {
logger.error("Check node.dns.maxMergeSize, should be [1~5], default 5");
throw new TronError("Check node.dns.maxMergeSize, should be [1~5], default 5",
TronError.ErrCode.PARAMETER_INIT);
}

if (StringUtils.isNotEmpty(dns.getDnsPrivate())) {
Expand Down
1 change: 0 additions & 1 deletion framework/src/main/java/org/tron/core/db/Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import static org.tron.common.math.Maths.min;
import static org.tron.common.utils.Commons.adjustBalance;
import static org.tron.core.Constant.TRANSACTION_MAX_BYTE_SIZE;
import static org.tron.core.exception.BadBlockException.TypeEnum.CALC_MERKLE_ROOT_FAILED;
import static org.tron.protos.Protocol.Transaction.Contract.ContractType.TransferContract;
import static org.tron.protos.Protocol.Transaction.Result.contractResult.SUCCESS;

Expand Down
148 changes: 148 additions & 0 deletions framework/src/test/java/org/tron/common/jetty/ConnectionLimitTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package org.tron.common.jetty;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.tron.common.TestConstants;
import org.tron.common.application.HttpService;
import org.tron.common.utils.PublicMethod;
import org.tron.core.config.args.Args;

/**
* Tests the connection limit configured in {@link HttpService}: once connections that send
* nothing hold every slot, the server closes them after the limit's idle timeout and serves
* new clients, instead of waiting for the connector's 30-second idle timeout.
*/
public class ConnectionLimitTest {

private static final int MAX_CONNECTIONS = 2;

// Below the connector's default 30-second idle timeout, above the limit's 10-second one
// applied twice (Jetty half-closes an idle connection before closing it).
private static final int TIMEOUT_MS = 25_000;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NIT] ConnectionLimitTest takes ~20s and runs with a tight timing margin

The test must really wait for the hardcoded CONNECTION_LIMIT_IDLE_TIMEOUT_MS = 10_000 in HttpService to fire, and Jetty closes an idled connection in two idle periods (half-close first, verified in jetty-io 9.4.58), so slot release takes ~20s. The test method therefore runs ~20.3s, well above the project's per-test performance baseline, and the client-side timeout of 25s (TIMEOUT_MS here) leaves only ~5s of margin against the ~20s expected close point — an occasional failure on a loaded CI machine is plausible (the @Test(timeout = 60_000) backstop turns it into a rerun, not a false diagnosis).

Suggestion: make the idle timeout injectable (e.g. a @VisibleForTesting setter or constructor parameter on the service/limit wiring) so the test can use a 1-2s timeout, finish within ~5s, and keep a comfortable wait margin. Directional improvement; need not block this PR.


@ClassRule
public static final TemporaryFolder temporaryFolder = new TemporaryFolder();

private static TestHttpService httpService;
private static int port;

public static class OkServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setStatus(HttpServletResponse.SC_OK);
resp.getWriter().print("ok");
}
}

static class TestHttpService extends HttpService {
TestHttpService(int port) {
this.port = port;
this.contextPath = "/";
}

@Override
protected void addServlet(ServletContextHandler context) {
context.addServlet(new ServletHolder(new OkServlet()), "/*");
}

int connectedEndPoints() {
return ((AbstractConnector) apiServer.getConnectors()[0]).getConnectedEndPoints().size();
}
}

@BeforeClass
public static void setup() throws Exception {
Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()},
TestConstants.TEST_CONF);
Args.getInstance().setMaxHttpConnectNumber(MAX_CONNECTIONS);
port = PublicMethod.chooseRandomPort();
httpService = new TestHttpService(port);
httpService.start().get(10, TimeUnit.SECONDS);
}

@AfterClass
public static void teardown() throws Exception {
try {
if (httpService != null) {
httpService.stop();
}
} finally {
Args.clearParam();
}
}

@Test(timeout = 60_000)
public void testIdleConnectionsDoNotLockOutClients() throws Exception {
List<Socket> idleSockets = new ArrayList<>();
try {
for (int i = 0; i < MAX_CONNECTIONS; i++) {
Socket socket = new Socket();
socket.connect(new InetSocketAddress("localhost", port), TIMEOUT_MS);
idleSockets.add(socket);
awaitConnectedEndPoints(i + 1);
}

Assert.assertEquals("HTTP/1.1 200 OK", get());
for (Socket socket : idleSockets) {
assertClosedByServer(socket);
}
} finally {
for (Socket socket : idleSockets) {
socket.close();
}
}
}

private static void awaitConnectedEndPoints(int expected) throws InterruptedException {
long deadline = System.currentTimeMillis() + TIMEOUT_MS;
while (httpService.connectedEndPoints() < expected) {
Assert.assertTrue("server did not accept connection " + expected,
System.currentTimeMillis() < deadline);
Thread.sleep(10);
}
}

private static String get() throws IOException {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress("localhost", port), TIMEOUT_MS);
socket.setSoTimeout(TIMEOUT_MS);
OutputStream out = socket.getOutputStream();
out.write("GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n"
.getBytes(StandardCharsets.US_ASCII));
out.flush();
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream(), StandardCharsets.US_ASCII));
return in.readLine();
}
}

private static void assertClosedByServer(Socket socket) throws IOException {
socket.setSoTimeout(TIMEOUT_MS);
try {
Assert.assertEquals(-1, socket.getInputStream().read());
} catch (SocketException e) {
// a reset also means the server dropped the connection
}
}
}
24 changes: 24 additions & 0 deletions framework/src/test/java/org/tron/core/config/args/ArgsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,30 @@ public void testDnsPublishRejectsEmptyRequiredParameterWithParameterInitError()
error.getMessage());
}

@Test
public void testDnsPublishRejectsInvalidChangeThresholdWithParameterInitError() {
Config config = dnsPublishConfig("node.dns.changeThreshold", "-0.1");

TronError error = Assert.assertThrows(TronError.class,
() -> Args.loadDnsPublishConfig(NodeConfig.fromConfig(config)));

Assert.assertEquals(TronError.ErrCode.PARAMETER_INIT, error.getErrCode());
Assert.assertEquals("Check node.dns.changeThreshold, should be bigger than 0, default 0.1",
error.getMessage());
}

@Test
public void testDnsPublishRejectsInvalidMaxMergeSizeWithParameterInitError() {
Config config = dnsPublishConfig("node.dns.maxMergeSize", "6");

TronError error = Assert.assertThrows(TronError.class,
() -> Args.loadDnsPublishConfig(NodeConfig.fromConfig(config)));

Assert.assertEquals(TronError.ErrCode.PARAMETER_INIT, error.getErrCode());
Assert.assertEquals("Check node.dns.maxMergeSize, should be [1~5], default 5",
error.getMessage());
}

@Test
public void testCommitteeConfigRejectsOldRewardOptimizationWithoutPrerequisite() {
Map<String, Object> configMap = new HashMap<>();
Expand Down
40 changes: 31 additions & 9 deletions shell.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ If you already downloaded the `FullNode.jar`, you can use `start.sh` to run it,

The script is available in the java-tron project at [github](https://github.com/tronprotocol/java-tron), or if you need a separate script: [start.sh](https://github.com/tronprotocol/java-tron/blob/develop/start.sh)

The script runs on x86_64 with JDK 8 and on ARM64 with JDK 17. It picks the JVM options for the Java version it finds, and downloads the release jars built for that architecture (`FullNode-aarch64.jar` on ARM64).

Downloaded release jars are verified against the GPG signature published with each release, made by the key listed under "Integrity Check" in the [README](./README.md), so `gpg` must be installed; a jar that fails verification is not used. The mainnet config is downloaded from java-tron and the Nile testnet config from [nile-testnet](https://github.com/tron-nile-testnet/nile-testnet).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NIT] Documentation & metadata backlog (2 items rolled up)

Grouped as one comment since both are doc-or-metadata asks. This is a cross-cutting concern — the anchored line itself is fine.

  1. Keyserver network dependency and trust-anchor boundary not fully documented (shell.md:13, README "Integrity Check"). The verification documented here fetches the release key fresh from keys.openpgp.org / keyserver.ubuntu.com on every download, so installing a release jar now requires outbound access to those keyservers, and fails closed when both are unreachable. Also worth one sentence on the trust-anchor boundary: the hardcoded fingerprint protects existing deployments against release-asset tampering, but cannot protect against a compromise of the GitHub repo that hosts both the script and the jars. Impact: operators troubleshooting "why won't it install" lack a documented path. Suggestion: add a sentence on the keyserver network requirement to shell.md, and optionally note the trust-anchor boundary in the README "Integrity Check" section.

  2. Branch naming convention (cross-cutting, no code anchor). The head branch fix_audit_issues has no feature/ / hotfix/ style prefix and no / separator, departing from the documented convention in CONTRIBUTING.md. No action needed for this PR; please use a prefixed name for future branches.

Suggestion: one sentence in shell.md about the keyserver requirement (plus an optional README note), and a prefixed branch name next time.


***

# Usage
Expand All @@ -32,6 +36,8 @@ The script is available in the java-tron project at [github](https://github.com/
sh start.sh --stop
```

`--run` records the process id in `<jar name>.pid` (`FullNode.jar.pid` by default) next to `start.log`, and `--stop` reads it. Run `--stop` in the directory the node was started from, with the same `-j` name if one was given. After `--release` or `-cb` the node runs in `FullNode/`; `--stop` finds it there from the parent directory as well. A node started by an earlier version of the script (a `start.log` but no pid file) is still found by its jar name.

* Get the latest version of `FullNode.jar` and start it

```
Expand All @@ -58,17 +64,21 @@ The script is available in the java-tron project at [github](https://github.com/

start the service

* `--stop`
* `--stop` or `-s`

stop the service started from the current directory

* `--`

stop the service
Everything after it is passed to `FullNode.jar` unchanged. Options the script does not know are passed on as well, so `--` is only needed when a value of such an option looks like a script option or a jar name.

* `-c`

Specify the configuration file, by default it will load the `config.conf` in the same directory as `FullNode.jar`
Specify the configuration file, by default it will load the `config.conf` in the current directory

* `-d`

Specify the database storage path, The default path is the same directory where `FullNode.jar` is located.
Specify the database storage path. The default is `output-directory` in the directory the script is run from (`FullNode/` after `--release` or `-cb`).

* `-j`

Expand All @@ -79,7 +89,7 @@ The script is available in the java-tron project at [github](https://github.com/
Specify the maximum memory of the `FullNode.jar` service in`MB`, jvm's startup maximum memory will be adjusted according to this parameter.

* `--net`
Select test and private networks.
Select test (Nile) and private networks.

### build project

Expand All @@ -91,6 +101,14 @@ The script is available in the java-tron project at [github](https://github.com/

Get the latest released version of the `jar` package from github.

* `--upgrade`

Replace the local `jar` package with the latest release; the previous one is kept as `FullNode.jar_bak`.

* `--download`

Download the latest released `jar` package into the current directory without starting it.


### rebuild the manifest

Expand All @@ -100,7 +118,7 @@ The script is available in the java-tron project at [github](https://github.com/

* `-m`

specify the minimum required manifest file size ,unit:M,default:0
specify the minimum required manifest file size ,unit:M,default:128

* `-b`

Expand Down Expand Up @@ -152,7 +170,7 @@ sh start.sh --stop
Format:

```
sh start.sh <[--release | -cb]> <--run> [-m <manifest size>] | [-b <batch size>] | [-d <db database-directory> | [-dr | --disable-rewrite-manifes]]
sh start.sh <[--release | -cb]> <--run> [-m <manifest size>] | [-b <batch size>] | [-d <db database-directory> | [-dr | --disable-rewrite-manifest]]
```

Get the latest released version.
Expand All @@ -162,13 +180,15 @@ Get the latest released version.
sh start.sh --release --run
```

Following file structure will be generated after executing the above command and the `FullNode.jar` will be started.
Following file structure will be generated after executing the above command and the `FullNode.jar` will be started. The node runs from `FullNode/`, so later `--run` commands are executed there with the copied script; `--stop` works both there and from the parent directory.

```
├── ...
├── FullNode/
├── config.conf
├── FullNode.jar
├── FullNode.jar.pid
├── start.log
├── start.sh
```

Expand Down Expand Up @@ -214,12 +234,14 @@ Following file structure will be created:
├── FullNode/
|── config.conf
├── FullNode.jar
├── FullNode.jar.pid
├── start.log
├── start.sh
```

### 3. rebuild manifest tool

This tool provides the ability to reformat the manifest based on current database, Enabled by default.
This tool provides the ability to reformat the manifest based on current database, Enabled by default. It applies to LevelDB only and is skipped on ARM64, which runs RocksDB.

1.Local mode:

Expand Down
Loading
Loading