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
6 changes: 5 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ plugins {
id 'net.ltgt.errorprone' version '5.0.0' apply false
}

ext {
grpcVersion = "1.83.0"
}

allprojects {
version = "1.0.0"
apply plugin: "java-library"
Expand Down Expand Up @@ -41,7 +45,7 @@ ext.archInfo = [
// https://github.com/grpc/grpc-java/issues/7690
// https://github.com/grpc/grpc-java/pull/12319, Add support for macOS aarch64 with universal binary
// https://github.com/grpc/grpc-java/pull/11371 , 1.64.x is not supported CentOS 7.
ProtocGenVersion: isArm64 || isMac ? '1.81.0' : '1.60.0'
ProtocGenVersion: isArm64 || isMac ? rootProject.grpcVersion : '1.60.0'
],
VMOptions: isArm64 ? "${rootDir}/gradle/jdk17/java-tron.vmoptions" : "${rootDir}/gradle/java-tron.vmoptions"
]
Expand Down
2 changes: 1 addition & 1 deletion common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ dependencies {
api 'org.aspectj:aspectjrt:1.9.8'
api 'org.aspectj:aspectjweaver:1.9.8'
api 'org.aspectj:aspectjtools:1.9.8'
api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.8',{
api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.9',{
exclude group: 'io.grpc', module: 'grpc-context'
exclude group: 'io.grpc', module: 'grpc-core'
exclude group: 'io.grpc', module: 'grpc-netty'
Expand Down
9 changes: 7 additions & 2 deletions common/src/main/java/org/tron/core/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ node {
# Number of gRPC thread, default availableProcessors / 2
# thread = 16

# The maximum number of concurrent calls permitted for each incoming connection
# maxConcurrentCallsPerConnection =
# The maximum number of concurrent calls permitted for each incoming connection,
# default 100. Setting 0 also uses the secure default.
# maxConcurrentCallsPerConnection = 100

# The HTTP/2 flow control window, default 1MB
# flowControlWindow =
Expand All @@ -75,6 +76,10 @@ node {
}
```

> **Upgrade note:** `maxConcurrentCallsPerConnection = 0` previously disabled the limit.
> It now selects the secure default of 100. Configure an explicit positive value if a node
> requires more than 100 concurrent calls on one connection.

## backup
You can customize backup options in the `node.backup` part of `config.conf`, which looks like:
```
Expand Down
16 changes: 14 additions & 2 deletions common/src/main/java/org/tron/core/config/args/NodeConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ public static class HttpConfig {
@Setter
public static class RpcConfig {

public static final int DEFAULT_MAX_CONCURRENT_CALLS_PER_CONNECTION = 100;

private boolean enable = true;
private int port = 50051;
private boolean solidityEnable = true;
Expand All @@ -215,7 +217,8 @@ public static class RpcConfig {
private int pBFTPort = 50071;

private int thread = 0;
private int maxConcurrentCallsPerConnection = 0;
private int maxConcurrentCallsPerConnection =
DEFAULT_MAX_CONCURRENT_CALLS_PER_CONNECTION;
private int flowControlWindow = 1048576;
private long maxConnectionIdleInMillis = 0;
private long maxConnectionAgeInMillis = 0;
Expand Down Expand Up @@ -358,8 +361,17 @@ private void postProcess() {
rpc.thread = (Runtime.getRuntime().availableProcessors() + 1) / 2;
}

if (rpc.maxConcurrentCallsPerConnection < 0) {
throw new TronError("node.rpc.maxConcurrentCallsPerConnection must be non-negative, got: "
+ rpc.maxConcurrentCallsPerConnection, PARAMETER_INIT);
}
if (rpc.maxConcurrentCallsPerConnection == 0) {
rpc.maxConcurrentCallsPerConnection = Integer.MAX_VALUE;
logger.warn("Configuring [node.rpc.maxConcurrentCallsPerConnection] as 0 no longer "
+ "disables the limit; using the secure default of {}. Configure an explicit positive "
+ "value if more concurrency is required.",
RpcConfig.DEFAULT_MAX_CONCURRENT_CALLS_PER_CONNECTION);
rpc.maxConcurrentCallsPerConnection =
RpcConfig.DEFAULT_MAX_CONCURRENT_CALLS_PER_CONNECTION;
}
if (rpc.maxConnectionIdleInMillis == 0) {
rpc.maxConnectionIdleInMillis = Long.MAX_VALUE;
Expand Down
2 changes: 2 additions & 0 deletions common/src/main/java/org/tron/json/JSON.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public final class JSON {
.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true)
// Fastjson Feature.IgnoreNotMatch (default ON) — unknown fields silently ignored
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
// Fastjson 1.x rejects non-comment tokens after the root value
.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, true)
// Fastjson serializes empty beans as "{}" without error
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
// Fastjson omits null-valued fields by default (WriteMapNullValue is OFF by default)
Expand Down
5 changes: 2 additions & 3 deletions common/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,8 @@ node {
# Number of gRPC threads, 0 = auto (availableProcessors / 2)
thread = 0

# Maximum concurrent calls per incoming connection
# 0 means No limit on concurrent calls per connection
maxConcurrentCallsPerConnection = 0
# Maximum concurrent calls per incoming connection. 0 falls back to the secure default of 100.
maxConcurrentCallsPerConnection = 100

# HTTP/2 flow control window (bytes), default 1MB
flowControlWindow = 1048576
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.junit.Test;
import org.tron.core.exception.TronError;

public class NodeConfigTest {

Expand Down Expand Up @@ -102,7 +104,8 @@ public void testRpcDefaultsFromReference() {
NodeConfig.RpcConfig rpc = nc.getRpc();

// reference.conf provides actual final defaults, no sentinel conversion needed
assertEquals(2147483647, rpc.getMaxConcurrentCallsPerConnection());
assertEquals(NodeConfig.RpcConfig.DEFAULT_MAX_CONCURRENT_CALLS_PER_CONNECTION,
rpc.getMaxConcurrentCallsPerConnection());
assertEquals(1048576, rpc.getFlowControlWindow());
assertEquals(9223372036854775807L, rpc.getMaxConnectionIdleInMillis());
assertEquals(9223372036854775807L, rpc.getMaxConnectionAgeInMillis());
Expand All @@ -122,6 +125,27 @@ public void testRpcUserOverrideZeroNotConverted() {
assertEquals(0, nc.getRpc().getMinEffectiveConnection());
}

@Test
public void testRpcZeroConcurrentCallsUsesSecureDefault() {
Config config = withRef(
"node { rpc { maxConcurrentCallsPerConnection = 0 } }");
NodeConfig nc = NodeConfig.fromConfig(config);
assertEquals(NodeConfig.RpcConfig.DEFAULT_MAX_CONCURRENT_CALLS_PER_CONNECTION,
nc.getRpc().getMaxConcurrentCallsPerConnection());
}

@Test
public void testRpcNegativeConcurrentCallsRejected() {
Config config = withRef(
"node { rpc { maxConcurrentCallsPerConnection = -1 } }");

TronError exception = assertThrows(TronError.class,
() -> NodeConfig.fromConfig(config));

assertTrue(exception.getMessage().contains(
"node.rpc.maxConcurrentCallsPerConnection must be non-negative, got: -1"));
}

@Test
public void testRpcUserOverrideExplicitValues() {
Config config = withRef(
Expand Down
8 changes: 6 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ node {
port = 50051
solidityEnable = true
solidityPort = 50061
# Maximum concurrent calls per connection. 0 = no limit.
maxConcurrentCallsPerConnection = 0
# Maximum concurrent calls per connection. 0 uses the secure default of 100.
maxConcurrentCallsPerConnection = 100
# Idle connection timeout (ms). 0 = no limit.
maxConnectionIdleInMillis = 0
# Minimum active connections required before broadcasting transactions.
Expand All @@ -114,6 +114,10 @@ node {
}
```

> **Upgrade note:** `node.rpc.maxConcurrentCallsPerConnection = 0` previously meant no limit.
> It now selects the secure default of 100. Configure an explicit positive value if a client
> needs more than 100 concurrent calls on one connection.

To disable an API endpoint that you do not want to expose publicly, set its `Enable` flag to `false` or add endpoints to `node.disabledApi`:

```hocon
Expand Down
4 changes: 4 additions & 0 deletions framework/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ dependencies {
// end local libraries
implementation group: 'com.beust', name: 'jcommander', version: '1.78'
implementation group: 'io.dropwizard.metrics', name: 'metrics-core', version: '3.1.2'
implementation('io.netty:netty-codec-protobuf:4.2.15.Final') {
exclude group: 'com.google.protobuf'
exclude group: 'com.google.protobuf.nano'
}
// http
implementation 'org.eclipse.jetty:jetty-server:9.4.58.v20250814'
implementation 'org.eclipse.jetty:jetty-servlet:9.4.58.v20250814'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* java-tron is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* java-tron is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with java-tron. If not, see <http://www.gnu.org/licenses/>.
*/

package org.tron.common.application;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import io.grpc.netty.GrpcHttp2ConnectionHandler;
import io.grpc.netty.InternalProtocolNegotiator;
import io.grpc.netty.InternalProtocolNegotiators;
import io.grpc.netty.NettyServerBuilder;
import io.netty.channel.ChannelHandler;
import io.netty.util.AsciiString;

/** Enforces the advertised HTTP/2 concurrent stream limit for grpc-netty servers. */
final class GrpcNettyMaxConcurrentStreamsLimiter {

private GrpcNettyMaxConcurrentStreamsLimiter() {
}

static NettyServerBuilder configurePlaintext(
NettyServerBuilder builder, int maxConcurrentStreams) {
checkNotNull(builder, "builder");
checkArgument(maxConcurrentStreams > 0, "maxConcurrentStreams must be positive");
builder.maxConcurrentCallsPerConnection(maxConcurrentStreams);
// TODO: Remove this shim after https://github.com/grpc/grpc-java/issues/12930 is fixed.
return builder.protocolNegotiator(newPlaintextNegotiator(maxConcurrentStreams));
}

static InternalProtocolNegotiator.ProtocolNegotiator newPlaintextNegotiator(
int maxConcurrentStreams) {
checkArgument(maxConcurrentStreams > 0, "maxConcurrentStreams must be positive");
return new EnforcingProtocolNegotiator(
InternalProtocolNegotiators.serverPlaintext(), maxConcurrentStreams);
}

private static final class EnforcingProtocolNegotiator
implements InternalProtocolNegotiator.ProtocolNegotiator {

private final InternalProtocolNegotiator.ProtocolNegotiator delegate;
private final int maxConcurrentStreams;

private EnforcingProtocolNegotiator(
InternalProtocolNegotiator.ProtocolNegotiator delegate, int maxConcurrentStreams) {
this.delegate = checkNotNull(delegate, "delegate");
this.maxConcurrentStreams = maxConcurrentStreams;
}

@Override
public AsciiString scheme() {
return delegate.scheme();
}

@Override
public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
// grpc-java builds the connection directly, bypassing Netty's builder-side enforcement.
grpcHandler.connection().remote().maxActiveStreams(maxConcurrentStreams);
return delegate.newHandler(grpcHandler);
}

@Override
public void close() {
delegate.close();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ protected NettyServerBuilder initServerBuilder() {
serverBuilder = serverBuilder.executor(this.executorService);
}
// Set configs from config.conf or default value
serverBuilder = GrpcNettyMaxConcurrentStreamsLimiter.configurePlaintext(
serverBuilder, parameter.getMaxConcurrentCallsPerConnection());
serverBuilder
.maxConcurrentCallsPerConnection(parameter.getMaxConcurrentCallsPerConnection())
.flowControlWindow(parameter.getFlowControlWindow())
.maxConnectionIdle(parameter.getMaxConnectionIdleInMillis(), TimeUnit.MILLISECONDS)
.maxConnectionAge(parameter.getMaxConnectionAgeInMillis(), TimeUnit.MILLISECONDS)
Expand Down
49 changes: 36 additions & 13 deletions framework/src/main/java/org/tron/core/services/http/JsonFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
import org.tron.common.utils.Commons;
import org.tron.common.utils.StringUtil;
import org.tron.core.Constant;
import org.tron.json.JSON;
import org.tron.protos.contract.BalanceContract;

/**
Expand Down Expand Up @@ -851,29 +850,27 @@ static String escapeBytes(ByteString input) {
return ByteArray.toHexString(input.toByteArray());
}

static String escapeBytes(ByteString input, final String fliedName, boolean selfType) {
static String escapeBytes(ByteString input, final String fieldName, boolean selfType) {
if (!selfType) {
return ByteArray.toHexString(input.toByteArray());
} else {
return escapeBytesSelfType(input, fliedName);
return escapeBytesSelfType(input, fieldName);
}
}

static String escapeBytesSelfType(ByteString input, final String fliedName) {
static String escapeBytesSelfType(ByteString input, final String fieldName) {
//Address
if (HttpSelfFormatFieldName.isAddressFormat(fliedName)) {
if (HttpSelfFormatFieldName.isAddressFormat(fieldName)) {
return StringUtil.encode58Check(input.toByteArray());
}
//Normal String
if (HttpSelfFormatFieldName.isNameStringFormat(fliedName)) {
String result = new String(input.toByteArray());
result = result.replaceAll("\"", "\\\\\"");
try {
JSON.parseObject("{\"key\":\"" + result + "\"}");
return result;
} catch (Exception e) {
if (HttpSelfFormatFieldName.isNameStringFormat(fieldName)) {
// Preserve arbitrary bytes losslessly as hex instead of decoding malformed UTF-8 with
// the platform default charset.
if (!input.isValidUtf8()) {
return ByteArray.toHexString(input.toByteArray());
}
return escapeNameStringText(input.toStringUtf8());
}
//HEX
return ByteArray.toHexString(input.toByteArray());
Expand Down Expand Up @@ -904,6 +901,29 @@ static ByteString unescapeBytes(CharSequence input) throws InvalidEscapeSequence
// Some of these methods are package-private because Descriptors.java uses
// them.

/**
* Escapes a valid UTF-8 name-string without exposing it to the U+FFFF sentinel used by
* {@link StringCharacterIterator}. Keeping this workaround on the new bytes path avoids
* changing the established behavior of {@link #escapeText(String)} for proto string fields.
*/
private static String escapeNameStringText(String input) {
int index = input.indexOf(Character.MAX_VALUE);
if (index < 0) {
return escapeText(input);
}

StringBuilder result = new StringBuilder(input.length());
int start = 0;
while (index >= 0) {
result.append(escapeText(input.substring(start, index)));
result.append(Character.MAX_VALUE);
start = index + 1;
index = input.indexOf(Character.MAX_VALUE, start);
}
result.append(escapeText(input.substring(start)));
return result.toString();
}

/**
* Implements JSON string escaping as specified <a href="http://www.ietf.org/rfc/rfc4627.txt">here</a>.
* <ul> <li>The following characters are escaped by prefixing them with a '\' :
Expand Down Expand Up @@ -1004,6 +1024,9 @@ static String unescapeText(String input) throws InvalidEscapeSequence {
case '\\':
builder.append('\\');
break;
case '/':
builder.append('/');
break;
case '"':
builder.append('\"');
break;
Expand Down Expand Up @@ -1358,7 +1381,7 @@ static ByteString unescapeBytesSelfType(String input, final String fliedName)

//Normal String -> ByteString
if (HttpSelfFormatFieldName.isNameStringFormat(fliedName)) {
return ByteString.copyFromUtf8(input);
return ByteString.copyFromUtf8(unescapeText(input));
}

return unescapeBytes(input);
Expand Down
6 changes: 3 additions & 3 deletions framework/src/main/java/org/tron/program/Version.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

public class Version {

public static final String VERSION_NAME = "GreatVoyage-v4.8.1.1-173-gaced0d5654";
public static final String VERSION_CODE = "18817";
private static final String VERSION = "4.8.2";
public static final String VERSION_NAME = "GreatVoyage-v4.8.2-6-g348db25bfd";
public static final String VERSION_CODE = "18825";
private static final String VERSION = "4.8.2.1";

public static String getVersion() {
return VERSION;
Expand Down
Loading
Loading