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()); }