From c8b53b6c3cd7b5a5b55c2f615be2311c97fa676d Mon Sep 17 00:00:00 2001
From: jiangyuanshu <317787106@qq.com>
Date: Tue, 5 May 2026 18:05:38 +0800
Subject: [PATCH 1/6] optimize config file
---
.../org/tron/core/config/BeanDefaults.java | 93 ++
.../org/tron/core/config/Configuration.java | 5 +-
.../tron/core/config/args/BlockConfig.java | 8 +-
.../core/config/args/CommitteeConfig.java | 9 +-
.../tron/core/config/args/EventConfig.java | 13 +-
.../tron/core/config/args/GenesisConfig.java | 8 +-
.../tron/core/config/args/MetricsConfig.java | 11 +-
.../org/tron/core/config/args/NodeConfig.java | 15 +-
.../core/config/args/RateLimiterConfig.java | 8 +-
.../tron/core/config/args/StorageConfig.java | 12 +-
.../org/tron/core/config/args/VmConfig.java | 11 +-
common/src/main/resources/reference.conf | 835 ------------------
.../tron/core/config/BeanDefaultsTest.java | 216 +++++
framework/src/main/resources/config.conf | 101 ++-
.../org/tron/core/config/args/ArgsTest.java | 18 +-
15 files changed, 455 insertions(+), 908 deletions(-)
create mode 100644 common/src/main/java/org/tron/core/config/BeanDefaults.java
delete mode 100644 common/src/main/resources/reference.conf
create mode 100644 common/src/test/java/org/tron/core/config/BeanDefaultsTest.java
diff --git a/common/src/main/java/org/tron/core/config/BeanDefaults.java b/common/src/main/java/org/tron/core/config/BeanDefaults.java
new file mode 100644
index 00000000000..4c279cee7c2
--- /dev/null
+++ b/common/src/main/java/org/tron/core/config/BeanDefaults.java
@@ -0,0 +1,93 @@
+package org.tron.core.config;
+
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigFactory;
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Generates a Typesafe {@link Config} from a bean instance's current field values.
+ *
+ *
Used by each {@code XxxConfig.fromConfig()} to replace the role that
+ * {@code reference.conf} played: ensures every key ConfigBeanFactory needs is
+ * present, so a partial user config works without throwing
+ * {@code ConfigException.Missing}.
+ *
+ *
Only public getter+setter pairs (standard JavaBean properties) are included —
+ * the same set that {@code ConfigBeanFactory.create()} auto-binds. Keys are
+ * decapitalized exactly as ConfigBeanFactory does:
+ * {@code Character.toLowerCase(name.charAt(0)) + name.substring(1)}.
+ *
+ *
Nested bean fields are recursed into nested HOCON objects.
+ * {@code List} fields are serialized as HOCON lists (empty by default).
+ * Fields with no public setter (e.g. {@code @Getter(AccessLevel.NONE)} overrides)
+ * are automatically skipped — these are handled manually in each
+ * {@code fromConfig()} via {@code hasPath} guards.
+ */
+public final class BeanDefaults {
+
+ private BeanDefaults() {}
+
+ /**
+ * Convert {@code bean}'s public JavaBean properties to a Typesafe Config.
+ * The resulting Config can be used as a {@code withFallback()} for a user's
+ * config section to guarantee all keys are present for ConfigBeanFactory.
+ */
+ public static Config toConfig(Object bean) {
+ return ConfigFactory.parseMap(toMap(bean));
+ }
+
+ private static Map toMap(Object bean) {
+ Map map = new LinkedHashMap<>();
+ try {
+ BeanInfo info = Introspector.getBeanInfo(bean.getClass());
+ for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
+ Method getter = pd.getReadMethod();
+ Method setter = pd.getWriteMethod();
+ // Skip read-only properties (no setter) — matches ConfigBeanFactory's contract
+ if (getter == null || setter == null) {
+ continue;
+ }
+ // Use the property name exactly as Introspector produced it.
+ // ConfigBeanFactory does configProps.get(beanProp.getName()) — the lookup key
+ // is the property name verbatim, not decapitalized. For ordinary camelCase
+ // setters (setMaxConnections → "MaxConnections" → decapitalize → "maxConnections")
+ // Introspector already returns the lowercase form. For setters that start with
+ // two consecutive uppercase letters (setPBFTEnable → "PBFTEnable") the JavaBean
+ // spec forbids decapitalization, so pd.getName() == "PBFTEnable" — matching the
+ // capital-P key that config.conf uses for those fields.
+ String key = pd.getName();
+ Object value = getter.invoke(bean);
+ map.put(key, toValue(value));
+ }
+ } catch (Exception ignored) {
+ // Best-effort: any unresolvable field is simply omitted.
+ // ConfigBeanFactory will throw with a clear path if a required key is missing.
+ }
+ return map;
+ }
+
+ private static Object toValue(Object value) {
+ if (value == null) {
+ return "";
+ }
+ if (value instanceof Boolean || value instanceof Number || value instanceof String) {
+ return value;
+ }
+ if (value instanceof List) {
+ List list = new ArrayList<>();
+ for (Object item : (List>) value) {
+ list.add(toValue(item));
+ }
+ return list;
+ }
+ // Assume nested bean — recurse so it becomes a nested HOCON object.
+ return toMap(value);
+ }
+}
diff --git a/common/src/main/java/org/tron/core/config/Configuration.java b/common/src/main/java/org/tron/core/config/Configuration.java
index 80735290b8c..9870f56a194 100644
--- a/common/src/main/java/org/tron/core/config/Configuration.java
+++ b/common/src/main/java/org/tron/core/config/Configuration.java
@@ -48,10 +48,11 @@ public static com.typesafe.config.Config getByFileName(
private static void resolveConfigFile(String fileName, File confFile) {
if (confFile.exists()) {
- config = ConfigFactory.parseFile(confFile)
- .withFallback(ConfigFactory.defaultReference());
+ config = ConfigFactory.parseFile(confFile);
} else if (Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)
!= null) {
+ // ConfigFactory.load merges system properties (higher priority than the file),
+ // which tests rely on to override storage.db.engine via -D flags.
config = ConfigFactory.load(fileName);
} else {
throw new IllegalArgumentException(
diff --git a/common/src/main/java/org/tron/core/config/args/BlockConfig.java b/common/src/main/java/org/tron/core/config/args/BlockConfig.java
index 4746f390e0c..6ade2b00068 100644
--- a/common/src/main/java/org/tron/core/config/args/BlockConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/BlockConfig.java
@@ -7,6 +7,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import org.tron.core.config.BeanDefaults;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -25,8 +26,6 @@ public class BlockConfig {
private long proposalExpireTime = DEFAULT_PROPOSAL_EXPIRE_TIME;
private int checkFrozenTime = 1;
- // Defaults come from reference.conf (loaded globally via Configuration.java)
-
/**
* Create BlockConfig from the "block" section of the application config.
* Also checks that committee.proposalExpireTime is not used (must use block.proposalExpireTime).
@@ -38,7 +37,10 @@ public static BlockConfig fromConfig(Config config) {
+ "config.conf, please set the value in block.proposalExpireTime.", PARAMETER_INIT);
}
- Config blockSection = config.getConfig("block");
+ Config defaults = BeanDefaults.toConfig(new BlockConfig());
+ Config blockSection = config.hasPath("block")
+ ? config.getConfig("block").withFallback(defaults)
+ : defaults;
BlockConfig blockConfig = ConfigBeanFactory.create(blockSection, BlockConfig.class);
blockConfig.postProcess();
return blockConfig;
diff --git a/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java b/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java
index 0f94e7a59eb..c2ad56f227c 100644
--- a/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import org.tron.core.config.BeanDefaults;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -87,8 +88,6 @@ public class CommitteeConfig {
// proposalExpireTime is NOT a committee field — it's in block.* and handled by BlockConfig
- // Defaults come from reference.conf (loaded globally via Configuration.java)
-
/**
* Create CommitteeConfig from the "committee" section of the application config.
*
@@ -100,8 +99,10 @@ public class CommitteeConfig {
private static final String ALLOW_PBFT_KEY = "allowPBFT";
public static CommitteeConfig fromConfig(Config config) {
- Config section = config.getConfig("committee");
-
+ Config defaults = BeanDefaults.toConfig(new CommitteeConfig());
+ Config section = config.hasPath("committee")
+ ? config.getConfig("committee").withFallback(defaults)
+ : defaults;
CommitteeConfig cc = ConfigBeanFactory.create(section, CommitteeConfig.class);
// Ensure the manually-named fields get the right values from the original keys
cc.allowPBFT = section.hasPath(ALLOW_PBFT_KEY) ? section.getLong(ALLOW_PBFT_KEY) : 0;
diff --git a/common/src/main/java/org/tron/core/config/args/EventConfig.java b/common/src/main/java/org/tron/core/config/args/EventConfig.java
index ac1731de2dc..42c03a04b8f 100644
--- a/common/src/main/java/org/tron/core/config/args/EventConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/EventConfig.java
@@ -3,6 +3,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
import com.typesafe.config.ConfigFactory;
+import org.tron.core.config.BeanDefaults;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
@@ -68,8 +69,6 @@ public static class FilterConfig {
private List contractTopic = new ArrayList<>();
}
- // Defaults come from reference.conf (loaded globally via Configuration.java)
-
/**
* Create EventConfig from the "event.subscribe" section of the application config.
*
@@ -77,14 +76,20 @@ public static class FilterConfig {
* "nativeQueue" but config key is "native". We handle this manually after binding.
*/
public static EventConfig fromConfig(Config config) {
- Config section = config.getConfig("event.subscribe");
+ // BeanDefaults covers enable/version/startSyncBlockNum/path/server/dbconfig/
+ // contractParse/filter. nativeQueue and topics are excluded (no public setter).
+ Config defaults = BeanDefaults.toConfig(new EventConfig());
+ Config section = config.hasPath("event.subscribe")
+ ? config.getConfig("event.subscribe")
+ : ConfigFactory.empty();
// "native" is a Java reserved word, "topics" has optional fields per item —
// strip both before binding, read manually
String nativeKey = "native";
String topicsKey = "topics";
Config bindable = section.withoutPath(nativeKey).withoutPath(topicsKey)
- .withoutPath("topicDefaults");
+ .withoutPath("topicDefaults")
+ .withFallback(defaults);
EventConfig ec = ConfigBeanFactory.create(bindable, EventConfig.class);
// manually bind "native" sub-section
diff --git a/common/src/main/java/org/tron/core/config/args/GenesisConfig.java b/common/src/main/java/org/tron/core/config/args/GenesisConfig.java
index a17e06d5c0f..74d4338bbb5 100644
--- a/common/src/main/java/org/tron/core/config/args/GenesisConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/GenesisConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import org.tron.core.config.BeanDefaults;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
@@ -41,10 +42,11 @@ public static class WitnessConfig {
private long voteCount = 0;
}
- // Defaults come from reference.conf (loaded globally via Configuration.java)
-
public static GenesisConfig fromConfig(Config config) {
- Config section = config.getConfig("genesis.block");
+ Config defaults = BeanDefaults.toConfig(new GenesisConfig());
+ Config section = config.hasPath("genesis.block")
+ ? config.getConfig("genesis.block").withFallback(defaults)
+ : defaults;
return ConfigBeanFactory.create(section, GenesisConfig.class);
}
}
diff --git a/common/src/main/java/org/tron/core/config/args/MetricsConfig.java b/common/src/main/java/org/tron/core/config/args/MetricsConfig.java
index 5b504acdd1c..98d5c0a36d7 100644
--- a/common/src/main/java/org/tron/core/config/args/MetricsConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/MetricsConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import org.tron.core.config.BeanDefaults;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -35,13 +36,11 @@ public static class InfluxDbConfig {
private int metricsReportInterval = 10;
}
- // Defaults come from reference.conf (loaded globally via Configuration.java)
-
- /**
- * Create MetricsConfig from the "node.metrics" section of the application config.
- */
public static MetricsConfig fromConfig(Config config) {
- Config section = config.getConfig("node.metrics");
+ Config defaults = BeanDefaults.toConfig(new MetricsConfig());
+ Config section = config.hasPath("node.metrics")
+ ? config.getConfig("node.metrics").withFallback(defaults)
+ : defaults;
return ConfigBeanFactory.create(section, MetricsConfig.class);
}
}
diff --git a/common/src/main/java/org/tron/core/config/args/NodeConfig.java b/common/src/main/java/org/tron/core/config/args/NodeConfig.java
index c3305e976de..4b151d133e5 100644
--- a/common/src/main/java/org/tron/core/config/args/NodeConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/NodeConfig.java
@@ -5,6 +5,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
import com.typesafe.config.ConfigFactory;
+import org.tron.core.config.BeanDefaults;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
@@ -89,8 +90,8 @@ public class NodeConfig {
private double activeConnectFactor = 0.1;
private double connectFactor = 0.6;
// Legacy alias `maxActiveNodesWithSameIp` has no bean field: we only peek at it via
- // section.hasPath() below. Keeping it field-less means reference.conf doesn't have to
- // ship a default that would otherwise mask the modern `maxConnectionsWithSameIp` key.
+ // section.hasPath() below. Keeping it field-less means BeanDefaults does not emit a
+ // default that would mask the modern `maxConnectionsWithSameIp` key.
// ---- Sub-beans matching config's dot-notation nested structure ----
private ListenConfig listen = new ListenConfig();
@@ -339,8 +340,6 @@ public static class DnsConfig {
private String awsHostZoneId = "";
}
- // Defaults come from reference.conf (loaded globally via Configuration.java)
-
// ===========================================================================
// Factory method
// ===========================================================================
@@ -359,8 +358,10 @@ public static class DnsConfig {
* since ConfigBeanFactory expects typed bean lists, not string lists.
*/
public static NodeConfig fromConfig(Config config) {
- Config section = config.getConfig("node");
-
+ Config defaults = BeanDefaults.toConfig(new NodeConfig());
+ Config section = config.hasPath("node")
+ ? config.getConfig("node").withFallback(defaults)
+ : defaults;
// Auto-bind all fields and sub-beans. ConfigBeanFactory fails fast with a
// descriptive path on any `= null` value — external configs that use the
// HOCON null keyword should fix their config rather than rely on silent coercion.
@@ -386,7 +387,7 @@ public static NodeConfig fromConfig(Config config) {
}
// Legacy key fallback: node.fullNodeAllowShieldedTransaction -> allowShieldedTransactionApi.
- // reference.conf does not ship the legacy key, so hasPath here reliably means the user
+ // BeanDefaults does not emit this legacy key, so hasPath here reliably means the user
// set it in their config. When present, it overrides the modern key.
if (section.hasPath("fullNodeAllowShieldedTransaction")) {
nc.allowShieldedTransactionApi = section.getBoolean("fullNodeAllowShieldedTransaction");
diff --git a/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java b/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
index eed5ef1898b..8fda4fba49c 100644
--- a/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import org.tron.core.config.BeanDefaults;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
@@ -66,10 +67,11 @@ public static class RpcRateLimitItem {
private String paramString = "";
}
- // Defaults come from reference.conf (loaded globally via Configuration.java)
-
public static RateLimiterConfig fromConfig(Config config) {
- Config section = config.getConfig("rate.limiter");
+ Config defaults = BeanDefaults.toConfig(new RateLimiterConfig());
+ Config section = config.hasPath("rate.limiter")
+ ? config.getConfig("rate.limiter").withFallback(defaults)
+ : defaults;
return ConfigBeanFactory.create(section, RateLimiterConfig.class);
}
}
diff --git a/common/src/main/java/org/tron/core/config/args/StorageConfig.java b/common/src/main/java/org/tron/core/config/args/StorageConfig.java
index 2517f4d10d7..ad8a41d35d2 100644
--- a/common/src/main/java/org/tron/core/config/args/StorageConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/StorageConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import org.tron.core.config.BeanDefaults;
import com.typesafe.config.ConfigObject;
import java.util.ArrayList;
import java.util.List;
@@ -205,11 +206,14 @@ public static class PropertyConfig {
private int maxOpenFiles = 100;
}
- // Defaults come from reference.conf (loaded globally via Configuration.java)
-
public static StorageConfig fromConfig(Config config) {
- Config section = config.getConfig("storage");
-
+ Config defaults = BeanDefaults.toConfig(new StorageConfig());
+ // User's storage section takes priority; defaults fill in any omitted scalar keys.
+ // readDbOption() uses hasPath() on the merged section, so user-set optional keys
+ // (default, defaultM, defaultL) are still detected correctly.
+ Config section = config.hasPath("storage")
+ ? config.getConfig("storage").withFallback(defaults)
+ : defaults;
StorageConfig sc = ConfigBeanFactory.create(section, StorageConfig.class);
sc.rawStorageConfig = section;
diff --git a/common/src/main/java/org/tron/core/config/args/VmConfig.java b/common/src/main/java/org/tron/core/config/args/VmConfig.java
index d583cf4c601..a4b219f8726 100644
--- a/common/src/main/java/org/tron/core/config/args/VmConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/VmConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import org.tron.core.config.BeanDefaults;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -28,13 +29,11 @@ public class VmConfig {
private boolean saveFeaturedInternalTx = false;
private boolean saveCancelAllUnfreezeV2Details = false;
- /**
- * Create VmConfig from the "vm" section of the application config.
- * Defaults come from reference.conf (loaded globally via Configuration.java),
- * so no per-bean DEFAULTS needed.
- */
public static VmConfig fromConfig(Config config) {
- Config vmSection = config.getConfig("vm");
+ Config defaults = BeanDefaults.toConfig(new VmConfig());
+ Config vmSection = config.hasPath("vm")
+ ? config.getConfig("vm").withFallback(defaults)
+ : defaults;
VmConfig vmConfig = ConfigBeanFactory.create(vmSection, VmConfig.class);
vmConfig.postProcess();
return vmConfig;
diff --git a/common/src/main/resources/reference.conf b/common/src/main/resources/reference.conf
deleted file mode 100644
index 11970a0a673..00000000000
--- a/common/src/main/resources/reference.conf
+++ /dev/null
@@ -1,835 +0,0 @@
-# =============================================================================
-# reference.conf — Full default configuration for java-tron
-# =============================================================================
-#
-# This file defines the default value for every configuration parameter.
-# It is packaged inside the jar and loaded automatically via Typesafe Config's
-# standard mechanism: ConfigFactory.defaultReference().
-#
-# Loading priority (highest wins):
-# 1. User's external config file (e.g. config.conf passed via -c flag)
-# 2. This file (reference.conf, bundled in jar)
-#
-# When a user's config.conf omits a parameter, the value from this file is
-# used as the fallback. This ensures the node always has a complete and valid
-# configuration, even if the user only overrides a few parameters.
-#
-# Maintenance rules:
-# - Every parameter that the code reads must have an entry here
-# - Values must match the bean field initializers in the corresponding
-# XxxConfig.java classes (VmConfig, NodeConfig, CommitteeConfig, etc.)
-# - Keep the section order and key order identical to config.conf for
-# easy side-by-side comparison
-# - When adding a new parameter: add it here AND in the bean class
-#
-# Key naming rules (required for ConfigBeanFactory auto-binding):
-# - Use standard camelCase: maxConnections, syncFetchBatchNum, etc.
-#
-# Keys that cannot auto-bind (handled manually in bean fromConfig):
-#
-# 1. committee.pBFTExpireNum — lowercase "p" then uppercase "BFT":
-# setPBFTExpireNum -> property "PBFTExpireNum" (capital P),
-# mismatches config key "pBFTExpireNum" (lowercase p).
-#
-# 2. node.isOpenFullTcpDisconnect — boolean "is" prefix:
-# getter isOpenFullTcpDisconnect() -> property "openFullTcpDisconnect",
-# mismatches config key "isOpenFullTcpDisconnect".
-#
-# 3. node.shutdown.BlockTime/BlockHeight/BlockCount — PascalCase keys:
-# setBlockTime -> property "blockTime", mismatches "BlockTime".
-#
-# =============================================================================
-
-net {
- # type is deprecated and has no effect.
- # type = mainnet
-}
-
-storage {
- # Database engine: "LEVELDB" or "ROCKSDB" (ARM only supports ROCKSDB)
- db.engine = "LEVELDB"
- db.sync = false
- db.directory = "database"
-
- # Index directory (legacy, not consumed by any runtime code, kept for CLI/test compatibility)
- index.directory = "index"
- index.switch = "on"
-
- # Whether to write transaction result in transactionRetStore
- transHistory.switch = "on"
-
- # Per-database LevelDB option overrides. Default: empty (all databases use global defaults).
- # setting can improve leveldb performance .... start, deprecated for arm
- # node: if this will increase process fds, you may check your ulimit if 'too many open files' error occurs
- # see https://github.com/tronprotocol/tips/blob/master/tip-343.md for detail
- # if you find block sync has lower performance, you can try this settings
- # default = {
- # maxOpenFiles = 100
- # }
- # defaultM = {
- # maxOpenFiles = 500
- # }
- # defaultL = {
- # maxOpenFiles = 1000
- # }
- # setting can improve leveldb performance .... end, deprecated for arm
-
- # Example per-database overrides:
- # {
- # name = "account",
- # path = "storage_directory_test",
- # createIfMissing = true,
- # paranoidChecks = true,
- # verifyChecksums = true,
- # compressionType = 1, // compressed with snappy
- # blockSize = 4096, // 4 KB = 4 * 1024 B
- # writeBufferSize = 10485760, // 10 MB = 10 * 1024 * 1024 B
- # cacheSize = 10485760, // 10 MB = 10 * 1024 * 1024 B
- # maxOpenFiles = 100
- # }
- properties = []
-
- needToUpdateAsset = true
-
- # RocksDB settings (only used when db.engine = "ROCKSDB")
- # Strongly recommend NOT modifying unless you know every item's meaning clearly.
- dbSettings = {
- levelNumber = 7
- compactThreads = 0 // 0 = auto: max(availableProcessors, 1)
- blocksize = 16 // n * KB
- maxBytesForLevelBase = 256 // n * MB
- maxBytesForLevelMultiplier = 10
- level0FileNumCompactionTrigger = 2
- targetFileSizeBase = 64 // n * MB
- targetFileSizeMultiplier = 1
- maxOpenFiles = 5000
- }
-
- balance.history.lookup = false
-
- # Checkpoint version for snapshot mechanism. Version 2 enables V2 snapshot.
- checkpoint.version = 1
- checkpoint.sync = true
-
- # Estimated number of block transactions (default 1000, min 100, max 10000).
- # Total cached transactions = 65536 * txCache.estimatedTransactions
- txCache.estimatedTransactions = 1000
- # If true, transaction cache initialization will be faster.
- txCache.initOptimization = false
-
- # Number of blocks flushed to db in each batch during node syncing.
- snapshot.maxFlushCount = 1
-
- # Database backup settings (RocksDB only)
- backup = {
- enable = false
- propPath = "prop.properties"
- bak1path = "bak1/database/"
- bak2path = "bak2/database/"
- frequency = 10000
- }
-
- # Data root setting, for check data, currently only reward-vi is used.
- # merkleRoot = {
- # reward-vi = 9debcb9924055500aaae98cdee10501c5c39d4daa75800a996f4bdda73dbccd8 // main-net
- # }
-}
-
-node.discovery = {
- enable = false
- persist = false
- external.ip = ""
-}
-
-# Custom stop condition
-# node.shutdown = {
-# BlockTime = "54 59 08 * * ?" # if block header time in persistent db matched
-# BlockHeight = 33350800 # if block header height in persistent db matched
-# BlockCount = 12 # block sync count after node start
-# }
-
-node.backup {
- port = 10001
- priority = 0
- keepAliveInterval = 3000
- members = [
- # "ip",
- # "ip"
- ]
-}
-
-# Algorithm for generating public key from private key. Do not modify to avoid forks.
-crypto {
- engine = "eckey"
-}
-
-# Energy limit block number (config key has typo "enery" preserved for backward compatibility)
-enery.limit.block.num = 4727890
-
-# Actuator whitelist — empty means all actuators allowed
-actuator {
- whitelist = []
-}
-
-node.metrics = {
- prometheus {
- enable = false
- port = 9527
- }
-
- storageEnable = false
-
- influxdb {
- ip = ""
- port = 8086
- database = "metrics"
- metricsReportInterval = 10
- }
-}
-
-node {
- # Trust node for solidity node (example: "127.0.0.1:50051").
- # Empty string here = "not configured"; Args.java bridge converts "" → null so the
- # runtime behavior matches develop (trustNodeAddr is null unless user sets the key).
- trustNode = ""
-
- # Expose extension api to public or not
- walletExtensionApi = false
-
- listen.port = 18888
- connection.timeout = 2
- fetchBlock.timeout = 500
-
- # Number of blocks to fetch in one batch during sync. Range: [100, 2000].
- syncFetchBatchNum = 2000
-
- # Number of validate sign threads, default availableProcessors
- # Number of validate sign threads, 0 = auto (availableProcessors)
- validateSignThreadNum = 0
-
- maxConnections = 30
- minConnections = 8
- minActiveConnections = 3
- maxConnectionsWithSameIp = 2
- maxHttpConnectNumber = 50
- minParticipationRate = 0
-
- # Whether to enable shielded transaction API
- allowShieldedTransactionApi = true
-
- # Whether to print config log at startup
- openPrintLog = true
-
- # If true, SR packs transactions into a block in descending order of fee;
- # otherwise, packs by receive timestamp.
- openTransactionSort = false
-
- # Threshold for broadcast transactions received from each peer per second,
- # transactions exceeding this are discarded
- maxTps = 1000
-
- isOpenFullTcpDisconnect = false
- inactiveThreshold = 600 // seconds
- tcpNettyWorkThreadNum = 0
- udpNettyWorkThreadNum = 1
- maxFastForwardNum = 4
- activeConnectFactor = 0.1
- connectFactor = 0.6
- # Legacy alias `maxActiveNodesWithSameIp` is still accepted from user config
- # (see NodeConfig alias-fallback) but is intentionally NOT defaulted here —
- # shipping it in reference.conf would always mask the modern `maxConnectionsWithSameIp`.
- channel.read.timeout = 0
- metricsEnable = false
-
- p2p {
- version = 11111 # Mainnet:11111; Nile:201910292; Shasta:1
- }
-
- active = [
- # Active establish connection in any case
- # "ip:port",
- # "ip:port"
- ]
-
- passive = [
- # Passive accept connection in any case
- # "ip:port",
- # "ip:port"
- ]
-
- fastForward = [
- "100.27.171.62:18888",
- "15.188.6.125:18888"
- ]
-
- http {
- fullNodeEnable = true
- fullNodePort = 8090
- solidityEnable = true
- solidityPort = 8091
- PBFTEnable = true
- PBFTPort = 8092
- }
-
- rpc {
- enable = true
- port = 50051
- solidityEnable = true
- solidityPort = 50061
- PBFTEnable = true
- PBFTPort = 50071
-
- # Number of gRPC threads, 0 = auto (availableProcessors / 2)
- thread = 0
-
- # Maximum concurrent calls per incoming connection
- # No limit on concurrent calls per connection
- maxConcurrentCallsPerConnection = 2147483647
-
- # HTTP/2 flow control window (bytes), default 1MB
- flowControlWindow = 1048576
-
- # Connection idle timeout (ms). No limit by default.
- maxConnectionIdleInMillis = 9223372036854775807
-
- # Connection max age (ms). No limit by default.
- maxConnectionAgeInMillis = 9223372036854775807
-
- # Maximum message size (bytes), default 4MB
- maxMessageSize = 4194304
-
- # Maximum header list size (bytes), default 8192
- maxHeaderListSize = 8192
-
- # RST_STREAM frames allowed per connection per period, 0 = no limit
- maxRstStream = 0
-
- # Seconds per period for gRPC RST_STREAM limit
- secondsPerWindow = 0
-
- # Minimum effective connections required to broadcast transactions
- minEffectiveConnection = 1
-
- # Reflection service switch for grpcurl tool
- reflectionService = false
- trxCacheEnable = false
- }
-
- # Number of solidity threads in FullNode.
- # Increase if solidity rpc/http interface timeouts occur.
- # Default: number of cpu cores.
- # Number of solidity threads, 0 = auto (availableProcessors)
- solidity.threads = 0
-
- # Maximum percentage of producing block interval (provides time for broadcast etc.)
- blockProducedTimeOut = 50
-
- # Maximum transactions from network layer per second
- netMaxTrxPerSecond = 700
-
- # Whether to enable node detection function
- nodeDetectEnable = false
-
- # Use IPv6 address for node discovery and TCP connection
- enableIpv6 = false
-
- # If node's highest block is below all peers, try to acquire new connection
- effectiveCheckEnable = false
-
- # Dynamic loading configuration function
- dynamicConfig = {
- enable = false
- checkInterval = 600
- }
-
- # Block solidification check
- unsolidifiedBlockCheck = false
- maxUnsolidifiedBlocks = 54
- blockCacheTimeout = 60
-
- # TCP and transaction limits
- receiveTcpMinDataLength = 2048
- maxTransactionPendingSize = 2000
- pendingTransactionTimeout = 60000
-
- # Consensus agreement
- agreeNodeCount = 0
-
- # Shielded transaction (ZK)
- zenTokenId = "000000"
- shieldedTransInPendingMaxCounts = 10
-
- # Contract proto validation thread pool (0 = auto: availableProcessors)
- validContractProto.threads = 0
-
- dns {
- treeUrls = [
- # "tree://AKMQMNAJJBL73LXWPXDI4I5ZWWIZ4AWO34DWQ636QOBBXNFXH3LQS@main.trondisco.net",
- ]
- publish = false
- dnsDomain = ""
- dnsPrivate = ""
- knownUrls = []
- staticNodes = []
- maxMergeSize = 0
- changeThreshold = 0.0
- serverType = ""
- accessKeyId = ""
- accessKeySecret = ""
- aliyunDnsEndpoint = ""
- awsRegion = ""
- awsHostZoneId = ""
- }
-
- # Open history query APIs on lite FullNode (may return null for some queries)
- openHistoryQueryWhenLiteFN = false
-
- jsonrpc {
- httpFullNodeEnable = false
- httpFullNodePort = 8545
- httpSolidityEnable = false
- httpSolidityPort = 8555
- httpPBFTEnable = false
- httpPBFTPort = 8565
-
- # Maximum blocks range for eth_getLogs, >0 otherwise no limit
- maxBlockRange = 5000
-
- # Maximum topics within a topic criteria, >0 otherwise no limit
- maxSubTopics = 1000
-
- # Maximum number for blockFilter
- maxBlockFilterNum = 50000
- }
-
- # Disabled API list (works for http, rpc and pbft, not jsonrpc). Case insensitive.
- disabledApi = [
- # "getaccount",
- # "getnowblock2"
- ]
-}
-
-## Rate limiter config
-rate.limiter = {
- # Strategies: GlobalPreemptibleAdapter, QpsRateLimiterAdapter, IPQPSRateLimiterAdapter
- # Default: QpsRateLimiterAdapter with qps=1000
-
- http = [
- # {
- # component = "GetNowBlockServlet",
- # strategy = "GlobalPreemptibleAdapter",
- # paramString = "permit=1"
- # },
- # {
- # component = "GetAccountServlet",
- # strategy = "IPQPSRateLimiterAdapter",
- # paramString = "qps=1"
- # },
- # {
- # component = "ListWitnessesServlet",
- # strategy = "QpsRateLimiterAdapter",
- # paramString = "qps=1"
- # }
- ]
-
- rpc = [
- # {
- # component = "protocol.Wallet/GetBlockByLatestNum2",
- # strategy = "GlobalPreemptibleAdapter",
- # paramString = "permit=1"
- # },
- # {
- # component = "protocol.Wallet/GetAccount",
- # strategy = "IPQPSRateLimiterAdapter",
- # paramString = "qps=1"
- # },
- # {
- # component = "protocol.Wallet/ListWitnesses",
- # strategy = "QpsRateLimiterAdapter",
- # paramString = "qps=1"
- # }
- ]
-
- p2p = {
- syncBlockChain = 3.0
- fetchInvData = 3.0
- disconnect = 1.0
- }
-
- global.qps = 50000
- global.ip.qps = 10000
- global.api.qps = 1000
-}
-
-seed.node = {
- ip.list = [
- "3.225.171.164:18888",
- "52.8.46.215:18888",
- "3.79.71.167:18888",
- "108.128.110.16:18888",
- "18.133.82.227:18888",
- "35.180.81.133:18888",
- "13.210.151.5:18888",
- "18.231.27.82:18888",
- "3.12.212.122:18888",
- "52.24.128.7:18888",
- "15.207.144.3:18888",
- "3.39.38.55:18888",
- "54.151.226.240:18888",
- "35.174.93.198:18888",
- "18.210.241.149:18888",
- "54.177.115.127:18888",
- "54.254.131.82:18888",
- "18.167.171.167:18888",
- "54.167.11.177:18888",
- "35.74.7.196:18888",
- "52.196.244.176:18888",
- "54.248.129.19:18888",
- "43.198.142.160:18888",
- "3.0.214.7:18888",
- "54.153.59.116:18888",
- "54.153.94.160:18888",
- "54.82.161.39:18888",
- "54.179.207.68:18888",
- "18.142.82.44:18888",
- "18.163.230.203:18888",
- # "[2a05:d014:1f2f:2600:1b15:921:d60b:4c60]:18888", // use this if support ipv6
- # "[2600:1f18:7260:f400:8947:ebf3:78a0:282b]:18888", // use this if support ipv6
- ]
-}
-
-genesis.block = {
- assets = [
- {
- accountName = "Zion"
- accountType = "AssetIssue"
- address = "TLLM21wteSPs4hKjbxgmH1L6poyMjeTbHm"
- balance = "99000000000000000"
- },
- {
- accountName = "Sun"
- accountType = "AssetIssue"
- address = "TXmVpin5vq5gdZsciyyjdZgKRUju4st1wM"
- balance = "0"
- },
- {
- accountName = "Blackhole"
- accountType = "AssetIssue"
- address = "TLsV52sRDL79HXGGm9yzwKibb6BeruhUzy"
- balance = "-9223372036854775808"
- }
- ]
-
- witnesses = [
- {
- address: THKJYuUmMKKARNf7s2VT51g5uPY6KEqnat,
- url = "http://GR1.com",
- voteCount = 100000026
- },
- {
- address: TVDmPWGYxgi5DNeW8hXrzrhY8Y6zgxPNg4,
- url = "http://GR2.com",
- voteCount = 100000025
- },
- {
- address: TWKZN1JJPFydd5rMgMCV5aZTSiwmoksSZv,
- url = "http://GR3.com",
- voteCount = 100000024
- },
- {
- address: TDarXEG2rAD57oa7JTK785Yb2Et32UzY32,
- url = "http://GR4.com",
- voteCount = 100000023
- },
- {
- address: TAmFfS4Tmm8yKeoqZN8x51ASwdQBdnVizt,
- url = "http://GR5.com",
- voteCount = 100000022
- },
- {
- address: TK6V5Pw2UWQWpySnZyCDZaAvu1y48oRgXN,
- url = "http://GR6.com",
- voteCount = 100000021
- },
- {
- address: TGqFJPFiEqdZx52ZR4QcKHz4Zr3QXA24VL,
- url = "http://GR7.com",
- voteCount = 100000020
- },
- {
- address: TC1ZCj9Ne3j5v3TLx5ZCDLD55MU9g3XqQW,
- url = "http://GR8.com",
- voteCount = 100000019
- },
- {
- address: TWm3id3mrQ42guf7c4oVpYExyTYnEGy3JL,
- url = "http://GR9.com",
- voteCount = 100000018
- },
- {
- address: TCvwc3FV3ssq2rD82rMmjhT4PVXYTsFcKV,
- url = "http://GR10.com",
- voteCount = 100000017
- },
- {
- address: TFuC2Qge4GxA2U9abKxk1pw3YZvGM5XRir,
- url = "http://GR11.com",
- voteCount = 100000016
- },
- {
- address: TNGoca1VHC6Y5Jd2B1VFpFEhizVk92Rz85,
- url = "http://GR12.com",
- voteCount = 100000015
- },
- {
- address: TLCjmH6SqGK8twZ9XrBDWpBbfyvEXihhNS,
- url = "http://GR13.com",
- voteCount = 100000014
- },
- {
- address: TEEzguTtCihbRPfjf1CvW8Euxz1kKuvtR9,
- url = "http://GR14.com",
- voteCount = 100000013
- },
- {
- address: TZHvwiw9cehbMxrtTbmAexm9oPo4eFFvLS,
- url = "http://GR15.com",
- voteCount = 100000012
- },
- {
- address: TGK6iAKgBmHeQyp5hn3imB71EDnFPkXiPR,
- url = "http://GR16.com",
- voteCount = 100000011
- },
- {
- address: TLaqfGrxZ3dykAFps7M2B4gETTX1yixPgN,
- url = "http://GR17.com",
- voteCount = 100000010
- },
- {
- address: TX3ZceVew6yLC5hWTXnjrUFtiFfUDGKGty,
- url = "http://GR18.com",
- voteCount = 100000009
- },
- {
- address: TYednHaV9zXpnPchSywVpnseQxY9Pxw4do,
- url = "http://GR19.com",
- voteCount = 100000008
- },
- {
- address: TCf5cqLffPccEY7hcsabiFnMfdipfyryvr,
- url = "http://GR20.com",
- voteCount = 100000007
- },
- {
- address: TAa14iLEKPAetX49mzaxZmH6saRxcX7dT5,
- url = "http://GR21.com",
- voteCount = 100000006
- },
- {
- address: TBYsHxDmFaRmfCF3jZNmgeJE8sDnTNKHbz,
- url = "http://GR22.com",
- voteCount = 100000005
- },
- {
- address: TEVAq8dmSQyTYK7uP1ZnZpa6MBVR83GsV6,
- url = "http://GR23.com",
- voteCount = 100000004
- },
- {
- address: TRKJzrZxN34YyB8aBqqPDt7g4fv6sieemz,
- url = "http://GR24.com",
- voteCount = 100000003
- },
- {
- address: TRMP6SKeFUt5NtMLzJv8kdpYuHRnEGjGfe,
- url = "http://GR25.com",
- voteCount = 100000002
- },
- {
- address: TDbNE1VajxjpgM5p7FyGNDASt3UVoFbiD3,
- url = "http://GR26.com",
- voteCount = 100000001
- },
- {
- address: TLTDZBcPoJ8tZ6TTEeEqEvwYFk2wgotSfD,
- url = "http://GR27.com",
- voteCount = 100000000
- }
- ]
-
- timestamp = "0"
-
- parentHash = "0xe58f33f9baf9305dc6f82b9f1934ea8f0ade2defb951258d50167028c780351f"
-}
-
-# Optional. Used when the witness account has set witnessPermission.
-# localWitnessAccountAddress =
-
-localwitness = [
-]
-
-# localwitnesskeystore = [
-# "localwitnesskeystore.json"
-# ]
-
-block = {
- needSyncCheck = false
- maintenanceTimeInterval = 21600000 // 6 hours (ms)
- proposalExpireTime = 259200000 // 3 days (ms), controlled by committee proposal
- checkFrozenTime = 1 // maintenance periods to check frozen balance (test only)
-}
-
-# Transaction reference block: "solid" or "head". Default "solid". "head" may cause TaPos error.
-trx.reference.block = "solid"
-
-# Transaction expiration time in milliseconds.
-trx.expiration.timeInMilliseconds = 60000
-
-vm = {
- supportConstant = false
- maxEnergyLimitForConstant = 100000000
- minTimeRatio = 0.0
- maxTimeRatio = 5.0
- saveInternalTx = false
- lruCacheSize = 500
- vmTrace = false
-
- # Whether to store featured internal transactions (freeze, vote, etc.)
- saveFeaturedInternalTx = false
-
- # Whether to store details of CANCELALLUNFREEZEV2 opcode internal transactions
- saveCancelAllUnfreezeV2Details = false
-
- # Max execution time (ms) for re-executed transactions during packaging
- longRunningTime = 10
-
- # Whether to support estimate energy API
- estimateEnergy = false
-
- # Max retry time for executing transaction in estimating energy
- estimateEnergyMaxRetry = 3
-}
-
-# Governance proposal toggle parameters. All default to 0 (disabled).
-# Controlled by on-chain committee proposals, not manual configuration.
-# Setting them in config is only for private chain testing.
-committee = {
- allowCreationOfContracts = 0
- allowMultiSign = 0
- allowAdaptiveEnergy = 0
- allowDelegateResource = 0
- allowSameTokenName = 0
- allowTvmTransferTrc10 = 0
- allowTvmConstantinople = 0
- allowTvmSolidity059 = 0
- forbidTransferToContract = 0
- allowShieldedTRC20Transaction = 0
- allowTvmIstanbul = 0
- allowMarketTransaction = 0
- allowProtoFilterNum = 0
- allowAccountStateRoot = 0
- changedDelegation = 0
- allowPBFT = 0
- pBFTExpireNum = 20
- allowTransactionFeePool = 0
- allowBlackHoleOptimization = 0
- allowNewResourceModel = 0
- allowReceiptsMerkleRoot = 0
- allowTvmFreeze = 0
- allowTvmVote = 0
- unfreezeDelayDays = 0
- allowTvmLondon = 0
- allowTvmCompatibleEvm = 0
- allowHigherLimitForMaxCpuTimeOfOneTx = 0
- allowNewRewardAlgorithm = 0
- allowOptimizedReturnValueOfChainId = 0
- allowTvmShangHai = 0
- allowOldRewardOpt = 0
- allowEnergyAdjustment = 0
- allowStrictMath = 0
- consensusLogicOptimization = 0
- allowTvmCancun = 0
- allowTvmBlob = 0
- allowTvmOsaka = 0
- allowAccountAssetOptimization = 0
- allowAssetOptimization = 0
- allowNewReward = 0
- memoFee = 0
- allowDelegateOptimization = 0
- allowDynamicEnergy = 0
- dynamicEnergyThreshold = 0
- dynamicEnergyIncreaseFactor = 0
- dynamicEnergyMaxFactor = 0
-}
-
-event.subscribe = {
- enable = false
-
- native = {
- useNativeQueue = true
- bindport = 5555
- sendqueuelength = 1000
- }
-
- version = 0
- startSyncBlockNum = 0
- path = ""
- server = ""
- dbconfig = ""
- contractParse = true
-
- topics = [
- {
- triggerName = "block"
- enable = false
- topic = "block"
- solidified = false
- },
- {
- triggerName = "transaction"
- enable = false
- topic = "transaction"
- solidified = false
- ethCompatible = false
- },
- {
- triggerName = "contractevent"
- enable = false
- topic = "contractevent"
- },
- {
- triggerName = "contractlog"
- enable = false
- topic = "contractlog"
- redundancy = false
- },
- {
- triggerName = "solidity"
- enable = true
- topic = "solidity"
- },
- {
- triggerName = "solidityevent"
- enable = false
- topic = "solidityevent"
- },
- {
- triggerName = "soliditylog"
- enable = false
- topic = "soliditylog"
- redundancy = false
- }
- ]
-
- filter = {
- fromblock = ""
- toblock = ""
- contractAddress = [
- ""
- ]
- contractTopic = [
- ""
- ]
- }
-}
diff --git a/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java b/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java
new file mode 100644
index 00000000000..c4170d580c1
--- /dev/null
+++ b/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java
@@ -0,0 +1,216 @@
+package org.tron.core.config;
+
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigBeanFactory;
+import com.typesafe.config.ConfigFactory;
+import org.junit.Assert;
+import org.junit.Test;
+import org.tron.core.config.args.CommitteeConfig;
+import org.tron.core.config.args.MetricsConfig;
+import org.tron.core.config.args.NodeConfig;
+import org.tron.core.config.args.RateLimiterConfig;
+import org.tron.core.config.args.StorageConfig;
+import org.tron.core.config.args.VmConfig;
+
+/**
+ * Verifies that BeanDefaults.toConfig() produces a Config that:
+ * 1. Contains the correct default values from Java field initializers.
+ * 2. Satisfies ConfigBeanFactory.create() without ConfigException.Missing.
+ * 3. Is properly overridden when a user value is supplied via withFallback().
+ */
+public class BeanDefaultsTest {
+
+ // ── VmConfig ─────────────────────────────────────────────────────────────
+
+ @Test
+ public void vmConfig_defaultValues() {
+ Config cfg = BeanDefaults.toConfig(new VmConfig());
+
+ Assert.assertFalse(cfg.getBoolean("supportConstant"));
+ Assert.assertEquals(100_000_000L, cfg.getLong("maxEnergyLimitForConstant"));
+ Assert.assertEquals(500, cfg.getInt("lruCacheSize"));
+ Assert.assertEquals(0.0, cfg.getDouble("minTimeRatio"), 0.0);
+ Assert.assertEquals(5.0, cfg.getDouble("maxTimeRatio"), 0.0);
+ Assert.assertEquals(10, cfg.getInt("longRunningTime"));
+ Assert.assertFalse(cfg.getBoolean("estimateEnergy"));
+ Assert.assertEquals(3, cfg.getInt("estimateEnergyMaxRetry"));
+ Assert.assertFalse(cfg.getBoolean("vmTrace"));
+ Assert.assertFalse(cfg.getBoolean("saveInternalTx"));
+ Assert.assertFalse(cfg.getBoolean("saveFeaturedInternalTx"));
+ Assert.assertFalse(cfg.getBoolean("saveCancelAllUnfreezeV2Details"));
+ }
+
+ @Test
+ public void vmConfig_roundTrip_withConfigBeanFactory() {
+ Config defaults = BeanDefaults.toConfig(new VmConfig());
+ // ConfigBeanFactory must not throw ConfigException.Missing
+ VmConfig vm = ConfigBeanFactory.create(defaults, VmConfig.class);
+ Assert.assertFalse(vm.isSupportConstant());
+ Assert.assertEquals(500, vm.getLruCacheSize());
+ }
+
+ @Test
+ public void vmConfig_userValueOverridesDefault() {
+ Config user = ConfigFactory.parseString("lruCacheSize = 999");
+ Config merged = user.withFallback(BeanDefaults.toConfig(new VmConfig()));
+ VmConfig vm = ConfigBeanFactory.create(merged, VmConfig.class);
+ Assert.assertEquals(999, vm.getLruCacheSize());
+ // other fields keep defaults
+ Assert.assertEquals(10, vm.getLongRunningTime());
+ }
+
+ // ── NodeConfig nested bean ────────────────────────────────────────────────
+
+ @Test
+ public void nodeConfig_defaultScalars() {
+ Config cfg = BeanDefaults.toConfig(new NodeConfig());
+
+ Assert.assertEquals(30, cfg.getInt("maxConnections"));
+ Assert.assertEquals(8, cfg.getInt("minConnections"));
+ Assert.assertEquals(1000, cfg.getInt("maxTps"));
+ Assert.assertTrue(cfg.getBoolean("openPrintLog"));
+ Assert.assertFalse(cfg.getBoolean("walletExtensionApi"));
+ }
+
+ @Test
+ public void nodeConfig_nestedBeans_present() {
+ Config cfg = BeanDefaults.toConfig(new NodeConfig());
+
+ // listen.port should exist as a nested object
+ Assert.assertTrue(cfg.hasPath("listen"));
+ Assert.assertEquals(18888, cfg.getInt("listen.port"));
+
+ // discovery.enable
+ Assert.assertTrue(cfg.hasPath("discovery"));
+ Assert.assertFalse(cfg.getBoolean("discovery.enable"));
+
+ // http.fullNodeEnable
+ Assert.assertTrue(cfg.hasPath("http"));
+ Assert.assertTrue(cfg.getBoolean("http.fullNodeEnable"));
+ Assert.assertEquals(8090, cfg.getInt("http.fullNodePort"));
+
+ // rpc.enable
+ Assert.assertTrue(cfg.hasPath("rpc"));
+ Assert.assertTrue(cfg.getBoolean("rpc.enable"));
+ Assert.assertEquals(50051, cfg.getInt("rpc.port"));
+ }
+
+ @Test
+ public void nodeConfig_listFields_empty() {
+ Config cfg = BeanDefaults.toConfig(new NodeConfig());
+ Assert.assertTrue(cfg.getList("active").isEmpty());
+ Assert.assertTrue(cfg.getList("passive").isEmpty());
+ Assert.assertTrue(cfg.getList("fastForward").isEmpty());
+ Assert.assertTrue(cfg.getList("disabledApi").isEmpty());
+ }
+
+ @Test
+ public void nodeConfig_pBFTFields_usePropertyNameAsIs() {
+ Config cfg = BeanDefaults.toConfig(new NodeConfig());
+ // setPBFTEnable → Introspector property name "PBFTEnable" (capital P, two consecutive
+ // uppercase letters → JavaBean spec forbids decapitalization).
+ // ConfigBeanFactory looks up configProps.get("PBFTEnable"), so the map key must match.
+ Assert.assertTrue(cfg.hasPath("http.PBFTEnable"));
+ Assert.assertTrue(cfg.getBoolean("http.PBFTEnable"));
+ Assert.assertEquals(8092, cfg.getInt("http.PBFTPort"));
+
+ Assert.assertTrue(cfg.hasPath("rpc.PBFTEnable"));
+ Assert.assertEquals(50071, cfg.getInt("rpc.PBFTPort"));
+ }
+
+ @Test
+ public void nodeConfig_roundTrip_withConfigBeanFactory() {
+ Config defaults = BeanDefaults.toConfig(new NodeConfig());
+ // Must not throw — all keys present
+ NodeConfig nc = ConfigBeanFactory.create(defaults, NodeConfig.class);
+ Assert.assertEquals(30, nc.getMaxConnections());
+ Assert.assertEquals(18888, nc.getListenPort());
+ Assert.assertTrue(nc.getRpc().isEnable());
+ }
+
+ // ── StorageConfig nested bean ─────────────────────────────────────────────
+
+ @Test
+ public void storageConfig_defaultValues() {
+ Config cfg = BeanDefaults.toConfig(new StorageConfig());
+
+ Assert.assertEquals("LEVELDB", cfg.getString("db.engine"));
+ Assert.assertFalse(cfg.getBoolean("db.sync"));
+ Assert.assertEquals("database", cfg.getString("db.directory"));
+ Assert.assertEquals(7, cfg.getInt("dbSettings.levelNumber"));
+ Assert.assertEquals(1, cfg.getInt("checkpoint.version"));
+ Assert.assertTrue(cfg.getBoolean("checkpoint.sync"));
+ Assert.assertEquals(1, cfg.getInt("snapshot.maxFlushCount"));
+ Assert.assertTrue(cfg.getList("properties").isEmpty());
+ }
+
+ @Test
+ public void storageConfig_roundTrip_withConfigBeanFactory() {
+ Config defaults = BeanDefaults.toConfig(new StorageConfig());
+ StorageConfig sc = ConfigBeanFactory.create(defaults, StorageConfig.class);
+ Assert.assertEquals("LEVELDB", sc.getDb().getEngine());
+ Assert.assertEquals(7, sc.getDbSettings().getLevelNumber());
+ }
+
+ // ── MetricsConfig nested sub-beans ───────────────────────────────────────
+
+ @Test
+ public void metricsConfig_defaultValues() {
+ Config cfg = BeanDefaults.toConfig(new MetricsConfig());
+
+ Assert.assertFalse(cfg.getBoolean("storageEnable"));
+ Assert.assertFalse(cfg.getBoolean("prometheus.enable"));
+ Assert.assertEquals(9527, cfg.getInt("prometheus.port"));
+ Assert.assertEquals("", cfg.getString("influxdb.ip"));
+ Assert.assertEquals(8086, cfg.getInt("influxdb.port"));
+ Assert.assertEquals("metrics", cfg.getString("influxdb.database"));
+ Assert.assertEquals(10, cfg.getInt("influxdb.metricsReportInterval"));
+ }
+
+ @Test
+ public void metricsConfig_roundTrip() {
+ Config defaults = BeanDefaults.toConfig(new MetricsConfig());
+ MetricsConfig mc = ConfigBeanFactory.create(defaults, MetricsConfig.class);
+ Assert.assertFalse(mc.isStorageEnable());
+ Assert.assertEquals(9527, mc.getPrometheus().getPort());
+ }
+
+ // ── RateLimiterConfig ────────────────────────────────────────────────────
+
+ @Test
+ public void rateLimiterConfig_defaultValues() {
+ Config cfg = BeanDefaults.toConfig(new RateLimiterConfig());
+
+ Assert.assertEquals(50000, cfg.getInt("global.qps"));
+ Assert.assertEquals(10000, cfg.getInt("global.ip.qps"));
+ Assert.assertEquals(1000, cfg.getInt("global.api.qps"));
+ Assert.assertTrue(cfg.getList("http").isEmpty());
+ Assert.assertTrue(cfg.getList("rpc").isEmpty());
+ }
+
+ @Test
+ public void rateLimiterConfig_roundTrip() {
+ Config defaults = BeanDefaults.toConfig(new RateLimiterConfig());
+ RateLimiterConfig rl = ConfigBeanFactory.create(defaults, RateLimiterConfig.class);
+ Assert.assertEquals(50000, rl.getGlobal().getQps());
+ Assert.assertTrue(rl.getHttp().isEmpty());
+ }
+
+ // ── CommitteeConfig ───────────────────────────────────────────────────────
+
+ @Test
+ public void committeeConfig_allZeroDefaults() {
+ Config cfg = BeanDefaults.toConfig(new CommitteeConfig());
+
+ Assert.assertEquals(0L, cfg.getLong("allowCreationOfContracts"));
+ Assert.assertEquals(0L, cfg.getLong("allowMultiSign"));
+ Assert.assertEquals(0L, cfg.getLong("allowTvmCancun"));
+ }
+
+ @Test
+ public void committeeConfig_roundTrip() {
+ Config defaults = BeanDefaults.toConfig(new CommitteeConfig());
+ CommitteeConfig cc = ConfigBeanFactory.create(defaults, CommitteeConfig.class);
+ Assert.assertEquals(0L, cc.getAllowCreationOfContracts());
+ }
+}
diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf
index 369924074bc..f3e720f4de4 100644
--- a/framework/src/main/resources/config.conf
+++ b/framework/src/main/resources/config.conf
@@ -11,20 +11,22 @@ storage {
# Whether to write transaction result in transactionRetStore
transHistory.switch = "on",
+ index.directory = "index",
+ index.switch = "on",
# setting can improve leveldb performance .... start, deprecated for arm
# node: if this will increase process fds,you may be check your ulimit if 'too many open files' error occurs
# see https://github.com/tronprotocol/tips/blob/master/tip-343.md for detail
# if you find block sync has lower performance, you can try this settings
- # default = {
- # maxOpenFiles = 100
- # }
- # defaultM = {
- # maxOpenFiles = 500
- # }
- # defaultL = {
- # maxOpenFiles = 1000
- # }
+ default = {
+ # maxOpenFiles = 100
+ }
+ defaultM = {
+ # maxOpenFiles = 500
+ }
+ defaultL = {
+ # maxOpenFiles = 1000
+ }
# setting can improve leveldb performance .... end, deprecated for arm
# You can customize the configuration for each database. Otherwise, the database settings will use
@@ -77,18 +79,26 @@ storage {
balance.history.lookup = false
- # checkpoint.version = 2
- # checkpoint.sync = true
+ backup = {
+ enable = false
+ propPath = "prop.properties"
+ bak1path = "bak1/database/"
+ bak2path = "bak2/database/"
+ frequency = 10000
+ }
+
+ checkpoint.version = 1
+ checkpoint.sync = true
- # the estimated number of block transactions (default 1000, min 100, max 10000).
- # so the total number of cached transactions is 65536 * txCache.estimatedTransactions
- # txCache.estimatedTransactions = 1000
+ # the estimated number of block transactions (min 100, max 10000).
+ # total cached transactions = 65536 * txCache.estimatedTransactions
+ txCache.estimatedTransactions = 1000
# if true, transaction cache initialization will be faster. Default: false
txCache.initOptimization = true
# The number of blocks flushed to db in each batch during node syncing. Default: 1
- # snapshot.maxFlushCount = 1
+ snapshot.maxFlushCount = 1
# data root setting, for check data, currently, only reward-vi is used.
# merkleRoot = {
@@ -143,7 +153,7 @@ node.metrics = {
influxdb {
ip = ""
port = 8086
- database = ""
+ database = "metrics"
metricsReportInterval = 10
}
}
@@ -151,16 +161,16 @@ node.metrics = {
node {
# trust node for solidity node
# trustNode = "ip:port"
- trustNode = "127.0.0.1:50051"
+ trustNode = ""
# expose extension api to public or not
- walletExtensionApi = true
+ walletExtensionApi = false
listen.port = 18888
connection.timeout = 2
- fetchBlock.timeout = 200
+ fetchBlock.timeout = 500
# syncFetchBatchNum = 2000
# Number of validate sign thread, default availableProcessors
@@ -193,6 +203,40 @@ node {
isOpenFullTcpDisconnect = false
inactiveThreshold = 600 //seconds
+ # Maximum number of fast-forward peers. Default: 4
+ maxFastForwardNum = 4
+
+ # Netty work thread counts; 0 = auto (availableProcessors). Default: tcp=0, udp=1
+ tcpNettyWorkThreadNum = 0
+ udpNettyWorkThreadNum = 1
+
+ # Maximum number of shielded transactions in the pending pool. Default: 10
+ shieldedTransInPendingMaxCounts = 10
+
+ # Block cache timeout in seconds. Default: 60
+ blockCacheTimeout = 60
+
+ # Minimum data length (bytes) to read from TCP. Default: 2048
+ receiveTcpMinDataLength = 2048
+
+ # Maximum pending transaction pool size. Default: 2000
+ maxTransactionPendingSize = 2000
+
+ # Timeout for pending transactions in milliseconds. Default: 60000
+ pendingTransactionTimeout = 60000
+
+ # Required agreement count for block consensus; 0 = auto (2/3 of witness count + 1). Default: 0
+ agreeNodeCount = 0
+
+ # Enable node-level metrics collection (prerequisite for prometheus/influxdb reporting). Default: false
+ metricsEnable = false
+
+ # Channel read timeout in seconds; 0 = no timeout. Default: 0
+ channel.read.timeout = 0
+
+ # Threads for contract protobuf validation; 0 = auto (availableProcessors). Default: 0
+ validContractProto.threads = 0
+
p2p {
version = 11111 # Mainnet:11111; Nile:201910292; Shasta:1
}
@@ -265,6 +309,9 @@ node {
# The switch of the reflection service, effective for all gRPC services, used for grpcurl tool. Default: false
reflectionService = false
+
+ # Cache transactions in the RPC layer. Default: false
+ trxCacheEnable = false
}
# number of solidity thread in the FullNode.
@@ -437,15 +484,17 @@ rate.limiter = {
]
p2p = {
- # syncBlockChain = 3.0
- # fetchInvData = 3.0
- # disconnect = 1.0
+ syncBlockChain = 3.0
+ fetchInvData = 3.0
+ disconnect = 1.0
}
# global qps, default 50000
global.qps = 50000
# IP-based global qps, default 10000
global.ip.qps = 10000
+ # API-based global qps, default 1000
+ global.api.qps = 1000
}
@@ -837,3 +886,11 @@ event.subscribe = {
]
}
}
+
+# Whitelist of actuator class names to enable. When empty, all actuators are enabled.
+# Example: ["org.tron.core.actuator.TransferActuator"]
+# actuator.whitelist = []
+
+# Block number from which the energy limit calculation applies.
+# Note: the config key contains a legacy typo ("enery") preserved for backward compatibility.
+# enery.limit.block.num = 4727890
diff --git a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java
index e0d9d456e9a..00acc2615d5 100644
--- a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java
+++ b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java
@@ -166,7 +166,7 @@ public void testInitService() {
storage.put("storage.db.directory", "database");
Config config = ConfigFactory.defaultOverrides()
.withFallback(ConfigFactory.parseMap(storage))
- .withFallback(ConfigFactory.defaultReference());
+ .withFallback(ConfigFactory.empty());
// test default value
Args.applyConfigParams(config);
Assert.assertTrue(Args.getInstance().isRpcEnable());
@@ -195,7 +195,7 @@ public void testInitService() {
storage.put("node.jsonrpc.maxSubTopics", "20");
config = ConfigFactory.defaultOverrides()
.withFallback(ConfigFactory.parseMap(storage))
- .withFallback(ConfigFactory.defaultReference());
+ .withFallback(ConfigFactory.empty());
// test value
Args.applyConfigParams(config);
Assert.assertTrue(Args.getInstance().isRpcEnable());
@@ -224,7 +224,7 @@ public void testInitService() {
storage.put("node.jsonrpc.maxSubTopics", "1000");
config = ConfigFactory.defaultOverrides()
.withFallback(ConfigFactory.parseMap(storage))
- .withFallback(ConfigFactory.defaultReference());
+ .withFallback(ConfigFactory.empty());
// test value
Args.applyConfigParams(config);
Assert.assertFalse(Args.getInstance().isRpcEnable());
@@ -253,7 +253,7 @@ public void testInitService() {
storage.put("node.jsonrpc.maxSubTopics", "40");
config = ConfigFactory.defaultOverrides()
.withFallback(ConfigFactory.parseMap(storage))
- .withFallback(ConfigFactory.defaultReference());
+ .withFallback(ConfigFactory.empty());
// test value
Args.applyConfigParams(config);
Assert.assertFalse(Args.getInstance().isRpcEnable());
@@ -273,7 +273,7 @@ public void testInitService() {
storage.put("node.jsonrpc.maxSubTopics", "0");
config = ConfigFactory.defaultOverrides()
.withFallback(ConfigFactory.parseMap(storage))
- .withFallback(ConfigFactory.defaultReference());
+ .withFallback(ConfigFactory.empty());
// check value
Args.applyConfigParams(config);
Assert.assertEquals(0, Args.getInstance().getJsonRpcMaxBlockRange());
@@ -284,7 +284,7 @@ public void testInitService() {
storage.put("node.jsonrpc.maxSubTopics", "-4");
config = ConfigFactory.defaultOverrides()
.withFallback(ConfigFactory.parseMap(storage))
- .withFallback(ConfigFactory.defaultReference());
+ .withFallback(ConfigFactory.empty());
// check value
Args.applyConfigParams(config);
Assert.assertEquals(-2, Args.getInstance().getJsonRpcMaxBlockRange());
@@ -386,7 +386,7 @@ public void testFetchBlockTimeoutClampedBelowMin() {
override.put("storage.db.directory", "database");
override.put("node.fetchBlock.timeout", "50");
Config config = ConfigFactory.parseMap(override)
- .withFallback(ConfigFactory.defaultReference());
+ .withFallback(ConfigFactory.empty());
Args.applyConfigParams(config);
Assert.assertEquals(100, Args.getInstance().getFetchBlockTimeout());
Args.clearParam();
@@ -398,7 +398,7 @@ public void testFetchBlockTimeoutClampedAboveMax() {
override.put("storage.db.directory", "database");
override.put("node.fetchBlock.timeout", "2000");
Config config = ConfigFactory.parseMap(override)
- .withFallback(ConfigFactory.defaultReference());
+ .withFallback(ConfigFactory.empty());
Args.applyConfigParams(config);
Assert.assertEquals(1000, Args.getInstance().getFetchBlockTimeout());
Args.clearParam();
@@ -410,7 +410,7 @@ public void testFetchBlockTimeoutInRangeUnchanged() {
override.put("storage.db.directory", "database");
override.put("node.fetchBlock.timeout", "500");
Config config = ConfigFactory.parseMap(override)
- .withFallback(ConfigFactory.defaultReference());
+ .withFallback(ConfigFactory.empty());
Args.applyConfigParams(config);
Assert.assertEquals(500, Args.getInstance().getFetchBlockTimeout());
Args.clearParam();
From afea1c9d8067eb0667bf614c6f43a62bd9897af7 Mon Sep 17 00:00:00 2001
From: jiangyuanshu <317787106@qq.com>
Date: Tue, 5 May 2026 18:21:37 +0800
Subject: [PATCH 2/6] format code
---
.../java/org/tron/core/config/args/Args.java | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java
index f91c6a437ac..cf8197a8f6b 100644
--- a/framework/src/main/java/org/tron/core/config/args/Args.java
+++ b/framework/src/main/java/org/tron/core/config/args/Args.java
@@ -2,42 +2,26 @@
import static java.lang.System.exit;
import static org.tron.common.math.Maths.max;
-import static org.tron.common.math.Maths.min;
import static org.tron.core.Constant.ADD_PRE_FIX_BYTE_MAINNET;
-import static org.tron.core.Constant.DEFAULT_PROPOSAL_EXPIRE_TIME;
-import static org.tron.core.Constant.DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE;
-import static org.tron.core.Constant.DYNAMIC_ENERGY_MAX_FACTOR_RANGE;
import static org.tron.core.Constant.ENERGY_LIMIT_IN_CONSTANT_TX;
-import static org.tron.core.Constant.MAX_PROPOSAL_EXPIRE_TIME;
-import static org.tron.core.Constant.MIN_PROPOSAL_EXPIRE_TIME;
-import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCE_TIMEOUT_PERCENT;
-import static org.tron.core.config.Parameter.ChainConstant.MAX_ACTIVE_WITNESS_NUM;
-import static org.tron.core.exception.TronError.ErrCode.PARAMETER_INIT;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterDescription;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.typesafe.config.Config;
-import com.typesafe.config.ConfigObject;
-import io.grpc.internal.GrpcUtil;
-import io.grpc.netty.NettyServerBuilder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
@@ -69,8 +53,6 @@
import org.tron.core.Constant;
import org.tron.core.Wallet;
import org.tron.core.config.Configuration;
-import org.tron.core.config.Parameter.NetConstants;
-import org.tron.core.config.Parameter.NodeConstant;
import org.tron.core.exception.TronError;
import org.tron.core.store.AccountStore;
import org.tron.p2p.P2pConfig;
From 7de92be0f54742ca4b092e695d7ad79cc36cac7d Mon Sep 17 00:00:00 2001
From: jiangyuanshu <317787106@qq.com>
Date: Fri, 8 May 2026 10:48:38 +0800
Subject: [PATCH 3/6] add stripNullLeaves to auto binding; simply binding not
compatible varible; fix the bug of allowShieldedTransactionApi
---
.../common/parameter/CommonParameter.java | 3 -
.../org/tron/core/config/BeanDefaults.java | 42 +++++
.../tron/core/config/args/BlockConfig.java | 6 +-
.../core/config/args/CommitteeConfig.java | 48 +----
.../tron/core/config/args/EventConfig.java | 76 +++-----
.../tron/core/config/args/GenesisConfig.java | 2 +-
.../tron/core/config/args/MetricsConfig.java | 2 +-
.../org/tron/core/config/args/NodeConfig.java | 173 ++++++++----------
.../org/tron/core/config/args/Overlay.java | 22 ---
.../core/config/args/RateLimiterConfig.java | 2 +-
.../tron/core/config/args/StorageConfig.java | 20 +-
.../org/tron/core/config/args/VmConfig.java | 2 +-
.../tron/core/config/BeanDefaultsTest.java | 20 ++
.../core/config/args/EventConfigTest.java | 4 +-
.../core/config/args/GenesisConfigTest.java | 8 +-
.../tron/core/config/args/MiscConfigTest.java | 7 +-
.../tron/core/config/args/NodeConfigTest.java | 6 +-
.../java/org/tron/common/ParameterTest.java | 1 -
.../tron/core/config/args/OverlayTest.java | 40 ----
19 files changed, 195 insertions(+), 289 deletions(-)
delete mode 100644 common/src/main/java/org/tron/core/config/args/Overlay.java
delete mode 100644 framework/src/test/java/org/tron/core/config/args/OverlayTest.java
diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java
index e7957c917e2..c2b233d6533 100644
--- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java
+++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java
@@ -15,7 +15,6 @@
import org.tron.common.logsfilter.FilterQuery;
import org.tron.common.setting.RocksDbSettings;
import org.tron.core.Constant;
-import org.tron.core.config.args.Overlay;
import org.tron.core.config.args.SeedNode;
import org.tron.core.config.args.Storage;
import org.tron.p2p.P2pConfig;
@@ -415,8 +414,6 @@ public class CommonParameter {
@Getter
public Storage storage;
@Getter
- public Overlay overlay;
- @Getter
public SeedNode seedNode;
@Getter
public EventPluginConfig eventPluginConfig;
diff --git a/common/src/main/java/org/tron/core/config/BeanDefaults.java b/common/src/main/java/org/tron/core/config/BeanDefaults.java
index 4c279cee7c2..1e54bac5724 100644
--- a/common/src/main/java/org/tron/core/config/BeanDefaults.java
+++ b/common/src/main/java/org/tron/core/config/BeanDefaults.java
@@ -2,6 +2,9 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
+import com.typesafe.config.ConfigObject;
+import com.typesafe.config.ConfigValue;
+import com.typesafe.config.ConfigValueType;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
@@ -43,6 +46,45 @@ public static Config toConfig(Object bean) {
return ConfigFactory.parseMap(toMap(bean));
}
+ /**
+ * Returns a copy of {@code config} with all null-valued leaf paths removed.
+ * Call this on a user-supplied config section before {@link Config#withFallback}
+ * so that HOCON {@code null} entries in legacy configs do not shadow bean defaults.
+ *
+ * Uses {@link ConfigObject#entrySet()} (not {@link Config#entrySet()}) because
+ * the latter silently excludes null values, making them impossible to detect.
+ */
+ public static Config stripNullLeaves(Config config) {
+ return stripNullObject(config.root()).toConfig();
+ }
+
+ /**
+ * Returns a copy of {@code config} where the value at {@code fromKey} is moved to
+ * {@code toKey}, leaving the original key absent. If {@code fromKey} is absent, the
+ * config is returned unchanged. Use this in {@code fromConfig()} to bridge config keys
+ * that violate JavaBean naming (e.g. {@code pBFTExpireNum} → {@code PBFTExpireNum}) so
+ * that {@code ConfigBeanFactory} finds the value under the key it derives from the setter.
+ */
+ public static Config remapKey(Config config, String fromKey, String toKey) {
+ if (!config.hasPath(fromKey)) {
+ return config;
+ }
+ return config.withValue(toKey, config.getValue(fromKey)).withoutPath(fromKey);
+ }
+
+ private static ConfigObject stripNullObject(ConfigObject obj) {
+ ConfigObject result = obj;
+ for (Map.Entry entry : obj.entrySet()) {
+ ConfigValue v = entry.getValue();
+ if (v.valueType() == ConfigValueType.NULL) {
+ result = result.withoutKey(entry.getKey());
+ } else if (v.valueType() == ConfigValueType.OBJECT) {
+ result = result.withValue(entry.getKey(), stripNullObject((ConfigObject) v));
+ }
+ }
+ return result;
+ }
+
private static Map toMap(Object bean) {
Map map = new LinkedHashMap<>();
try {
diff --git a/common/src/main/java/org/tron/core/config/args/BlockConfig.java b/common/src/main/java/org/tron/core/config/args/BlockConfig.java
index 6ade2b00068..567446e1003 100644
--- a/common/src/main/java/org/tron/core/config/args/BlockConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/BlockConfig.java
@@ -7,10 +7,10 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
-import org.tron.core.config.BeanDefaults;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
+import org.tron.core.config.BeanDefaults;
import org.tron.core.exception.TronError;
/**
@@ -22,7 +22,7 @@
public class BlockConfig {
private boolean needSyncCheck = false;
- private long maintenanceTimeInterval = 21600000L;
+ private long maintenanceTimeInterval = 6 * 3600 * 1000L; // 6 hours
private long proposalExpireTime = DEFAULT_PROPOSAL_EXPIRE_TIME;
private int checkFrozenTime = 1;
@@ -39,7 +39,7 @@ public static BlockConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new BlockConfig());
Config blockSection = config.hasPath("block")
- ? config.getConfig("block").withFallback(defaults)
+ ? BeanDefaults.stripNullLeaves(config.getConfig("block")).withFallback(defaults)
: defaults;
BlockConfig blockConfig = ConfigBeanFactory.create(blockSection, BlockConfig.class);
blockConfig.postProcess();
diff --git a/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java b/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java
index c2ad56f227c..5ef6e958957 100644
--- a/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import com.typesafe.config.ConfigFactory;
import org.tron.core.config.BeanDefaults;
import lombok.Getter;
import lombok.Setter;
@@ -36,29 +37,8 @@ public class CommitteeConfig {
private long allowProtoFilterNum = 0;
private long allowAccountStateRoot = 0;
private long changedDelegation = 0;
- // NON-STANDARD NAMING: "allowPBFT" and "pBFTExpireNum" in config.conf contain
- // consecutive uppercase letters ("PBFT"), which violates JavaBean naming convention.
- // ConfigBeanFactory derives config keys from setter names using JavaBean rules:
- // setPBFTExpireNum -> property "PBFTExpireNum" (capital P, per JavaBean spec)
- // but config.conf uses "pBFTExpireNum" (lowercase p) -> mismatch -> binding fails.
- //
- // These two fields are excluded from auto-binding and handled manually in fromConfig().
- // TODO: Rename config keys to standard camelCase (allowPbft, pbftExpireNum) when
- // PBFT feature is enabled and a breaking config change is acceptable.
- @Getter(lombok.AccessLevel.NONE)
- @Setter(lombok.AccessLevel.NONE)
private long allowPBFT = 0;
- @Getter(lombok.AccessLevel.NONE)
- @Setter(lombok.AccessLevel.NONE)
private long pBFTExpireNum = 20;
-
- // Only getters are exposed. No public setters — ConfigBeanFactory scans public
- // setters via reflection and would derive key "PBFTExpireNum" / "AllowPBFT"
- // (JavaBean uppercase rule), which does not match config keys "pBFTExpireNum"
- // / "allowPBFT" and would throw. Values are assigned to fields directly in
- // fromConfig() below.
- public long getAllowPBFT() { return allowPBFT; }
- public long getPBFTExpireNum() { return pBFTExpireNum; }
private long allowTvmFreeze = 0;
private long allowTvmVote = 0;
private long allowTvmLondon = 0;
@@ -88,27 +68,17 @@ public class CommitteeConfig {
// proposalExpireTime is NOT a committee field — it's in block.* and handled by BlockConfig
- /**
- * Create CommitteeConfig from the "committee" section of the application config.
- *
- * Note: allowPBFT and pBFTExpireNum have non-standard JavaBean naming (consecutive
- * uppercase letters) which causes ConfigBeanFactory key mismatch. These two fields
- * are excluded from automatic binding and handled manually after.
- */
- private static final String PBFT_EXPIRE_NUM_KEY = "pBFTExpireNum";
- private static final String ALLOW_PBFT_KEY = "allowPBFT";
-
public static CommitteeConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new CommitteeConfig());
- Config section = config.hasPath("committee")
- ? config.getConfig("committee").withFallback(defaults)
- : defaults;
+ Config userSection = config.hasPath("committee")
+ ? BeanDefaults.stripNullLeaves(config.getConfig("committee"))
+ : ConfigFactory.empty();
+ // pBFTExpireNum: config key uses lowercase-p prefix, but setPBFTExpireNum causes
+ // Introspector to derive "PBFTExpireNum" (consecutive uppercase prevents decapitalization).
+ // Remap so ConfigBeanFactory finds it under the expected key.
+ userSection = BeanDefaults.remapKey(userSection, "pBFTExpireNum", "PBFTExpireNum");
+ Config section = userSection.withFallback(defaults);
CommitteeConfig cc = ConfigBeanFactory.create(section, CommitteeConfig.class);
- // Ensure the manually-named fields get the right values from the original keys
- cc.allowPBFT = section.hasPath(ALLOW_PBFT_KEY) ? section.getLong(ALLOW_PBFT_KEY) : 0;
- cc.pBFTExpireNum = section.hasPath(PBFT_EXPIRE_NUM_KEY)
- ? section.getLong(PBFT_EXPIRE_NUM_KEY) : 20;
-
cc.postProcess();
return cc;
}
diff --git a/common/src/main/java/org/tron/core/config/args/EventConfig.java b/common/src/main/java/org/tron/core/config/args/EventConfig.java
index 42c03a04b8f..0dd8750e148 100644
--- a/common/src/main/java/org/tron/core/config/args/EventConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/EventConfig.java
@@ -3,12 +3,12 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
import com.typesafe.config.ConfigFactory;
-import org.tron.core.config.BeanDefaults;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
+import org.tron.core.config.BeanDefaults;
/**
* Event subscribe configuration bean.
@@ -26,11 +26,9 @@ public class EventConfig {
private String server = "";
private String dbconfig = "";
private boolean contractParse = true;
- @Getter(lombok.AccessLevel.NONE)
- @Setter(lombok.AccessLevel.NONE)
+ // Config key is "native" (Java reserved word); remapped to this field in fromConfig().
private NativeConfig nativeQueue = new NativeConfig();
- public NativeConfig getNativeQueue() { return nativeQueue; }
// Topics list has optional fields (ethCompatible, redundancy, solidified) that
// not all items have. ConfigBeanFactory requires all bean fields to exist in config.
// Excluded from auto-binding, read manually in fromConfig().
@@ -38,12 +36,16 @@ public class EventConfig {
@Setter(lombok.AccessLevel.NONE)
private List topics = new ArrayList<>();
- public List getTopics() { return topics; }
+ public List getTopics() {
+ return topics;
+ }
+
private FilterConfig filter = new FilterConfig();
@Getter
@Setter
public static class NativeConfig {
+
private boolean useNativeQueue = true;
private int bindport = 5555;
private int sendqueuelength = 1000;
@@ -52,6 +54,7 @@ public static class NativeConfig {
@Getter
@Setter
public static class TopicConfig {
+
private String triggerName = "";
private boolean enable = false;
private String topic = "";
@@ -63,6 +66,7 @@ public static class TopicConfig {
@Getter
@Setter
public static class FilterConfig {
+
private String fromblock = "";
private String toblock = "";
private List contractAddress = new ArrayList<>();
@@ -76,61 +80,29 @@ public static class FilterConfig {
* "nativeQueue" but config key is "native". We handle this manually after binding.
*/
public static EventConfig fromConfig(Config config) {
- // BeanDefaults covers enable/version/startSyncBlockNum/path/server/dbconfig/
- // contractParse/filter. nativeQueue and topics are excluded (no public setter).
Config defaults = BeanDefaults.toConfig(new EventConfig());
- Config section = config.hasPath("event.subscribe")
- ? config.getConfig("event.subscribe")
+ Config userSection = config.hasPath("event.subscribe")
+ ? BeanDefaults.stripNullLeaves(config.getConfig("event.subscribe"))
: ConfigFactory.empty();
- // "native" is a Java reserved word, "topics" has optional fields per item —
- // strip both before binding, read manually
- String nativeKey = "native";
- String topicsKey = "topics";
- Config bindable = section.withoutPath(nativeKey).withoutPath(topicsKey)
+ // "native" is a Java reserved word — remap to the field name so ConfigBeanFactory
+ // auto-binds it as NativeConfig nativeQueue. topics has optional fields per item
+ // so it is excluded from auto-binding and populated manually below.
+ Config bindable = BeanDefaults.remapKey(userSection, "native", "nativeQueue")
+ .withoutPath("topics")
.withoutPath("topicDefaults")
.withFallback(defaults);
EventConfig ec = ConfigBeanFactory.create(bindable, EventConfig.class);
- // manually bind "native" sub-section
- Config nativeSection = section.hasPath(nativeKey)
- ? section.getConfig(nativeKey) : ConfigFactory.empty();
- ec.nativeQueue = new NativeConfig();
- if (nativeSection.hasPath("useNativeQueue")) {
- ec.nativeQueue.useNativeQueue = nativeSection.getBoolean("useNativeQueue");
- }
- if (nativeSection.hasPath("bindport")) {
- ec.nativeQueue.bindport = nativeSection.getInt("bindport");
- }
- if (nativeSection.hasPath("sendqueuelength")) {
- ec.nativeQueue.sendqueuelength = nativeSection.getInt("sendqueuelength");
- }
-
- // manually bind topics — each item may have optional fields
- if (section.hasPath(topicsKey)) {
+ // topics: apply per-item BeanDefaults so optional fields (solidified, ethCompatible,
+ // redundancy) don't require every item to declare them explicitly.
+ if (userSection.hasPath("topics")) {
+ Config topicDefaults = BeanDefaults.toConfig(new TopicConfig());
ec.topics = new ArrayList<>();
- for (com.typesafe.config.ConfigObject obj : section.getObjectList(topicsKey)) {
- Config tc = obj.toConfig();
- TopicConfig topic = new TopicConfig();
- if (tc.hasPath("triggerName")) {
- topic.triggerName = tc.getString("triggerName");
- }
- if (tc.hasPath("enable")) {
- topic.enable = tc.getBoolean("enable");
- }
- if (tc.hasPath("topic")) {
- topic.topic = tc.getString("topic");
- }
- if (tc.hasPath("solidified")) {
- topic.solidified = tc.getBoolean("solidified");
- }
- if (tc.hasPath("ethCompatible")) {
- topic.ethCompatible = tc.getBoolean("ethCompatible");
- }
- if (tc.hasPath("redundancy")) {
- topic.redundancy = tc.getBoolean("redundancy");
- }
- ec.topics.add(topic);
+ for (com.typesafe.config.ConfigObject obj : userSection.getObjectList("topics")) {
+ ec.topics.add(ConfigBeanFactory.create(
+ BeanDefaults.stripNullLeaves(obj.toConfig()).withFallback(topicDefaults),
+ TopicConfig.class));
}
}
diff --git a/common/src/main/java/org/tron/core/config/args/GenesisConfig.java b/common/src/main/java/org/tron/core/config/args/GenesisConfig.java
index 74d4338bbb5..645fc238ee0 100644
--- a/common/src/main/java/org/tron/core/config/args/GenesisConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/GenesisConfig.java
@@ -45,7 +45,7 @@ public static class WitnessConfig {
public static GenesisConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new GenesisConfig());
Config section = config.hasPath("genesis.block")
- ? config.getConfig("genesis.block").withFallback(defaults)
+ ? BeanDefaults.stripNullLeaves(config.getConfig("genesis.block")).withFallback(defaults)
: defaults;
return ConfigBeanFactory.create(section, GenesisConfig.class);
}
diff --git a/common/src/main/java/org/tron/core/config/args/MetricsConfig.java b/common/src/main/java/org/tron/core/config/args/MetricsConfig.java
index 98d5c0a36d7..da0404d516d 100644
--- a/common/src/main/java/org/tron/core/config/args/MetricsConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/MetricsConfig.java
@@ -39,7 +39,7 @@ public static class InfluxDbConfig {
public static MetricsConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new MetricsConfig());
Config section = config.hasPath("node.metrics")
- ? config.getConfig("node.metrics").withFallback(defaults)
+ ? BeanDefaults.stripNullLeaves(config.getConfig("node.metrics")).withFallback(defaults)
: defaults;
return ConfigBeanFactory.create(section, MetricsConfig.class);
}
diff --git a/common/src/main/java/org/tron/core/config/args/NodeConfig.java b/common/src/main/java/org/tron/core/config/args/NodeConfig.java
index 02b7eee87b6..2bd912db493 100644
--- a/common/src/main/java/org/tron/core/config/args/NodeConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/NodeConfig.java
@@ -5,12 +5,12 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
import com.typesafe.config.ConfigFactory;
-import org.tron.core.config.BeanDefaults;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
+import org.tron.core.config.BeanDefaults;
// Node configuration bean for the "node" section of config.conf.
// ConfigBeanFactory auto-binds all fields including sub-beans, dot-notation keys,
@@ -42,7 +42,9 @@ public class NodeConfig {
@Setter(lombok.AccessLevel.NONE)
private boolean isOpenFullTcpDisconnect = false;
- public boolean isOpenFullTcpDisconnect() { return isOpenFullTcpDisconnect; }
+ public boolean isOpenFullTcpDisconnect() {
+ return isOpenFullTcpDisconnect;
+ }
// node.discovery.* — HOCON merges into node { discovery { ... } }, auto-bound
private DiscoveryConfig discovery = new DiscoveryConfig();
@@ -59,12 +61,30 @@ public class NodeConfig {
@Setter(lombok.AccessLevel.NONE)
private long shutdownBlockCount = -1;
- public boolean isDiscoveryEnable() { return discovery.isEnable(); }
- public boolean isDiscoveryPersist() { return discovery.isPersist(); }
- public String getDiscoveryExternalIp() { return discovery.getExternal().getIp(); }
- public String getShutdownBlockTime() { return shutdownBlockTime; }
- public long getShutdownBlockHeight() { return shutdownBlockHeight; }
- public long getShutdownBlockCount() { return shutdownBlockCount; }
+ public boolean isDiscoveryEnable() {
+ return discovery.isEnable();
+ }
+
+ public boolean isDiscoveryPersist() {
+ return discovery.isPersist();
+ }
+
+ public String getDiscoveryExternalIp() {
+ return discovery.getExternal().getIp();
+ }
+
+ public String getShutdownBlockTime() {
+ return shutdownBlockTime;
+ }
+
+ public long getShutdownBlockHeight() {
+ return shutdownBlockHeight;
+ }
+
+ public long getShutdownBlockCount() {
+ return shutdownBlockCount;
+ }
+
private int inactiveThreshold = 600;
private boolean metricsEnable = false;
private int blockProducedTimeOut = 50;
@@ -101,12 +121,29 @@ public class NodeConfig {
private SolidityConfig solidity = new SolidityConfig();
// Convenience getters for backward compatibility with applyNodeConfig
- public int getListenPort() { return listen.getPort(); }
- public int getConnectionTimeout() { return connection.getTimeout(); }
- public int getFetchBlockTimeout() { return fetchBlock.getTimeout(); }
- public int getSolidityThreads() { return solidity.getThreads(); }
- public int getChannelReadTimeout() { return channel.getRead().getTimeout(); }
- public int getValidContractProtoThreads() { return validContractProto.getThreads(); }
+ public int getListenPort() {
+ return listen.getPort();
+ }
+
+ public int getConnectionTimeout() {
+ return connection.getTimeout();
+ }
+
+ public int getFetchBlockTimeout() {
+ return fetchBlock.getTimeout();
+ }
+
+ public int getSolidityThreads() {
+ return solidity.getThreads();
+ }
+
+ public int getChannelReadTimeout() {
+ return channel.getRead().getTimeout();
+ }
+
+ public int getValidContractProtoThreads() {
+ return validContractProto.getThreads();
+ }
// ---- List fields (manually read) ----
private List active = new ArrayList<>();
@@ -199,69 +236,30 @@ public static class HttpConfig {
private int fullNodePort = 8090;
private boolean solidityEnable = true;
private int solidityPort = 8091;
- // PBFT fields — handled manually (same naming issue as CommitteeConfig)
- // Default must match CommonParameter.pBFTHttpEnable = true
- @Getter(lombok.AccessLevel.NONE)
- @Setter(lombok.AccessLevel.NONE)
+ // pBFTEnable/pBFTPort: fromConfig() remaps "pBFTEnable"→"PBFTEnable" so
+ // ConfigBeanFactory finds these under the JavaBean-derived key name.
private boolean pBFTEnable = true;
- @Getter(lombok.AccessLevel.NONE)
- @Setter(lombok.AccessLevel.NONE)
private int pBFTPort = 8092;
-
- public boolean isPBFTEnable() {
- return pBFTEnable;
- }
-
- public void setPBFTEnable(boolean v) {
- this.pBFTEnable = v;
- }
-
- public int getPBFTPort() {
- return pBFTPort;
- }
-
- public void setPBFTPort(int v) {
- this.pBFTPort = v;
- }
}
@Getter
@Setter
public static class RpcConfig {
+
private boolean enable = true;
private int port = 50051;
private boolean solidityEnable = true;
private int solidityPort = 50061;
- // PBFT fields — handled manually
- @Getter(lombok.AccessLevel.NONE)
- @Setter(lombok.AccessLevel.NONE)
+ // pBFTEnable/pBFTPort: remapped in NodeConfig.fromConfig() (same reason as HttpConfig).
private boolean pBFTEnable = true;
- @Getter(lombok.AccessLevel.NONE)
- @Setter(lombok.AccessLevel.NONE)
private int pBFTPort = 50071;
- public boolean isPBFTEnable() {
- return pBFTEnable;
- }
-
- public void setPBFTEnable(boolean v) {
- this.pBFTEnable = v;
- }
-
- public int getPBFTPort() {
- return pBFTPort;
- }
-
- public void setPBFTPort(int v) {
- this.pBFTPort = v;
- }
-
private int thread = 0;
- private int maxConcurrentCallsPerConnection = 2147483647;
- private int flowControlWindow = 1048576;
+ private int maxConcurrentCallsPerConnection = Integer.MAX_VALUE;
+ private int flowControlWindow = 1024 * 1024;
private long maxConnectionIdleInMillis = Long.MAX_VALUE;
private long maxConnectionAgeInMillis = Long.MAX_VALUE;
- private int maxMessageSize = 4194304;
+ private int maxMessageSize = 4 * 1024 * 1024;
private int maxHeaderListSize = 8192;
private int maxRstStream = 0;
private int secondsPerWindow = 0;
@@ -273,34 +271,16 @@ public void setPBFTPort(int v) {
@Getter
@Setter
public static class JsonRpcConfig {
+
private boolean httpFullNodeEnable = false;
private int httpFullNodePort = 8545;
private boolean httpSolidityEnable = false;
private int httpSolidityPort = 8555;
- // PBFT fields — handled manually
- @Getter(lombok.AccessLevel.NONE)
- @Setter(lombok.AccessLevel.NONE)
+ // httpPBFTEnable/httpPBFTPort: setHttpPBFTEnable → property "httpPBFTEnable" — matches
+ // config key directly, no remapping needed.
private boolean httpPBFTEnable = false;
- @Getter(lombok.AccessLevel.NONE)
- @Setter(lombok.AccessLevel.NONE)
private int httpPBFTPort = 8565;
- public boolean isHttpPBFTEnable() {
- return httpPBFTEnable;
- }
-
- public void setHttpPBFTEnable(boolean v) {
- this.httpPBFTEnable = v;
- }
-
- public int getHttpPBFTPort() {
- return httpPBFTPort;
- }
-
- public void setHttpPBFTPort(int v) {
- this.httpPBFTPort = v;
- }
-
private int maxBlockRange = 5000;
private int maxSubTopics = 1000;
private int maxBlockFilterNum = 50000;
@@ -352,20 +332,27 @@ public static class DnsConfig {
* solidity.threads) become nested HOCON objects and cannot be auto-bound to flat
* Java fields. They are read manually after ConfigBeanFactory binding.
*
- * PBFT-named fields in http, rpc, and jsonrpc sub-beans have the same JavaBean
- * naming issue as CommitteeConfig and are patched manually.
+ *
pBFT-prefixed fields in http and rpc sub-beans are remapped before binding
+ * (pBFTEnable → PBFTEnable) because consecutive uppercase letters prevent
+ * Introspector from decapitalizing the JavaBean property name.
+ * jsonrpc.httpPBFT* binds directly (httpPBFTEnable → property "httpPBFTEnable" ✓).
*
- *
List fields (active, passive, fastForward, disabledApi) are read manually
- * since ConfigBeanFactory expects typed bean lists, not string lists.
+ *
List fields (active, passive, fastForward, disabledApi) auto-bind via
+ * ConfigBeanFactory's List<String> support.
*/
public static NodeConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new NodeConfig());
- Config section = config.hasPath("node")
- ? config.getConfig("node").withFallback(defaults)
- : defaults;
- // Auto-bind all fields and sub-beans. ConfigBeanFactory fails fast with a
- // descriptive path on any `= null` value — external configs that use the
- // HOCON null keyword should fix their config rather than rely on silent coercion.
+ Config userSection = config.hasPath("node")
+ ? BeanDefaults.stripNullLeaves(config.getConfig("node"))
+ : ConfigFactory.empty();
+ // pBFTEnable/pBFTPort: config uses lowercase-p prefix; JavaBean derives "PBFTEnable"
+ // (consecutive uppercase prevents Introspector from decapitalizing). Remap so
+ // ConfigBeanFactory binds the user value instead of silently using the default.
+ userSection = BeanDefaults.remapKey(userSection, "http.pBFTEnable", "http.PBFTEnable");
+ userSection = BeanDefaults.remapKey(userSection, "http.pBFTPort", "http.PBFTPort");
+ userSection = BeanDefaults.remapKey(userSection, "rpc.pBFTEnable", "rpc.PBFTEnable");
+ userSection = BeanDefaults.remapKey(userSection, "rpc.pBFTPort", "rpc.PBFTPort");
+ Config section = userSection.withFallback(defaults);
NodeConfig nc = ConfigBeanFactory.create(section, NodeConfig.class);
// isOpenFullTcpDisconnect: boolean "is" prefix breaks JavaBean pairing
@@ -390,7 +377,8 @@ public static NodeConfig fromConfig(Config config) {
// Legacy key fallback: node.fullNodeAllowShieldedTransaction -> allowShieldedTransactionApi.
// BeanDefaults does not emit this legacy key, so hasPath here reliably means the user
// set it in their config. When present, it overrides the modern key.
- if (section.hasPath("fullNodeAllowShieldedTransaction")) {
+ if (!section.hasPath("allowShieldedTransactionApi") &&
+ section.hasPath("fullNodeAllowShieldedTransaction")) {
nc.allowShieldedTransactionApi = section.getBoolean("fullNodeAllowShieldedTransaction");
logger.warn("Configuring [node.fullNodeAllowShieldedTransaction] will be deprecated. "
+ "Please use [node.allowShieldedTransactionApi] instead.");
@@ -403,7 +391,6 @@ public static NodeConfig fromConfig(Config config) {
nc.shutdownBlockCount = config.hasPath("node.shutdown.BlockCount")
? config.getLong("node.shutdown.BlockCount") : -1;
-
nc.postProcess();
return nc;
}
diff --git a/common/src/main/java/org/tron/core/config/args/Overlay.java b/common/src/main/java/org/tron/core/config/args/Overlay.java
deleted file mode 100644
index bdaa40724c7..00000000000
--- a/common/src/main/java/org/tron/core/config/args/Overlay.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.tron.core.config.args;
-
-import lombok.Getter;
-import org.apache.commons.lang3.Range;
-
-public class Overlay {
-
- @Getter
- private int port;
-
- /**
- * Monitor port number.
- */
- public void setPort(final int port) {
- Range range = Range.between(0, 65535);
- if (!range.contains(port)) {
- throw new IllegalArgumentException("Port(" + port + ") must in [0, 65535]");
- }
-
- this.port = port;
- }
-}
diff --git a/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java b/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
index 8fda4fba49c..ed92ed600bd 100644
--- a/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
@@ -70,7 +70,7 @@ public static class RpcRateLimitItem {
public static RateLimiterConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new RateLimiterConfig());
Config section = config.hasPath("rate.limiter")
- ? config.getConfig("rate.limiter").withFallback(defaults)
+ ? BeanDefaults.stripNullLeaves(config.getConfig("rate.limiter")).withFallback(defaults)
: defaults;
return ConfigBeanFactory.create(section, RateLimiterConfig.class);
}
diff --git a/common/src/main/java/org/tron/core/config/args/StorageConfig.java b/common/src/main/java/org/tron/core/config/args/StorageConfig.java
index ad8a41d35d2..45def5d8fe2 100644
--- a/common/src/main/java/org/tron/core/config/args/StorageConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/StorageConfig.java
@@ -33,38 +33,20 @@ public class StorageConfig {
private TxCacheConfig txCache = new TxCacheConfig();
private List properties = new ArrayList<>();
- // merkleRoot is a nested object (e.g. { reward-vi = "hash..." }) not a string.
- // Excluded from auto-binding, handled by Storage class directly.
- @Getter(lombok.AccessLevel.NONE)
- @Setter(lombok.AccessLevel.NONE)
- private Object merkleRoot;
-
// Raw storage config sub-tree, kept for setCacheStrategies/setDbRoots which
// have dynamic keys that ConfigBeanFactory cannot bind.
- @Getter(lombok.AccessLevel.NONE)
@Setter(lombok.AccessLevel.NONE)
private Config rawStorageConfig;
- public Config getRawStorageConfig() {
- return rawStorageConfig;
- }
-
// LevelDB per-database option overrides (default, defaultM, defaultL).
// Excluded from auto-binding: optional partial overrides that ConfigBeanFactory cannot handle.
- @Getter(lombok.AccessLevel.NONE)
@Setter(lombok.AccessLevel.NONE)
private DbOptionOverride defaultDbOption;
- @Getter(lombok.AccessLevel.NONE)
@Setter(lombok.AccessLevel.NONE)
private DbOptionOverride defaultMDbOption;
- @Getter(lombok.AccessLevel.NONE)
@Setter(lombok.AccessLevel.NONE)
private DbOptionOverride defaultLDbOption;
- public DbOptionOverride getDefaultDbOption() { return defaultDbOption; }
- public DbOptionOverride getDefaultMDbOption() { return defaultMDbOption; }
- public DbOptionOverride getDefaultLDbOption() { return defaultLDbOption; }
-
@Getter
@Setter
public static class DbConfig {
@@ -212,7 +194,7 @@ public static StorageConfig fromConfig(Config config) {
// readDbOption() uses hasPath() on the merged section, so user-set optional keys
// (default, defaultM, defaultL) are still detected correctly.
Config section = config.hasPath("storage")
- ? config.getConfig("storage").withFallback(defaults)
+ ? BeanDefaults.stripNullLeaves(config.getConfig("storage")).withFallback(defaults)
: defaults;
StorageConfig sc = ConfigBeanFactory.create(section, StorageConfig.class);
sc.rawStorageConfig = section;
diff --git a/common/src/main/java/org/tron/core/config/args/VmConfig.java b/common/src/main/java/org/tron/core/config/args/VmConfig.java
index a4b219f8726..c0136d06594 100644
--- a/common/src/main/java/org/tron/core/config/args/VmConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/VmConfig.java
@@ -32,7 +32,7 @@ public class VmConfig {
public static VmConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new VmConfig());
Config vmSection = config.hasPath("vm")
- ? config.getConfig("vm").withFallback(defaults)
+ ? BeanDefaults.stripNullLeaves(config.getConfig("vm")).withFallback(defaults)
: defaults;
VmConfig vmConfig = ConfigBeanFactory.create(vmSection, VmConfig.class);
vmConfig.postProcess();
diff --git a/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java b/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java
index c4170d580c1..88107484b01 100644
--- a/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java
+++ b/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java
@@ -213,4 +213,24 @@ public void committeeConfig_roundTrip() {
CommitteeConfig cc = ConfigBeanFactory.create(defaults, CommitteeConfig.class);
Assert.assertEquals(0L, cc.getAllowCreationOfContracts());
}
+
+ @Test
+ public void stripNullLeaves_removesNullPaths() {
+ Config cfg = ConfigFactory.parseString("a = null\nb = 1\nc.d = null\nc.e = 2");
+ Config stripped = BeanDefaults.stripNullLeaves(cfg);
+ Assert.assertFalse(stripped.hasPath("a"));
+ Assert.assertTrue(stripped.hasPath("b"));
+ Assert.assertFalse(stripped.hasPath("c.d"));
+ Assert.assertTrue(stripped.hasPath("c.e"));
+ }
+
+ @Test
+ public void nodeConfig_fromConfig_toleratesNullExternalIp() {
+ // Legacy configs used "node.discovery.external.ip = null" — must not throw.
+ Config cfg = ConfigFactory.parseString(
+ "node { discovery { external { ip = null } } }");
+ NodeConfig nc = NodeConfig.fromConfig(cfg);
+ Assert.assertNotNull(nc);
+ Assert.assertEquals("", nc.getDiscoveryExternalIp());
+ }
}
diff --git a/common/src/test/java/org/tron/core/config/args/EventConfigTest.java b/common/src/test/java/org/tron/core/config/args/EventConfigTest.java
index 361d9f48581..1aaca42f6fc 100644
--- a/common/src/test/java/org/tron/core/config/args/EventConfigTest.java
+++ b/common/src/test/java/org/tron/core/config/args/EventConfigTest.java
@@ -22,11 +22,11 @@ private static Config withRef() {
public void testDefaults() {
Config empty = withRef();
EventConfig ec = EventConfig.fromConfig(empty);
- // reference.conf has event.subscribe with enable=false, topics with 7 entries
+ // BeanDefaults provides scalar defaults; topics list is empty by default (user must configure)
assertFalse(ec.isEnable());
assertEquals(0, ec.getVersion());
assertEquals("", ec.getPath());
- assertFalse(ec.getTopics().isEmpty()); // reference.conf has default topic entries
+ assertTrue(ec.getTopics().isEmpty());
}
@Test
diff --git a/common/src/test/java/org/tron/core/config/args/GenesisConfigTest.java b/common/src/test/java/org/tron/core/config/args/GenesisConfigTest.java
index 5e653a79b7f..4f3e8829ade 100644
--- a/common/src/test/java/org/tron/core/config/args/GenesisConfigTest.java
+++ b/common/src/test/java/org/tron/core/config/args/GenesisConfigTest.java
@@ -22,10 +22,10 @@ private static Config withRef() {
public void testDefaults() {
Config empty = withRef();
GenesisConfig gc = GenesisConfig.fromConfig(empty);
- // reference.conf has genesis.block with timestamp, parentHash, assets, witnesses
- assertEquals("0", gc.getTimestamp());
- assertFalse(gc.getAssets().isEmpty()); // reference.conf has seed accounts
- assertFalse(gc.getWitnesses().isEmpty()); // reference.conf has seed witnesses
+ // BeanDefaults: timestamp/parentHash default to "", assets/witnesses to empty lists
+ assertEquals("", gc.getTimestamp());
+ assertTrue(gc.getAssets().isEmpty());
+ assertTrue(gc.getWitnesses().isEmpty());
}
@Test
diff --git a/common/src/test/java/org/tron/core/config/args/MiscConfigTest.java b/common/src/test/java/org/tron/core/config/args/MiscConfigTest.java
index ed369d6c35f..de06b31ac4b 100644
--- a/common/src/test/java/org/tron/core/config/args/MiscConfigTest.java
+++ b/common/src/test/java/org/tron/core/config/args/MiscConfigTest.java
@@ -28,10 +28,9 @@ public void testDefaults() {
assertEquals("solid", mc.getTrxReferenceBlock());
assertEquals(Constant.TRANSACTION_DEFAULT_EXPIRATION_TIME,
mc.getTrxExpirationTimeInMilliseconds());
- // reference.conf has crypto.engine = "eckey" (lowercase)
- assertEquals("eckey", mc.getCryptoEngine());
- // reference.conf has seed.node.ip.list with actual IPs
- assertFalse(mc.getSeedNodeIpList().isEmpty());
+ // BeanDefaults: cryptoEngine defaults to Constant.ECKey_ENGINE; seedNodeIpList defaults to empty
+ assertEquals(Constant.ECKey_ENGINE, mc.getCryptoEngine());
+ assertTrue(mc.getSeedNodeIpList().isEmpty());
assertTrue(mc.getActuatorWhitelist().isEmpty());
}
diff --git a/common/src/test/java/org/tron/core/config/args/NodeConfigTest.java b/common/src/test/java/org/tron/core/config/args/NodeConfigTest.java
index fb22029262e..39500a12197 100644
--- a/common/src/test/java/org/tron/core/config/args/NodeConfigTest.java
+++ b/common/src/test/java/org/tron/core/config/args/NodeConfigTest.java
@@ -105,10 +105,10 @@ public void testRpcDefaultsFromReference() {
NodeConfig.RpcConfig rpc = nc.getRpc();
// reference.conf provides actual final defaults, no sentinel conversion needed
- assertEquals(2147483647, rpc.getMaxConcurrentCallsPerConnection());
+ assertEquals(Integer.MAX_VALUE, rpc.getMaxConcurrentCallsPerConnection());
assertEquals(1048576, rpc.getFlowControlWindow());
- assertEquals(9223372036854775807L, rpc.getMaxConnectionIdleInMillis());
- assertEquals(9223372036854775807L, rpc.getMaxConnectionAgeInMillis());
+ assertEquals(Long.MAX_VALUE, rpc.getMaxConnectionIdleInMillis());
+ assertEquals(Long.MAX_VALUE, rpc.getMaxConnectionAgeInMillis());
assertEquals(4194304, rpc.getMaxMessageSize());
assertEquals(8192, rpc.getMaxHeaderListSize());
assertEquals(1, rpc.getMinEffectiveConnection());
diff --git a/framework/src/test/java/org/tron/common/ParameterTest.java b/framework/src/test/java/org/tron/common/ParameterTest.java
index d5dbced87fe..81a2e3fcdcd 100644
--- a/framework/src/test/java/org/tron/common/ParameterTest.java
+++ b/framework/src/test/java/org/tron/common/ParameterTest.java
@@ -226,7 +226,6 @@ public void testCommonParameter() {
assertEquals(1000, parameter.getRateLimiterGlobalQps());
parameter.setRateLimiterGlobalIpQps(100);
assertEquals(100, parameter.getRateLimiterGlobalIpQps());
- assertNull(parameter.getOverlay());
assertNull(parameter.getEventPluginConfig());
assertNull(parameter.getEventFilter());
parameter.setCryptoEngine(ECKey_ENGINE);
diff --git a/framework/src/test/java/org/tron/core/config/args/OverlayTest.java b/framework/src/test/java/org/tron/core/config/args/OverlayTest.java
deleted file mode 100644
index 1b7045c5b21..00000000000
--- a/framework/src/test/java/org/tron/core/config/args/OverlayTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 this program. If not, see .
- */
-
-package org.tron.core.config.args;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-public class OverlayTest {
-
- private Overlay overlay = new Overlay();
-
- @Before
- public void setOverlay() {
- overlay.setPort(8080);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void whenSetOutOfBoundsPort() {
- overlay.setPort(-1);
- }
-
- @Test
- public void getOverlay() {
- Assert.assertEquals(8080, overlay.getPort());
- }
-}
From 650e8e1dc4d347a09ccb76074c891860888b8856 Mon Sep 17 00:00:00 2001
From: jiangyuanshu <317787106@qq.com>
Date: Fri, 8 May 2026 13:50:06 +0800
Subject: [PATCH 4/6] add userSection; resolve the priority of
allowShieldedTransactionApi
---
.../java/org/tron/core/config/args/BlockConfig.java | 10 ++++++----
.../org/tron/core/config/args/GenesisConfig.java | 8 +++++---
.../org/tron/core/config/args/MetricsConfig.java | 8 +++++---
.../java/org/tron/core/config/args/NodeConfig.java | 6 +++---
.../tron/core/config/args/RateLimiterConfig.java | 8 +++++---
.../org/tron/core/config/args/StorageConfig.java | 13 ++++++++-----
.../java/org/tron/core/config/args/VmConfig.java | 10 ++++++----
.../org/tron/core/config/args/NodeConfigTest.java | 6 +++---
framework/src/main/resources/config.conf | 5 +++--
9 files changed, 44 insertions(+), 30 deletions(-)
diff --git a/common/src/main/java/org/tron/core/config/args/BlockConfig.java b/common/src/main/java/org/tron/core/config/args/BlockConfig.java
index 567446e1003..a0e187f1b5e 100644
--- a/common/src/main/java/org/tron/core/config/args/BlockConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/BlockConfig.java
@@ -7,6 +7,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import com.typesafe.config.ConfigFactory;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -38,10 +39,11 @@ public static BlockConfig fromConfig(Config config) {
}
Config defaults = BeanDefaults.toConfig(new BlockConfig());
- Config blockSection = config.hasPath("block")
- ? BeanDefaults.stripNullLeaves(config.getConfig("block")).withFallback(defaults)
- : defaults;
- BlockConfig blockConfig = ConfigBeanFactory.create(blockSection, BlockConfig.class);
+ Config userSection = config.hasPath("block")
+ ? BeanDefaults.stripNullLeaves(config.getConfig("block"))
+ : ConfigFactory.empty();
+ Config section = userSection.withFallback(defaults);
+ BlockConfig blockConfig = ConfigBeanFactory.create(section, BlockConfig.class);
blockConfig.postProcess();
return blockConfig;
}
diff --git a/common/src/main/java/org/tron/core/config/args/GenesisConfig.java b/common/src/main/java/org/tron/core/config/args/GenesisConfig.java
index 645fc238ee0..6d87ede74f7 100644
--- a/common/src/main/java/org/tron/core/config/args/GenesisConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/GenesisConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import com.typesafe.config.ConfigFactory;
import org.tron.core.config.BeanDefaults;
import java.util.ArrayList;
import java.util.List;
@@ -44,9 +45,10 @@ public static class WitnessConfig {
public static GenesisConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new GenesisConfig());
- Config section = config.hasPath("genesis.block")
- ? BeanDefaults.stripNullLeaves(config.getConfig("genesis.block")).withFallback(defaults)
- : defaults;
+ Config userSection = config.hasPath("genesis.block")
+ ? BeanDefaults.stripNullLeaves(config.getConfig("genesis.block"))
+ : ConfigFactory.empty();
+ Config section = userSection.withFallback(defaults);
return ConfigBeanFactory.create(section, GenesisConfig.class);
}
}
diff --git a/common/src/main/java/org/tron/core/config/args/MetricsConfig.java b/common/src/main/java/org/tron/core/config/args/MetricsConfig.java
index da0404d516d..a2462631232 100644
--- a/common/src/main/java/org/tron/core/config/args/MetricsConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/MetricsConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import com.typesafe.config.ConfigFactory;
import org.tron.core.config.BeanDefaults;
import lombok.Getter;
import lombok.Setter;
@@ -38,9 +39,10 @@ public static class InfluxDbConfig {
public static MetricsConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new MetricsConfig());
- Config section = config.hasPath("node.metrics")
- ? BeanDefaults.stripNullLeaves(config.getConfig("node.metrics")).withFallback(defaults)
- : defaults;
+ Config userSection = config.hasPath("node.metrics")
+ ? BeanDefaults.stripNullLeaves(config.getConfig("node.metrics"))
+ : ConfigFactory.empty();
+ Config section = userSection.withFallback(defaults);
return ConfigBeanFactory.create(section, MetricsConfig.class);
}
}
diff --git a/common/src/main/java/org/tron/core/config/args/NodeConfig.java b/common/src/main/java/org/tron/core/config/args/NodeConfig.java
index 2bd912db493..1e27e5234b8 100644
--- a/common/src/main/java/org/tron/core/config/args/NodeConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/NodeConfig.java
@@ -374,11 +374,11 @@ public static NodeConfig fromConfig(Config config) {
nc.maxConnectionsWithSameIp = section.getInt("maxActiveNodesWithSameIp");
}
- // Legacy key fallback: node.fullNodeAllowShieldedTransaction -> allowShieldedTransactionApi.
+ // Legacy key fallback: node.allowShieldedTransactionApi -> fullNodeAllowShieldedTransaction.
// BeanDefaults does not emit this legacy key, so hasPath here reliably means the user
// set it in their config. When present, it overrides the modern key.
- if (!section.hasPath("allowShieldedTransactionApi") &&
- section.hasPath("fullNodeAllowShieldedTransaction")) {
+ if (!userSection.hasPath("allowShieldedTransactionApi") &&
+ userSection.hasPath("fullNodeAllowShieldedTransaction")) {
nc.allowShieldedTransactionApi = section.getBoolean("fullNodeAllowShieldedTransaction");
logger.warn("Configuring [node.fullNodeAllowShieldedTransaction] will be deprecated. "
+ "Please use [node.allowShieldedTransactionApi] instead.");
diff --git a/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java b/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
index ed92ed600bd..33f42569154 100644
--- a/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import com.typesafe.config.ConfigFactory;
import org.tron.core.config.BeanDefaults;
import java.util.ArrayList;
import java.util.List;
@@ -69,9 +70,10 @@ public static class RpcRateLimitItem {
public static RateLimiterConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new RateLimiterConfig());
- Config section = config.hasPath("rate.limiter")
- ? BeanDefaults.stripNullLeaves(config.getConfig("rate.limiter")).withFallback(defaults)
- : defaults;
+ Config userSection = config.hasPath("rate.limiter")
+ ? BeanDefaults.stripNullLeaves(config.getConfig("rate.limiter"))
+ : ConfigFactory.empty();
+ Config section = userSection.withFallback(defaults);
return ConfigBeanFactory.create(section, RateLimiterConfig.class);
}
}
diff --git a/common/src/main/java/org/tron/core/config/args/StorageConfig.java b/common/src/main/java/org/tron/core/config/args/StorageConfig.java
index 45def5d8fe2..c12a92dcbc6 100644
--- a/common/src/main/java/org/tron/core/config/args/StorageConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/StorageConfig.java
@@ -2,8 +2,9 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
-import org.tron.core.config.BeanDefaults;
+import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigObject;
+import org.tron.core.config.BeanDefaults;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
@@ -190,12 +191,14 @@ public static class PropertyConfig {
public static StorageConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new StorageConfig());
+ Config userSection = config.hasPath("storage")
+ ? BeanDefaults.stripNullLeaves(config.getConfig("storage"))
+ : ConfigFactory.empty();
// User's storage section takes priority; defaults fill in any omitted scalar keys.
// readDbOption() uses hasPath() on the merged section, so user-set optional keys
- // (default, defaultM, defaultL) are still detected correctly.
- Config section = config.hasPath("storage")
- ? BeanDefaults.stripNullLeaves(config.getConfig("storage")).withFallback(defaults)
- : defaults;
+ // (default, defaultM, defaultL) are still detected correctly because they are
+ // absent from BeanDefaults and only present when the user explicitly set them.
+ Config section = userSection.withFallback(defaults);
StorageConfig sc = ConfigBeanFactory.create(section, StorageConfig.class);
sc.rawStorageConfig = section;
diff --git a/common/src/main/java/org/tron/core/config/args/VmConfig.java b/common/src/main/java/org/tron/core/config/args/VmConfig.java
index c0136d06594..2b4c06f194b 100644
--- a/common/src/main/java/org/tron/core/config/args/VmConfig.java
+++ b/common/src/main/java/org/tron/core/config/args/VmConfig.java
@@ -2,6 +2,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
+import com.typesafe.config.ConfigFactory;
import org.tron.core.config.BeanDefaults;
import lombok.Getter;
import lombok.Setter;
@@ -31,10 +32,11 @@ public class VmConfig {
public static VmConfig fromConfig(Config config) {
Config defaults = BeanDefaults.toConfig(new VmConfig());
- Config vmSection = config.hasPath("vm")
- ? BeanDefaults.stripNullLeaves(config.getConfig("vm")).withFallback(defaults)
- : defaults;
- VmConfig vmConfig = ConfigBeanFactory.create(vmSection, VmConfig.class);
+ Config userSection = config.hasPath("vm")
+ ? BeanDefaults.stripNullLeaves(config.getConfig("vm"))
+ : ConfigFactory.empty();
+ Config section = userSection.withFallback(defaults);
+ VmConfig vmConfig = ConfigBeanFactory.create(section, VmConfig.class);
vmConfig.postProcess();
return vmConfig;
}
diff --git a/common/src/test/java/org/tron/core/config/args/NodeConfigTest.java b/common/src/test/java/org/tron/core/config/args/NodeConfigTest.java
index 39500a12197..56a919a0f2f 100644
--- a/common/src/test/java/org/tron/core/config/args/NodeConfigTest.java
+++ b/common/src/test/java/org/tron/core/config/args/NodeConfigTest.java
@@ -307,13 +307,13 @@ public void testShieldedApiLegacyKeyRespected() {
}
@Test
- public void testShieldedApiLegacyKeyTakesPriorityOverModern() {
- // Consistent with maxActiveNodesWithSameIp: legacy key presence wins over modern.
+ public void testShieldedApiModernKeyTakesPriorityOverLegacy() {
+ // When both keys are present, allowShieldedTransactionApi (modern) wins.
NodeConfig nc = NodeConfig.fromConfig(
withRef("node {\n"
+ " allowShieldedTransactionApi = false\n"
+ " fullNodeAllowShieldedTransaction = true\n"
+ "}"));
- assertTrue(nc.isAllowShieldedTransactionApi());
+ assertFalse(nc.isAllowShieldedTransactionApi());
}
}
diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf
index f3e720f4de4..6da915b0720 100644
--- a/framework/src/main/resources/config.conf
+++ b/framework/src/main/resources/config.conf
@@ -108,8 +108,9 @@ storage {
}
node.discovery = {
- enable = true
- persist = true
+ enable = true # default: false
+ persist = true # default: false
+ # external.ip = "" # default: "" (auto-detect)
}
# custom stop condition
From 7dd0137f3d126ed6e73677510ba3d2dcd5c24177 Mon Sep 17 00:00:00 2001
From: jiangyuanshu <317787106@qq.com>
Date: Fri, 8 May 2026 14:07:11 +0800
Subject: [PATCH 5/6] optimize BeanDefaults
---
.../org/tron/core/config/BeanDefaults.java | 48 +++++++++++--------
1 file changed, 29 insertions(+), 19 deletions(-)
diff --git a/common/src/main/java/org/tron/core/config/BeanDefaults.java b/common/src/main/java/org/tron/core/config/BeanDefaults.java
index 1e54bac5724..1b9d8836759 100644
--- a/common/src/main/java/org/tron/core/config/BeanDefaults.java
+++ b/common/src/main/java/org/tron/core/config/BeanDefaults.java
@@ -87,30 +87,40 @@ private static ConfigObject stripNullObject(ConfigObject obj) {
private static Map toMap(Object bean) {
Map map = new LinkedHashMap<>();
+ BeanInfo info;
try {
- BeanInfo info = Introspector.getBeanInfo(bean.getClass());
- for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
- Method getter = pd.getReadMethod();
- Method setter = pd.getWriteMethod();
- // Skip read-only properties (no setter) — matches ConfigBeanFactory's contract
- if (getter == null || setter == null) {
- continue;
- }
- // Use the property name exactly as Introspector produced it.
- // ConfigBeanFactory does configProps.get(beanProp.getName()) — the lookup key
- // is the property name verbatim, not decapitalized. For ordinary camelCase
- // setters (setMaxConnections → "MaxConnections" → decapitalize → "maxConnections")
- // Introspector already returns the lowercase form. For setters that start with
- // two consecutive uppercase letters (setPBFTEnable → "PBFTEnable") the JavaBean
- // spec forbids decapitalization, so pd.getName() == "PBFTEnable" — matching the
- // capital-P key that config.conf uses for those fields.
+ info = Introspector.getBeanInfo(bean.getClass());
+ } catch (java.beans.IntrospectionException e) {
+ // Programming error: bean class does not conform to JavaBean spec.
+ // Propagate immediately so the misconfigured class is identified at startup,
+ // rather than returning a silent empty map that produces a confusing
+ // ConfigException.Missing pointing at the user config.
+ throw new IllegalStateException("Cannot introspect bean: " + bean.getClass().getName(), e);
+ }
+ for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
+ Method getter = pd.getReadMethod();
+ Method setter = pd.getWriteMethod();
+ // Skip read-only properties (no setter) — matches ConfigBeanFactory's contract
+ if (getter == null || setter == null) {
+ continue;
+ }
+ // Use the property name exactly as Introspector produced it.
+ // ConfigBeanFactory does configProps.get(beanProp.getName()) — the lookup key
+ // is the property name verbatim, not decapitalized. For ordinary camelCase
+ // setters (setMaxConnections → "MaxConnections" → decapitalize → "maxConnections")
+ // Introspector already returns the lowercase form. For setters that start with
+ // two consecutive uppercase letters (setPBFTEnable → "PBFTEnable") the JavaBean
+ // spec forbids decapitalization, so pd.getName() == "PBFTEnable" — matching the
+ // capital-P key that config.conf uses for those fields.
+ try {
String key = pd.getName();
Object value = getter.invoke(bean);
map.put(key, toValue(value));
+ } catch (Exception ignored) {
+ // Best-effort: skip individual unresolvable property so that the rest of
+ // the defaults are still emitted. getter.invoke() is the only realistic
+ // throw site (InvocationTargetException / IllegalAccessException).
}
- } catch (Exception ignored) {
- // Best-effort: any unresolvable field is simply omitted.
- // ConfigBeanFactory will throw with a clear path if a required key is missing.
}
return map;
}
From 86f8d74f56fa0b2c7126686060d2d6612506e7b0 Mon Sep 17 00:00:00 2001
From: jiangyuanshu <317787106@qq.com>
Date: Sat, 9 May 2026 23:11:16 +0800
Subject: [PATCH 6/6] fix bug of BeanDefaultsTest
---
common/src/test/java/org/tron/core/config/BeanDefaultsTest.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java b/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java
index 88107484b01..fff457e3e00 100644
--- a/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java
+++ b/common/src/test/java/org/tron/core/config/BeanDefaultsTest.java
@@ -171,7 +171,6 @@ public void metricsConfig_defaultValues() {
public void metricsConfig_roundTrip() {
Config defaults = BeanDefaults.toConfig(new MetricsConfig());
MetricsConfig mc = ConfigBeanFactory.create(defaults, MetricsConfig.class);
- Assert.assertFalse(mc.isStorageEnable());
Assert.assertEquals(9527, mc.getPrometheus().getPort());
}