From 443ada06c3beee9fecac15486312252003e09198 Mon Sep 17 00:00:00 2001 From: ouy95917 Date: Wed, 25 Feb 2026 15:45:16 +0800 Subject: [PATCH 01/14] func(proposal): add Osaka proposal for TVM --- .../java/org/tron/core/utils/ProposalUtil.java | 18 +++++++++++++++++- .../org/tron/core/vm/config/ConfigLoader.java | 1 + .../core/store/DynamicPropertiesStore.java | 13 +++++++++++++ .../tron/common/parameter/CommonParameter.java | 4 ++++ .../src/main/java/org/tron/core/Constant.java | 2 ++ .../java/org/tron/core/config/Parameter.java | 5 +++-- .../java/org/tron/core/vm/config/VMConfig.java | 10 ++++++++++ .../src/main/java/org/tron/core/Wallet.java | 5 +++++ .../java/org/tron/core/config/args/Args.java | 5 +++++ .../tron/core/consensus/ProposalService.java | 4 ++++ framework/src/main/resources/config.conf | 1 + 11 files changed, 65 insertions(+), 3 deletions(-) diff --git a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java index d7e2f1c0949..94aa3e019ec 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -871,6 +871,21 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, } break; } + case ALLOW_TVM_OSAKA: { + if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) { + throw new ContractValidateException( + "Bad chain parameter id [ALLOW_TVM_OSAKA]"); + } + if (dynamicPropertiesStore.getAllowTvmOsaka() == 1) { + throw new ContractValidateException( + "[ALLOW_TVM_OSAKA] has been valid, no need to propose again"); + } + if (value != 1) { + throw new ContractValidateException( + "This value[ALLOW_TVM_OSAKA] is only allowed to be 1"); + } + break; + } default: break; } @@ -955,7 +970,8 @@ public enum ProposalType { // current value, value range CONSENSUS_LOGIC_OPTIMIZATION(88), // 0, 1 ALLOW_TVM_BLOB(89), // 0, 1 PROPOSAL_EXPIRE_TIME(92), // (0, 31536003000) - ALLOW_TVM_SELFDESTRUCT_RESTRICTION(94); // 0, 1 + ALLOW_TVM_SELFDESTRUCT_RESTRICTION(94), // 0, 1 + ALLOW_TVM_OSAKA(95); // 0, 1 private long code; diff --git a/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java b/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java index e0ebca329cd..e099101912b 100644 --- a/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java +++ b/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java @@ -45,6 +45,7 @@ public static void load(StoreFactory storeFactory) { VMConfig.initDisableJavaLangMath(ds.getConsensusLogicOptimization()); VMConfig.initAllowTvmBlob(ds.getAllowTvmBlob()); VMConfig.initAllowTvmSelfdestructRestriction(ds.getAllowTvmSelfdestructRestriction()); + VMConfig.initAllowTvmOsaka(ds.getAllowTvmOsaka()); } } } diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index 89c5ba18e59..24fb8a9caca 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -238,6 +238,8 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking private static final byte[] ALLOW_TVM_SELFDESTRUCT_RESTRICTION = "ALLOW_TVM_SELFDESTRUCT_RESTRICTION".getBytes(); + private static final byte[] ALLOW_TVM_OSAKA = "ALLOW_TVM_OSAKA".getBytes(); + @Autowired private DynamicPropertiesStore(@Value("properties") String dbName) { super(dbName); @@ -2980,6 +2982,17 @@ public long getProposalExpireTime() { .orElse(CommonParameter.getInstance().getProposalExpireTime()); } + public long getAllowTvmOsaka() { + return Optional.ofNullable(getUnchecked(ALLOW_TVM_OSAKA)) + .map(BytesCapsule::getData) + .map(ByteArray::toLong) + .orElse(0L); + } + + public void saveAllowTvmOsaka(long value) { + this.put(ALLOW_TVM_OSAKA, new BytesCapsule(ByteArray.fromLong(value))); + } + private static class DynamicResourceProperties { private static final byte[] ONE_DAY_NET_LIMIT = "ONE_DAY_NET_LIMIT".getBytes(); 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 0583962f266..fc4a2f48280 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -753,6 +753,10 @@ public class CommonParameter { @Setter public long allowTvmBlob; + @Getter + @Setter + public long allowTvmOsaka; + private static double calcMaxTimeRatio() { //return max(2.0, min(5.0, 5 * 4.0 / max(Runtime.getRuntime().availableProcessors(), 1))); return 5.0; diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index 47331808a5b..6777762540a 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -424,4 +424,6 @@ public class Constant { public static final String COMMITTEE_PROPOSAL_EXPIRE_TIME = "committee.proposalExpireTime"; + public static final String COMMITTEE_ALLOW_TVM_OSAKA = "committee.allowTvmOsaka"; + } diff --git a/common/src/main/java/org/tron/core/config/Parameter.java b/common/src/main/java/org/tron/core/config/Parameter.java index 8ec27ed3eb5..db88ab5e047 100644 --- a/common/src/main/java/org/tron/core/config/Parameter.java +++ b/common/src/main/java/org/tron/core/config/Parameter.java @@ -28,7 +28,8 @@ public enum ForkBlockVersionEnum { VERSION_4_7_7(31, 1596780000000L, 80), VERSION_4_8_0(32, 1596780000000L, 80), VERSION_4_8_0_1(33, 1596780000000L, 70), - VERSION_4_8_1(34, 1596780000000L, 80); + VERSION_4_8_1(34, 1596780000000L, 80), + VERSION_4_8_2(35, 1596780000000L, 80); // if add a version, modify BLOCK_VERSION simultaneously @Getter @@ -77,7 +78,7 @@ public class ChainConstant { public static final int SINGLE_REPEAT = 1; public static final int BLOCK_FILLED_SLOTS_NUMBER = 128; public static final int MAX_FROZEN_NUMBER = 1; - public static final int BLOCK_VERSION = 34; + public static final int BLOCK_VERSION = 35; public static final long FROZEN_PERIOD = 86_400_000L; public static final long DELEGATE_PERIOD = 3 * 86_400_000L; public static final long TRX_PRECISION = 1000_000L; diff --git a/common/src/main/java/org/tron/core/vm/config/VMConfig.java b/common/src/main/java/org/tron/core/vm/config/VMConfig.java index 578827b2f8c..1a7f0c058a4 100644 --- a/common/src/main/java/org/tron/core/vm/config/VMConfig.java +++ b/common/src/main/java/org/tron/core/vm/config/VMConfig.java @@ -61,6 +61,8 @@ public class VMConfig { private static boolean ALLOW_TVM_SELFDESTRUCT_RESTRICTION = false; + private static boolean ALLOW_TVM_OSAKA = false; + private VMConfig() { } @@ -172,6 +174,10 @@ public static void initAllowTvmSelfdestructRestriction(long allow) { ALLOW_TVM_SELFDESTRUCT_RESTRICTION = allow == 1; } + public static void initAllowTvmOsaka(long allow) { + ALLOW_TVM_OSAKA = allow == 1; + } + public static boolean getEnergyLimitHardFork() { return CommonParameter.ENERGY_LIMIT_HARD_FORK; } @@ -271,4 +277,8 @@ public static boolean allowTvmBlob() { public static boolean allowTvmSelfdestructRestriction() { return ALLOW_TVM_SELFDESTRUCT_RESTRICTION; } + + public static boolean allowTvmOsaka() { + return ALLOW_TVM_OSAKA; + } } diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 8c86f2f66ac..39e8f06c281 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -1509,6 +1509,11 @@ public Protocol.ChainParameters getChainParameters() { .setValue(dbManager.getDynamicPropertiesStore().getProposalExpireTime()) .build()); + builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder() + .setKey("getAllowTvmOsaka") + .setValue(dbManager.getDynamicPropertiesStore().getAllowTvmOsaka()) + .build()); + return builder.build(); } 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 46695986c1f..248ae10db7e 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 @@ -256,6 +256,7 @@ public static void clearParam() { PARAMETER.allowTvmBlob = 0; PARAMETER.rpcMaxRstStream = 0; PARAMETER.rpcSecondsPerWindow = 0; + PARAMETER.allowTvmOsaka = 0; } /** @@ -1289,6 +1290,10 @@ public static void setParam(final Config config) { config.hasPath(Constant.COMMITTEE_ALLOW_TVM_BLOB) ? config .getInt(Constant.COMMITTEE_ALLOW_TVM_BLOB) : 0; + PARAMETER.allowTvmOsaka = + config.hasPath(Constant.COMMITTEE_ALLOW_TVM_OSAKA) ? config + .getInt(Constant.COMMITTEE_ALLOW_TVM_OSAKA) : 0; + logConfig(); } diff --git a/framework/src/main/java/org/tron/core/consensus/ProposalService.java b/framework/src/main/java/org/tron/core/consensus/ProposalService.java index 51d53f6a59e..1bec0c2bda3 100644 --- a/framework/src/main/java/org/tron/core/consensus/ProposalService.java +++ b/framework/src/main/java/org/tron/core/consensus/ProposalService.java @@ -392,6 +392,10 @@ public static boolean process(Manager manager, ProposalCapsule proposalCapsule) manager.getDynamicPropertiesStore().saveProposalExpireTime(entry.getValue()); break; } + case ALLOW_TVM_OSAKA: { + manager.getDynamicPropertiesStore().saveAllowTvmOsaka(entry.getValue()); + break; + } default: find = false; break; diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf index 54f229e4e25..e78af547060 100644 --- a/framework/src/main/resources/config.conf +++ b/framework/src/main/resources/config.conf @@ -761,6 +761,7 @@ committee = { # allowTvmBlob = 0 # consensusLogicOptimization = 0 # allowOptimizedReturnValueOfChainId = 0 + # allowTvmOsaka = 0 } event.subscribe = { From 4e1eef4165b194e5a79cd53b3959e8dacb69cd19 Mon Sep 17 00:00:00 2001 From: ouy95917 Date: Wed, 25 Feb 2026 15:46:20 +0800 Subject: [PATCH 02/14] func(vm): implement eip-7823 --- .../main/java/org/tron/core/vm/PrecompiledContracts.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java index 654a76db33b..69189864261 100644 --- a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java +++ b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java @@ -65,6 +65,7 @@ import org.tron.core.capsule.TransactionCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.db.TransactionTrace; +import org.tron.core.exception.TronException; import org.tron.core.exception.ZksnarkException; import org.tron.core.vm.config.VMConfig; import org.tron.core.vm.program.Program; @@ -622,6 +623,8 @@ public static class ModExp extends PrecompiledContract { private static final int ARGS_OFFSET = 32 * 3; // addresses length part + private static final int UPPER_BOUND = 1024; + @Override public long getEnergyForData(byte[] data) { @@ -660,6 +663,12 @@ public Pair execute(byte[] data) { int expLen = parseLen(data, 1); int modLen = parseLen(data, 2); + if (VMConfig.allowTvmOsaka() && + (baseLen > UPPER_BOUND || expLen > UPPER_BOUND || modLen > UPPER_BOUND)) { + throw Program.Exception.contractExecuteException( + new TronException("one or more of base/exponent/modulus length exceeded 1024 bytes")); + } + BigInteger base = parseArg(data, ARGS_OFFSET, baseLen); BigInteger exp = parseArg(data, addSafely(ARGS_OFFSET, baseLen), expLen); BigInteger mod = parseArg(data, addSafely(addSafely(ARGS_OFFSET, baseLen), expLen), modLen); From 8dd8e7dfef86e3ef7c88ec931278f2234a74be33 Mon Sep 17 00:00:00 2001 From: ouy95917 Date: Wed, 25 Feb 2026 16:18:57 +0800 Subject: [PATCH 03/14] func(vm): add unit test for eip-7823 --- .../common/runtime/vm/AllowTvmOsakaTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java diff --git a/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java b/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java new file mode 100644 index 00000000000..cb654ffbb0d --- /dev/null +++ b/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java @@ -0,0 +1,47 @@ +package org.tron.common.runtime.vm; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Test; +import org.tron.common.utils.ByteUtil; +import org.tron.core.vm.PrecompiledContracts; +import org.tron.core.vm.config.ConfigLoader; +import org.tron.core.vm.config.VMConfig; +import org.tron.core.vm.program.Program; + +@Slf4j +public class AllowTvmOsakaTest extends VMTestBase { + + @Test + public void testEIP7823() { + ConfigLoader.disable = true; + VMConfig.initAllowTvmOsaka(1); + + byte[] baseLen = new byte[32]; + byte[] expLen = new byte[32]; + byte[] modLen = new byte[32]; + + PrecompiledContracts.PrecompiledContract modExp = new PrecompiledContracts.ModExp(); + + // Valid lens + try { + modExp.execute(ByteUtil.merge(baseLen, expLen, modLen)); + } catch (Exception e) { + Assert.fail(); + } + + // Invalid lens + try { + baseLen[0] = 0x01; + modExp.execute(ByteUtil.merge(baseLen, expLen, modLen)); + } catch (Exception e) { + Assert.assertTrue(e instanceof Program.PrecompiledContractException); + return; + } finally { + VMConfig.initAllowTvmOsaka(0); + ConfigLoader.disable = false; + } + + Assert.fail(); + } +} From e1969bb91340526fd2d6dae75e779690fcc3b742 Mon Sep 17 00:00:00 2001 From: operagxsasha Date: Fri, 13 Mar 2026 10:16:13 +0200 Subject: [PATCH 04/14] docs: fix shieldedTransaction typos in comments --- protocol/src/main/protos/api/api.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol/src/main/protos/api/api.proto b/protocol/src/main/protos/api/api.proto index a67113cb606..d66d8aa6263 100644 --- a/protocol/src/main/protos/api/api.proto +++ b/protocol/src/main/protos/api/api.proto @@ -671,7 +671,7 @@ service Wallet { }; - // for shiededTransaction + // for shieldedTransaction rpc CreateShieldedTransaction (PrivateParameters) returns (TransactionExtention) { }; @@ -747,7 +747,7 @@ service Wallet { rpc GetTriggerInputForShieldedTRC20Contract (ShieldedTRC20TriggerContractParameters) returns (BytesMessage) { }; - // end for shiededTransaction + // end for shieldedTransaction rpc CreateCommonTransaction (Transaction) returns (TransactionExtention) { }; From a8fa3d42048bc968afcd05084dd6d223603a2058 Mon Sep 17 00:00:00 2001 From: ouy95917 Date: Mon, 30 Mar 2026 16:57:02 +0800 Subject: [PATCH 05/14] fix(vm): correct osaka proposal id, default config and modexp behavior 1. Change ALLOW_TVM_OSAKA proposal id from 95 to 96 2. DynamicPropertiesStore.getAllowTvmOsaka() defaults to CommonParameter 3. ModExp returns Pair.of(false, EMPTY_BYTE_ARRAY) instead of throwing PrecompiledContractException when inputs exceed 1024 bytes, matching geth/besu behavior where only the CALL fails (not the whole tx) 4. Update test to verify return value instead of catching exception --- .../org/tron/core/utils/ProposalUtil.java | 2 +- .../tron/core/vm/PrecompiledContracts.java | 8 ++--- .../core/store/DynamicPropertiesStore.java | 2 +- .../org/tron/core/config/args/ConfigKey.java | 1 + .../common/runtime/vm/AllowTvmOsakaTest.java | 31 +++++++------------ 5 files changed, 18 insertions(+), 26 deletions(-) diff --git a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java index 94aa3e019ec..cd42d7a9010 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -971,7 +971,7 @@ public enum ProposalType { // current value, value range ALLOW_TVM_BLOB(89), // 0, 1 PROPOSAL_EXPIRE_TIME(92), // (0, 31536003000) ALLOW_TVM_SELFDESTRUCT_RESTRICTION(94), // 0, 1 - ALLOW_TVM_OSAKA(95); // 0, 1 + ALLOW_TVM_OSAKA(96); // 0, 1 private long code; diff --git a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java index 69189864261..634f7f2d3d1 100644 --- a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java +++ b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java @@ -65,7 +65,6 @@ import org.tron.core.capsule.TransactionCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.db.TransactionTrace; -import org.tron.core.exception.TronException; import org.tron.core.exception.ZksnarkException; import org.tron.core.vm.config.VMConfig; import org.tron.core.vm.program.Program; @@ -663,10 +662,9 @@ public Pair execute(byte[] data) { int expLen = parseLen(data, 1); int modLen = parseLen(data, 2); - if (VMConfig.allowTvmOsaka() && - (baseLen > UPPER_BOUND || expLen > UPPER_BOUND || modLen > UPPER_BOUND)) { - throw Program.Exception.contractExecuteException( - new TronException("one or more of base/exponent/modulus length exceeded 1024 bytes")); + if (VMConfig.allowTvmOsaka() + && (baseLen > UPPER_BOUND || expLen > UPPER_BOUND || modLen > UPPER_BOUND)) { + return Pair.of(false, EMPTY_BYTE_ARRAY); } BigInteger base = parseArg(data, ARGS_OFFSET, baseLen); diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index 24fb8a9caca..e0adb0d444a 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -2986,7 +2986,7 @@ public long getAllowTvmOsaka() { return Optional.ofNullable(getUnchecked(ALLOW_TVM_OSAKA)) .map(BytesCapsule::getData) .map(ByteArray::toLong) - .orElse(0L); + .orElse(CommonParameter.getInstance().getAllowTvmOsaka()); } public void saveAllowTvmOsaka(long value) { diff --git a/framework/src/main/java/org/tron/core/config/args/ConfigKey.java b/framework/src/main/java/org/tron/core/config/args/ConfigKey.java index 06dd7d9d57a..b21c9c440a4 100644 --- a/framework/src/main/java/org/tron/core/config/args/ConfigKey.java +++ b/framework/src/main/java/org/tron/core/config/args/ConfigKey.java @@ -247,6 +247,7 @@ private ConfigKey() { public static final String COMMITTEE_ALLOW_TVM_CANCUN = "committee.allowTvmCancun"; public static final String COMMITTEE_ALLOW_TVM_BLOB = "committee.allowTvmBlob"; public static final String COMMITTEE_PROPOSAL_EXPIRE_TIME = "committee.proposalExpireTime"; + public static final String COMMITTEE_ALLOW_TVM_OSAKA = "committee.allowTvmOsaka"; public static final String ALLOW_ACCOUNT_ASSET_OPTIMIZATION = "committee.allowAccountAssetOptimization"; public static final String ALLOW_ASSET_OPTIMIZATION = "committee.allowAssetOptimization"; diff --git a/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java b/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java index cb654ffbb0d..9c75b305158 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java @@ -1,13 +1,13 @@ package org.tron.common.runtime.vm; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; import org.junit.Assert; import org.junit.Test; import org.tron.common.utils.ByteUtil; import org.tron.core.vm.PrecompiledContracts; import org.tron.core.vm.config.ConfigLoader; import org.tron.core.vm.config.VMConfig; -import org.tron.core.vm.program.Program; @Slf4j public class AllowTvmOsakaTest extends VMTestBase { @@ -17,31 +17,24 @@ public void testEIP7823() { ConfigLoader.disable = true; VMConfig.initAllowTvmOsaka(1); - byte[] baseLen = new byte[32]; - byte[] expLen = new byte[32]; - byte[] modLen = new byte[32]; + try { + byte[] baseLen = new byte[32]; + byte[] expLen = new byte[32]; + byte[] modLen = new byte[32]; - PrecompiledContracts.PrecompiledContract modExp = new PrecompiledContracts.ModExp(); + PrecompiledContracts.PrecompiledContract modExp = new PrecompiledContracts.ModExp(); - // Valid lens - try { - modExp.execute(ByteUtil.merge(baseLen, expLen, modLen)); - } catch (Exception e) { - Assert.fail(); - } + // Valid lens: all zeros (0 <= 1024) + Pair result = modExp.execute(ByteUtil.merge(baseLen, expLen, modLen)); + Assert.assertTrue(result.getLeft()); - // Invalid lens - try { + // Invalid lens: baseLen = 0x01000000... = 16777216 > 1024 baseLen[0] = 0x01; - modExp.execute(ByteUtil.merge(baseLen, expLen, modLen)); - } catch (Exception e) { - Assert.assertTrue(e instanceof Program.PrecompiledContractException); - return; + result = modExp.execute(ByteUtil.merge(baseLen, expLen, modLen)); + Assert.assertFalse(result.getLeft()); } finally { VMConfig.initAllowTvmOsaka(0); ConfigLoader.disable = false; } - - Assert.fail(); } } From 0663e176c05a7f85731189f9551bc806834f3dc4 Mon Sep 17 00:00:00 2001 From: ouy95917 Date: Tue, 31 Mar 2026 09:42:36 +0800 Subject: [PATCH 06/14] test(vm): add boundary and edge case tests for eip-7823 modexp limit - baseLen == 1024 boundary value should succeed - baseLen == 1025 just over limit should fail - oversized expLen only should fail - oversized modLen only should fail - all limits exceeded with osaka disabled should succeed --- .../common/runtime/vm/AllowTvmOsakaTest.java | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java b/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java index 9c75b305158..897ec43e24a 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java @@ -12,29 +12,66 @@ @Slf4j public class AllowTvmOsakaTest extends VMTestBase { + private static final PrecompiledContracts.PrecompiledContract modExp = + new PrecompiledContracts.ModExp(); + + private static byte[] toLenBytes(int value) { + byte[] b = new byte[32]; + b[28] = (byte) ((value >> 24) & 0xFF); + b[29] = (byte) ((value >> 16) & 0xFF); + b[30] = (byte) ((value >> 8) & 0xFF); + b[31] = (byte) (value & 0xFF); + return b; + } + @Test public void testEIP7823() { ConfigLoader.disable = true; VMConfig.initAllowTvmOsaka(1); try { - byte[] baseLen = new byte[32]; - byte[] expLen = new byte[32]; - byte[] modLen = new byte[32]; - - PrecompiledContracts.PrecompiledContract modExp = new PrecompiledContracts.ModExp(); + // all-zero lengths: should succeed + Pair result = modExp.execute( + ByteUtil.merge(toLenBytes(0), toLenBytes(0), toLenBytes(0))); + Assert.assertTrue(result.getLeft()); - // Valid lens: all zeros (0 <= 1024) - Pair result = modExp.execute(ByteUtil.merge(baseLen, expLen, modLen)); + // baseLen == 1024: boundary, should succeed + result = modExp.execute( + ByteUtil.merge(toLenBytes(1024), toLenBytes(0), toLenBytes(0))); Assert.assertTrue(result.getLeft()); - // Invalid lens: baseLen = 0x01000000... = 16777216 > 1024 - baseLen[0] = 0x01; - result = modExp.execute(ByteUtil.merge(baseLen, expLen, modLen)); + // baseLen == 1025: just over the limit, should fail + result = modExp.execute( + ByteUtil.merge(toLenBytes(1025), toLenBytes(0), toLenBytes(0))); + Assert.assertFalse(result.getLeft()); + + // oversized expLen only: should fail + result = modExp.execute( + ByteUtil.merge(toLenBytes(0), toLenBytes(1025), toLenBytes(0))); + Assert.assertFalse(result.getLeft()); + + // oversized modLen only: should fail + result = modExp.execute( + ByteUtil.merge(toLenBytes(0), toLenBytes(0), toLenBytes(1025))); Assert.assertFalse(result.getLeft()); } finally { VMConfig.initAllowTvmOsaka(0); ConfigLoader.disable = false; } } + + @Test + public void testEIP7823DisabledShouldPass() { + ConfigLoader.disable = true; + VMConfig.initAllowTvmOsaka(0); + + try { + // all limits exceeded while osaka is disabled: should succeed (no restriction) + Pair result = modExp.execute( + ByteUtil.merge(toLenBytes(2048), toLenBytes(2048), toLenBytes(2048))); + Assert.assertTrue(result.getLeft()); + } finally { + ConfigLoader.disable = false; + } + } } From 18b5e61e3a1706a749f2438f905e9b9e6b46208e Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Fri, 27 Feb 2026 11:31:46 +0800 Subject: [PATCH 07/14] feat(actuator): prevent duplicate registration and stabilize CI Changes: - Add a registration status check to prevent duplicate initialization during CI test runs. - Throw TronError on actuator instantiation failures to improve error visibility and debugging. - Narrow package scanning from "org.tron" to "org.tron.core.actuator" to reduce reflection overhead and speed up registration. - Remove JVM args for CI test to avoid JDK8 G1 GC bugs and Evacuation Pause failures. - Optimize console output to prevent test OOM for CI - Adjust memory and parallelism settings - Exclude dnsjava InetAddressResolverProvider --- .../tron/core/utils/TransactionRegister.java | 50 ++++++- .../org/tron/core/exception/TronError.java | 1 + framework/build.gradle | 14 +- .../core/utils/TransactionRegisterTest.java | 137 ++++++++++++++++++ framework/src/test/resources/logback-test.xml | 82 ++++++----- 5 files changed, 230 insertions(+), 54 deletions(-) create mode 100644 framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java diff --git a/actuator/src/main/java/org/tron/core/utils/TransactionRegister.java b/actuator/src/main/java/org/tron/core/utils/TransactionRegister.java index 867ea06bfe2..14746211045 100644 --- a/actuator/src/main/java/org/tron/core/utils/TransactionRegister.java +++ b/actuator/src/main/java/org/tron/core/utils/TransactionRegister.java @@ -1,24 +1,58 @@ package org.tron.core.utils; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import lombok.extern.slf4j.Slf4j; import org.reflections.Reflections; import org.tron.core.actuator.AbstractActuator; +import org.tron.core.exception.TronError; @Slf4j(topic = "TransactionRegister") public class TransactionRegister { + private static final AtomicBoolean REGISTERED = new AtomicBoolean(false); + private static final String PACKAGE_NAME = "org.tron.core.actuator"; + public static void registerActuator() { - Reflections reflections = new Reflections("org.tron"); - Set> subTypes = reflections - .getSubTypesOf(AbstractActuator.class); - for (Class _class : subTypes) { - try { - _class.newInstance(); - } catch (Exception e) { - logger.error("{} contract actuator register fail!", _class, e); + if (REGISTERED.get()) { + logger.debug("Actuator already registered."); + return; + } + + synchronized (TransactionRegister.class) { + if (REGISTERED.get()) { + logger.debug("Actuator already registered."); + return; + } + logger.debug("Register actuator start."); + Reflections reflections = new Reflections(PACKAGE_NAME); + Set> subTypes = reflections + .getSubTypesOf(AbstractActuator.class); + + for (Class clazz : subTypes) { + try { + logger.debug("Registering actuator: {} start", clazz.getName()); + clazz.getDeclaredConstructor().newInstance(); + logger.debug("Registering actuator: {} done", clazz.getName()); + } catch (Exception e) { + Throwable cause = e.getCause() != null ? e.getCause() : e; + String detail = cause.getMessage() != null ? cause.getMessage() : cause.toString(); + throw new TronError(clazz.getName() + ": " + detail, + e, TronError.ErrCode.ACTUATOR_REGISTER); + } } + + REGISTERED.set(true); + logger.debug("Register actuator done, total {}.", subTypes.size()); } } + static boolean isRegistered() { + return REGISTERED.get(); + } + + // For testing only — resets registration state between tests. + static void resetForTesting() { + REGISTERED.set(false); + } } diff --git a/common/src/main/java/org/tron/core/exception/TronError.java b/common/src/main/java/org/tron/core/exception/TronError.java index f407c6dfe3c..4ee7cdae916 100644 --- a/common/src/main/java/org/tron/core/exception/TronError.java +++ b/common/src/main/java/org/tron/core/exception/TronError.java @@ -49,6 +49,7 @@ public enum ErrCode { RATE_LIMITER_INIT(1), SOLID_NODE_INIT(0), PARAMETER_INIT(1), + ACTUATOR_REGISTER(1), JDK_VERSION(1); private final int code; diff --git a/framework/build.gradle b/framework/build.gradle index 9b287fdeb27..d884b6a7c49 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -30,7 +30,7 @@ static def isWindows() { return org.gradle.internal.os.OperatingSystem.current().isWindows() } -task version(type: Exec) { +tasks.register('version', Exec) { commandLine 'bash', '-c', '../ver.sh' } @@ -78,7 +78,7 @@ checkstyleMain { source = 'src/main/java' } -task lint(type: Checkstyle) { +tasks.register('lint', Checkstyle) { // Cleaning the old log because of the creation of the new ones (not sure if totaly needed) delete fileTree(dir: "${project.rootDir}/app/build/reports") source 'src' @@ -123,10 +123,10 @@ def configureTestTask = { Task t -> t.exclude 'org/tron/core/ShieldedTRC20BuilderTest.class' t.exclude 'org/tron/common/runtime/vm/WithdrawRewardTest.class' } - t.maxHeapSize = "1024m" + t.maxHeapSize = "512m" + t.maxParallelForks = Math.max(1, Math.min(4, Runtime.runtime.availableProcessors())) t.doFirst { t.forkEvery = 100 - t.jvmArgs "-XX:MetaspaceSize=128m", "-XX:MaxMetaspaceSize=256m", "-XX:+UseG1GC" } } @@ -150,7 +150,7 @@ test { } } -task testWithRocksDb(type: Test) { +tasks.register('testWithRocksDb', Test) { description = 'Run tests with RocksDB engine' group = 'verification' configureTestTask(it) @@ -197,7 +197,9 @@ def binaryRelease(taskName, jarName, mainClass) { exclude "META-INF/*.SF" exclude "META-INF/*.DSA" exclude "META-INF/*.RSA" - + // for service SPI loader for dnsjava + // see https://issues.apache.org/jira/browse/HADOOP-19288 + exclude "META-INF/services/java.net.spi.InetAddressResolverProvider" manifest { attributes "Main-Class": "${mainClass}" } diff --git a/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java new file mode 100644 index 00000000000..cb87abff14f --- /dev/null +++ b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java @@ -0,0 +1,137 @@ +package org.tron.core.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedConstruction; +import org.mockito.junit.MockitoJUnitRunner; +import org.reflections.Reflections; +import org.tron.core.actuator.AbstractActuator; +import org.tron.core.actuator.TransferActuator; +import org.tron.core.config.args.Args; +import org.tron.core.exception.TronError; + +@RunWith(MockitoJUnitRunner.class) +public class TransactionRegisterTest { + + @Before + public void init() { + Args.getInstance().setActuatorSet(new HashSet<>()); + TransactionRegister.resetForTesting(); + } + + @After + public void destroy() { + Args.clearParam(); + } + + @Test + public void testAlreadyRegisteredSkipRegistration() { + TransactionRegister.registerActuator(); + assertTrue("First registration should be completed", TransactionRegister.isRegistered()); + + TransactionRegister.registerActuator(); + assertTrue("Registration should still be true", TransactionRegister.isRegistered()); + } + + @Test + public void testConcurrentAccessThreadSafe() throws InterruptedException { + final int threadCount = 5; + Thread[] threads = new Thread[threadCount]; + final AtomicBoolean testPassed = new AtomicBoolean(true); + + for (int i = 0; i < threadCount; i++) { + threads[i] = new Thread(() -> { + try { + TransactionRegister.registerActuator(); + } catch (Exception e) { + testPassed.set(false); + } + }); + } + + for (Thread thread : threads) { + thread.start(); + } + + for (Thread thread : threads) { + thread.join(); + } + + assertTrue("All threads should complete without exceptions", testPassed.get()); + assertTrue("Registration should be completed", TransactionRegister.isRegistered()); + } + + @Test + public void testDoubleCheckLockingAtomicBoolean() { + assertFalse("Initial registration state should be false", TransactionRegister.isRegistered()); + + TransactionRegister.registerActuator(); + assertTrue("After first call, should be registered", TransactionRegister.isRegistered()); + + TransactionRegister.registerActuator(); + assertTrue("After second call, should still be registered", TransactionRegister.isRegistered()); + } + + @Test + public void testRegistrationRunsExactlyOnce() { + final AtomicInteger constructorCallCount = new AtomicInteger(0); + + try (MockedConstruction ignored = mockConstruction(Reflections.class, + (mock, context) -> { + constructorCallCount.incrementAndGet(); + when(mock.getSubTypesOf(AbstractActuator.class)).thenReturn(Collections.emptySet()); + })) { + + // Call multiple times; Reflections should only be constructed once + for (int i = 0; i < 5; i++) { + TransactionRegister.registerActuator(); + } + + assertEquals("Reflections should be constructed exactly once regardless of call count", + 1, constructorCallCount.get()); + assertTrue(TransactionRegister.isRegistered()); + } + } + + @Test + public void testMultipleCallsConsistency() { + assertFalse("Should start unregistered", TransactionRegister.isRegistered()); + + TransactionRegister.registerActuator(); + assertTrue("Should be registered after first call", TransactionRegister.isRegistered()); + + for (int i = 0; i < 5; i++) { + TransactionRegister.registerActuator(); + assertTrue("Should remain registered after call " + (i + 2), + TransactionRegister.isRegistered()); + } + } + + @Test + public void testThrowsTronError() { + try (MockedConstruction ignored = mockConstruction(Reflections.class, + (mock, context) -> when(mock.getSubTypesOf(AbstractActuator.class)) + .thenReturn(Collections.singleton(TransferActuator.class))); + MockedConstruction ignored1 = mockConstruction(TransferActuator.class, + (mock, context) -> { + throw new RuntimeException("boom"); + })) { + TronError error = assertThrows(TronError.class, TransactionRegister::registerActuator); + assertEquals(TronError.ErrCode.ACTUATOR_REGISTER, error.getErrCode()); + assertTrue(error.getMessage().contains("TransferActuator")); + } + } +} \ No newline at end of file diff --git a/framework/src/test/resources/logback-test.xml b/framework/src/test/resources/logback-test.xml index 9cf4a04062f..cc8c84e831f 100644 --- a/framework/src/test/resources/logback-test.xml +++ b/framework/src/test/resources/logback-test.xml @@ -1,40 +1,42 @@ - - - - - - - %d{HH:mm:ss.SSS} %p [%c{1}] %m%n - - - INFO - - - - - ./logs/tron-test.log - - - ./logs/tron-test-%d{yyyy-MM-dd}.%i.log.zip - - - 100MB - 60 - 20GB - - - %d{HH:mm:ss.SSS} %p [%c{1}] %m%n - - - - - - - - - - - - + + + + + + + %d{HH:mm:ss.SSS} %p [%c{1}] %m%n + + + ${console.log.level:-ERROR} + + + + + ./logs/tron-test.log + 8192 + true + + + ./logs/tron-test-%d{yyyy-MM-dd}.%i.log.zip + + + 100MB + 60 + 20GB + + + %d{HH:mm:ss.SSS} %p [%c{1}] %m%n + + + + + + + + + + + + From 7c8746e6beeb7da70b2f1dbeae654d47c2949bd9 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Wed, 1 Apr 2026 11:38:35 +0800 Subject: [PATCH 08/14] fix(test): catch Throwable instead of Exception in concurrent registration test TronError extends Error, not Exception, so use Throwable to ensure worker thread failures are properly captured. Co-Authored-By: Claude Opus 4.6 --- .../test/java/org/tron/core/utils/TransactionRegisterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java index cb87abff14f..ea622213f45 100644 --- a/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java +++ b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java @@ -56,7 +56,7 @@ public void testConcurrentAccessThreadSafe() throws InterruptedException { threads[i] = new Thread(() -> { try { TransactionRegister.registerActuator(); - } catch (Exception e) { + } catch (Throwable e) { testPassed.set(false); } }); From dd349f30a43aab438139bac5b138d032ec0e9fd3 Mon Sep 17 00:00:00 2001 From: vividctrlalt Date: Wed, 1 Apr 2026 07:03:41 +0000 Subject: [PATCH 09/14] refactor(config): replace manual parsing with ConfigBeanFactory binding Replace ~850 lines of manual if/hasPath/getXxx blocks in Args.applyConfigParams() with ConfigBeanFactory.create() automatic binding. Each config.conf domain now maps to a typed Java bean class (VmConfig, BlockConfig, CommitteeConfig, MetricsConfig, NodeConfig, EventConfig, StorageConfig, etc.). - Delete ConfigKey.java (~100 string constants), config.conf is sole source of truth - Migrate Storage.java static getters to read from StorageConfig bean - Add unit tests for all config bean classes - Migrate DynamicArgs to use bean binding --- .../parameter/RateLimiterInitialization.java | 12 + .../tron/core/config/args/BlockConfig.java | 63 + .../core/config/args/CommitteeConfig.java | 210 ++ .../tron/core/config/args/EventConfig.java | 148 ++ .../tron/core/config/args/GenesisConfig.java | 59 + .../core/config/args/LocalWitnessConfig.java | 35 + .../tron/core/config/args/MetricsConfig.java | 55 + .../org/tron/core/config/args/MiscConfig.java | 69 + .../org/tron/core/config/args/NodeConfig.java | 537 +++++ .../core/config/args/RateLimiterConfig.java | 83 + .../org/tron/core/config/args/Storage.java | 300 +-- .../tron/core/config/args/StorageConfig.java | 203 ++ .../org/tron/core/config/args/VmConfig.java | 80 + .../core/config/args/BlockConfigTest.java | 54 + .../core/config/args/CommitteeConfigTest.java | 69 + .../core/config/args/EventConfigTest.java | 73 + .../core/config/args/GenesisConfigTest.java | 50 + .../config/args/LocalWitnessConfigTest.java | 40 + .../core/config/args/MetricsConfigTest.java | 40 + .../tron/core/config/args/MiscConfigTest.java | 46 + .../tron/core/config/args/NodeConfigTest.java | 91 + .../config/args/RateLimiterConfigTest.java | 46 + .../core/config/args/StorageConfigTest.java | 59 + .../tron/core/config/args/VmConfigTest.java | 65 + .../java/org/tron/core/config/args/Args.java | 1753 ++++++----------- .../org/tron/core/config/args/ConfigKey.java | 331 ---- .../tron/core/config/args/DynamicArgs.java | 9 +- .../org/tron/core/config/args/ArgsTest.java | 2 +- 28 files changed, 2947 insertions(+), 1635 deletions(-) create mode 100644 common/src/main/java/org/tron/core/config/args/BlockConfig.java create mode 100644 common/src/main/java/org/tron/core/config/args/CommitteeConfig.java create mode 100644 common/src/main/java/org/tron/core/config/args/EventConfig.java create mode 100644 common/src/main/java/org/tron/core/config/args/GenesisConfig.java create mode 100644 common/src/main/java/org/tron/core/config/args/LocalWitnessConfig.java create mode 100644 common/src/main/java/org/tron/core/config/args/MetricsConfig.java create mode 100644 common/src/main/java/org/tron/core/config/args/MiscConfig.java create mode 100644 common/src/main/java/org/tron/core/config/args/NodeConfig.java create mode 100644 common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java create mode 100644 common/src/main/java/org/tron/core/config/args/StorageConfig.java create mode 100644 common/src/main/java/org/tron/core/config/args/VmConfig.java create mode 100644 common/src/test/java/org/tron/core/config/args/BlockConfigTest.java create mode 100644 common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java create mode 100644 common/src/test/java/org/tron/core/config/args/EventConfigTest.java create mode 100644 common/src/test/java/org/tron/core/config/args/GenesisConfigTest.java create mode 100644 common/src/test/java/org/tron/core/config/args/LocalWitnessConfigTest.java create mode 100644 common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java create mode 100644 common/src/test/java/org/tron/core/config/args/MiscConfigTest.java create mode 100644 common/src/test/java/org/tron/core/config/args/NodeConfigTest.java create mode 100644 common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java create mode 100644 common/src/test/java/org/tron/core/config/args/StorageConfigTest.java create mode 100644 common/src/test/java/org/tron/core/config/args/VmConfigTest.java delete mode 100644 framework/src/main/java/org/tron/core/config/args/ConfigKey.java diff --git a/common/src/main/java/org/tron/common/parameter/RateLimiterInitialization.java b/common/src/main/java/org/tron/common/parameter/RateLimiterInitialization.java index 9ae7eb7db68..41388adeb7b 100644 --- a/common/src/main/java/org/tron/common/parameter/RateLimiterInitialization.java +++ b/common/src/main/java/org/tron/common/parameter/RateLimiterInitialization.java @@ -74,6 +74,12 @@ public HttpRateLimiterItem(ConfigObject asset) { strategy = asset.get("strategy").unwrapped().toString(); params = asset.get("paramString").unwrapped().toString(); } + + public HttpRateLimiterItem(String component, String strategy, String params) { + this.component = component; + this.strategy = strategy; + this.params = params; + } } @@ -93,5 +99,11 @@ public RpcRateLimiterItem(ConfigObject asset) { strategy = asset.get("strategy").unwrapped().toString(); params = asset.get("paramString").unwrapped().toString(); } + + public RpcRateLimiterItem(String component, String strategy, String params) { + this.component = component; + this.strategy = strategy; + this.params = params; + } } } \ No newline at end of file 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 new file mode 100644 index 00000000000..eac5c974c83 --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/BlockConfig.java @@ -0,0 +1,63 @@ +package org.tron.core.config.args; + +import static org.tron.core.Constant.DEFAULT_PROPOSAL_EXPIRE_TIME; +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.exception.TronError.ErrCode.PARAMETER_INIT; + +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; +import org.tron.core.exception.TronError; + +/** + * Block configuration bean. Field names match config.conf keys under the "block" section. + */ +@Slf4j +@Getter +@Setter +public class BlockConfig { + + private boolean needSyncCheck = false; + private long maintenanceTimeInterval = 21600000L; + private long proposalExpireTime = DEFAULT_PROPOSAL_EXPIRE_TIME; + private int checkFrozenTime = 1; + + private static final Config DEFAULTS = ConfigFactory.parseString( + "needSyncCheck = false\n" + + "maintenanceTimeInterval = 21600000\n" + + "proposalExpireTime = " + DEFAULT_PROPOSAL_EXPIRE_TIME + "\n" + + "checkFrozenTime = 1\n" + ); + + /** + * Create BlockConfig from the "block" section of the application config. + * Also checks that committee.proposalExpireTime is not used (must use block.proposalExpireTime). + */ + public static BlockConfig fromConfig(Config config) { + // Reject legacy committee.proposalExpireTime location + if (config.hasPath("committee.proposalExpireTime")) { + throw new TronError("It is not allowed to configure committee.proposalExpireTime in " + + "config.conf, please set the value in block.proposalExpireTime.", PARAMETER_INIT); + } + + Config blockSection = config.hasPath("block") + ? config.getConfig("block").withFallback(DEFAULTS) + : DEFAULTS; + BlockConfig blockConfig = ConfigBeanFactory.create(blockSection, BlockConfig.class); + blockConfig.postProcess(); + return blockConfig; + } + + private void postProcess() { + if (proposalExpireTime <= MIN_PROPOSAL_EXPIRE_TIME + || proposalExpireTime >= MAX_PROPOSAL_EXPIRE_TIME) { + throw new TronError("The value[block.proposalExpireTime] is only allowed to " + + "be greater than " + MIN_PROPOSAL_EXPIRE_TIME + " and less than " + + MAX_PROPOSAL_EXPIRE_TIME + "!", PARAMETER_INIT); + } + } +} 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 new file mode 100644 index 00000000000..d5c5e0c3b92 --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java @@ -0,0 +1,210 @@ +package org.tron.core.config.args; + +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; + +/** + * Committee (governance) configuration bean. + * Field names match config.conf keys under the "committee" section. + * All fields are governance proposal toggles, default 0 (disabled). + */ +@Slf4j +@Getter +@Setter +@SuppressWarnings("unused") // setters used by ConfigBeanFactory via reflection +public class CommitteeConfig { + + private long allowCreationOfContracts = 0; + private long allowMultiSign = 0; + private long allowAdaptiveEnergy = 0; + private long allowDelegateResource = 0; + private long allowSameTokenName = 0; + private long allowTvmTransferTrc10 = 0; + private long allowTvmConstantinople = 0; + private long allowTvmSolidity059 = 0; + private long forbidTransferToContract = 0; + private long allowShieldedTRC20Transaction = 0; + private long allowMarketTransaction = 0; + private long allowTransactionFeePool = 0; + private long allowBlackHoleOptimization = 0; + private long allowNewResourceModel = 0; + private long allowTvmIstanbul = 0; + 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; + + public long getAllowPBFT() { return allowPBFT; } + public void setAllowPBFT(long v) { this.allowPBFT = v; } + public long getPBFTExpireNum() { return pBFTExpireNum; } + public void setPBFTExpireNum(long v) { this.pBFTExpireNum = v; } + private long allowTvmFreeze = 0; + private long allowTvmVote = 0; + private long allowTvmLondon = 0; + private long allowTvmCompatibleEvm = 0; + private long allowHigherLimitForMaxCpuTimeOfOneTx = 0; + private long allowNewRewardAlgorithm = 0; + private long allowOptimizedReturnValueOfChainId = 0; + private long allowTvmShangHai = 0; + private long allowOldRewardOpt = 0; + private long allowEnergyAdjustment = 0; + private long allowStrictMath = 0; + private long consensusLogicOptimization = 0; + private long allowTvmCancun = 0; + private long allowTvmBlob = 0; + private long unfreezeDelayDays = 0; + private long allowReceiptsMerkleRoot = 0; + private long allowAccountAssetOptimization = 0; + private long allowAssetOptimization = 0; + private long allowNewReward = 0; + private long memoFee = 0; + private long allowDelegateOptimization = 0; + private long allowDynamicEnergy = 0; + private long dynamicEnergyThreshold = 0; + private long dynamicEnergyIncreaseFactor = 0; + private long dynamicEnergyMaxFactor = 0; + + // proposalExpireTime is NOT a committee field — it's in block.* and handled by BlockConfig + + private static final Config DEFAULTS; + + static { + StringBuilder sb = new StringBuilder(); + sb.append("allowCreationOfContracts = 0\n"); + sb.append("allowMultiSign = 0\n"); + sb.append("allowAdaptiveEnergy = 0\n"); + sb.append("allowDelegateResource = 0\n"); + sb.append("allowSameTokenName = 0\n"); + sb.append("allowTvmTransferTrc10 = 0\n"); + sb.append("allowTvmConstantinople = 0\n"); + sb.append("allowTvmSolidity059 = 0\n"); + sb.append("forbidTransferToContract = 0\n"); + sb.append("allowShieldedTRC20Transaction = 0\n"); + sb.append("allowMarketTransaction = 0\n"); + sb.append("allowTransactionFeePool = 0\n"); + sb.append("allowBlackHoleOptimization = 0\n"); + sb.append("allowNewResourceModel = 0\n"); + sb.append("allowTvmIstanbul = 0\n"); + sb.append("allowProtoFilterNum = 0\n"); + sb.append("allowAccountStateRoot = 0\n"); + sb.append("changedDelegation = 0\n"); + sb.append("allowPBFT = 0\n"); + sb.append("pBFTExpireNum = 20\n"); + sb.append("allowTvmFreeze = 0\n"); + sb.append("allowTvmVote = 0\n"); + sb.append("allowTvmLondon = 0\n"); + sb.append("allowTvmCompatibleEvm = 0\n"); + sb.append("allowHigherLimitForMaxCpuTimeOfOneTx = 0\n"); + sb.append("allowNewRewardAlgorithm = 0\n"); + sb.append("allowOptimizedReturnValueOfChainId = 0\n"); + sb.append("allowTvmShangHai = 0\n"); + sb.append("allowOldRewardOpt = 0\n"); + sb.append("allowEnergyAdjustment = 0\n"); + sb.append("allowStrictMath = 0\n"); + sb.append("consensusLogicOptimization = 0\n"); + sb.append("allowTvmCancun = 0\n"); + sb.append("allowTvmBlob = 0\n"); + sb.append("unfreezeDelayDays = 0\n"); + sb.append("allowReceiptsMerkleRoot = 0\n"); + sb.append("allowAccountAssetOptimization = 0\n"); + sb.append("allowAssetOptimization = 0\n"); + sb.append("allowNewReward = 0\n"); + sb.append("memoFee = 0\n"); + sb.append("allowDelegateOptimization = 0\n"); + sb.append("allowDynamicEnergy = 0\n"); + sb.append("dynamicEnergyThreshold = 0\n"); + sb.append("dynamicEnergyIncreaseFactor = 0\n"); + sb.append("dynamicEnergyMaxFactor = 0\n"); + DEFAULTS = ConfigFactory.parseString(sb.toString()); + } + + /** + * 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. + */ + public static CommitteeConfig fromConfig(Config config) { + Config section = config.hasPath("committee") + ? config.getConfig("committee").withFallback(DEFAULTS) + : DEFAULTS; + + // ConfigBeanFactory derives key names from setter methods. For setPBFTExpireNum() + // it expects "PBFTExpireNum" (capital P), but config.conf uses "pBFTExpireNum". + // Similarly, getAllowPBFT() maps to "allowPBFT" which may be missing in test configs. + // Add uppercase aliases so ConfigBeanFactory can find them. + Config aliased = section; + if (section.hasPath("pBFTExpireNum") && !section.hasPath("PBFTExpireNum")) { + aliased = aliased.withValue("PBFTExpireNum", section.getValue("pBFTExpireNum")); + } + + CommitteeConfig cc = ConfigBeanFactory.create(aliased, CommitteeConfig.class); + // Ensure the manually-named fields get the right values from the original keys + cc.allowPBFT = section.hasPath("allowPBFT") ? section.getLong("allowPBFT") : 0; + cc.pBFTExpireNum = section.hasPath("pBFTExpireNum") ? section.getLong("pBFTExpireNum") : 20; + + cc.postProcess(); + return cc; + } + + private void postProcess() { + // clamp unfreezeDelayDays to 0-365 + if (unfreezeDelayDays < 0) { + unfreezeDelayDays = 0; + } + if (unfreezeDelayDays > 365) { + unfreezeDelayDays = 365; + } + + // clamp allowDelegateOptimization to 0-1 + if (allowDelegateOptimization < 0) { allowDelegateOptimization = 0; } + if (allowDelegateOptimization > 1) { allowDelegateOptimization = 1; } + + // clamp allowDynamicEnergy to 0-1 + if (allowDynamicEnergy < 0) { allowDynamicEnergy = 0; } + if (allowDynamicEnergy > 1) { allowDynamicEnergy = 1; } + + // clamp dynamicEnergyThreshold to 0-100_000_000_000_000_000 + if (dynamicEnergyThreshold < 0) { dynamicEnergyThreshold = 0; } + if (dynamicEnergyThreshold > 100_000_000_000_000_000L) { + dynamicEnergyThreshold = 100_000_000_000_000_000L; + } + + // clamp dynamicEnergyIncreaseFactor to 0-10_000 + if (dynamicEnergyIncreaseFactor < 0) { dynamicEnergyIncreaseFactor = 0; } + if (dynamicEnergyIncreaseFactor > 10_000L) { dynamicEnergyIncreaseFactor = 10_000L; } + + // clamp dynamicEnergyMaxFactor to 0-100_000 + if (dynamicEnergyMaxFactor < 0) { dynamicEnergyMaxFactor = 0; } + if (dynamicEnergyMaxFactor > 100_000L) { dynamicEnergyMaxFactor = 100_000L; } + + // cross-field: allowOldRewardOpt requires at least one reward/vote flag + if (allowOldRewardOpt == 1 && allowNewRewardAlgorithm != 1 + && allowNewReward != 1 && allowTvmVote != 1) { + throw new IllegalArgumentException( + "At least one of the following proposals is required to be opened first: " + + "committee.allowNewRewardAlgorithm = 1" + + " or committee.allowNewReward = 1" + + " or committee.allowTvmVote = 1."); + } + } +} 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 new file mode 100644 index 00000000000..31cae06fd30 --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/EventConfig.java @@ -0,0 +1,148 @@ +package org.tron.core.config.args; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigBeanFactory; +import com.typesafe.config.ConfigFactory; +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * Event subscribe configuration bean. + * Field names match config.conf keys under "event.subscribe". + */ +@Slf4j +@Getter +@Setter +public class EventConfig { + + private boolean enable = false; + private int version = 0; + private long startSyncBlockNum = 0; + private String path = ""; + private String server = ""; + private String dbconfig = ""; + private boolean contractParse = true; + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + 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(). + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private List topics = new ArrayList<>(); + + 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; + } + + @Getter + @Setter + public static class TopicConfig { + private String triggerName = ""; + private boolean enable = false; + private String topic = ""; + private boolean solidified = false; + private boolean ethCompatible = false; + private boolean redundancy = false; + } + + @Getter + @Setter + public static class FilterConfig { + private String fromblock = ""; + private String toblock = ""; + private List contractAddress = new ArrayList<>(); + private List contractTopic = new ArrayList<>(); + } + + private static final Config DEFAULTS = ConfigFactory.parseString( + "enable = false\n" + + "version = 0\n" + + "startSyncBlockNum = 0\n" + + "path = \"\"\n" + + "server = \"\"\n" + + "dbconfig = \"\"\n" + + "contractParse = true\n" + + "native { useNativeQueue = true, bindport = 5555, sendqueuelength = 1000 }\n" + + "topics = []\n" + + "topicDefaults { triggerName = \"\", enable = false, topic = \"\"," + + " solidified = false, ethCompatible = false, redundancy = false }\n" + + "filter { fromblock = \"\", toblock = \"\"," + + " contractAddress = [\"\"], contractTopic = [\"\"] }\n" + ); + + /** + * Create EventConfig from the "event.subscribe" section of the application config. + * + *

Note: HOCON key "native" is a Java reserved word, so the bean field is named + * "nativeQueue" but config key is "native". We handle this manually after binding. + */ + public static EventConfig fromConfig(Config config) { + Config section = config.hasPath("event.subscribe") + ? config.getConfig("event.subscribe").withFallback(DEFAULTS) + : DEFAULTS; + + // "native" is a Java reserved word, "topics" has optional fields per item — + // strip both before binding, read manually + Config bindable = section.withoutPath("native").withoutPath("topics") + .withoutPath("topicDefaults"); + EventConfig ec = ConfigBeanFactory.create(bindable, EventConfig.class); + + // manually bind "native" sub-section + Config nativeSection = section.hasPath("native") + ? section.getConfig("native") : 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("topics")) { + ec.topics = new ArrayList<>(); + for (com.typesafe.config.ConfigObject obj : section.getObjectList("topics")) { + 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); + } + } + + return ec; + } +} 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 new file mode 100644 index 00000000000..97efb00ab3e --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/GenesisConfig.java @@ -0,0 +1,59 @@ +package org.tron.core.config.args; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigBeanFactory; +import com.typesafe.config.ConfigFactory; +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * Genesis block configuration bean. + * Field names match config.conf keys under "genesis.block". + * Assets and witnesses are stored as raw bean lists; address decoding + * (e.g. Base58Check) is done in the bridge method, not here. + */ +@Slf4j +@Getter +@Setter +public class GenesisConfig { + + private String timestamp = ""; + private String parentHash = ""; + private List assets = new ArrayList<>(); + private List witnesses = new ArrayList<>(); + + @Getter + @Setter + public static class AssetConfig { + private String accountName = ""; + private String accountType = ""; + private String address = ""; + private String balance = ""; + } + + @Getter + @Setter + public static class WitnessConfig { + private String address = ""; + private String url = ""; + private long voteCount = 0; + } + + private static final Config DEFAULTS = ConfigFactory.parseString( + "timestamp = \"\"\n" + + "parentHash = \"\"\n" + + "assets = []\n" + + "witnesses = []\n" + ); + + public static GenesisConfig fromConfig(Config config) { + if (!config.hasPath("genesis.block")) { + return new GenesisConfig(); + } + Config section = config.getConfig("genesis.block").withFallback(DEFAULTS); + return ConfigBeanFactory.create(section, GenesisConfig.class); + } +} diff --git a/common/src/main/java/org/tron/core/config/args/LocalWitnessConfig.java b/common/src/main/java/org/tron/core/config/args/LocalWitnessConfig.java new file mode 100644 index 00000000000..8a2cd2ce9e4 --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/LocalWitnessConfig.java @@ -0,0 +1,35 @@ +package org.tron.core.config.args; + +import com.typesafe.config.Config; +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +/** + * Local witness configuration bean. + * Reads top-level config keys: localwitness, localWitnessAccountAddress, localwitnesskeystore. + * These are not under a sub-section — they are at the root of config.conf. + */ +@Slf4j +@Getter +public class LocalWitnessConfig { + + private List privateKeys = new ArrayList<>(); + private String accountAddress = null; + private List keystores = new ArrayList<>(); + + public static LocalWitnessConfig fromConfig(Config config) { + LocalWitnessConfig lw = new LocalWitnessConfig(); + if (config.hasPath("localwitness")) { + lw.privateKeys = config.getStringList("localwitness"); + } + if (config.hasPath("localWitnessAccountAddress")) { + lw.accountAddress = config.getString("localWitnessAccountAddress"); + } + if (config.hasPath("localwitnesskeystore")) { + lw.keystores = config.getStringList("localwitnesskeystore"); + } + return lw; + } +} 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 new file mode 100644 index 00000000000..708d23cfac8 --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/MetricsConfig.java @@ -0,0 +1,55 @@ +package org.tron.core.config.args; + +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; + +/** + * Metrics configuration bean. Field names match config.conf keys under "node.metrics". + * Contains nested sub-beans for prometheus and influxdb sections. + */ +@Slf4j +@Getter +@Setter +public class MetricsConfig { + + private boolean storageEnable = false; + private PrometheusConfig prometheus = new PrometheusConfig(); + private InfluxDbConfig influxdb = new InfluxDbConfig(); + + @Getter + @Setter + public static class PrometheusConfig { + private boolean enable = false; + private int port = 9527; + } + + @Getter + @Setter + public static class InfluxDbConfig { + private String ip = ""; + private int port = 8086; + private String database = "metrics"; + private int metricsReportInterval = 10; + } + + private static final Config DEFAULTS = ConfigFactory.parseString( + "storageEnable = false\n" + + "prometheus { enable = false, port = 9527 }\n" + + "influxdb { ip = \"\", port = 8086, database = metrics," + + " metricsReportInterval = 10 }\n" + ); + + /** + * Create MetricsConfig from the "node.metrics" section of the application config. + */ + public static MetricsConfig fromConfig(Config config) { + 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/MiscConfig.java b/common/src/main/java/org/tron/core/config/args/MiscConfig.java new file mode 100644 index 00000000000..fa8b4f5d90f --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/MiscConfig.java @@ -0,0 +1,69 @@ +package org.tron.core.config.args; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.tron.core.Constant; + +/** + * Miscellaneous small config domains that don't warrant their own bean class. + * Covers: storage (partial), trx, energy, crypto, seed, actuator. + * + *

These use manual reads because they span multiple unrelated config.conf + * top-level sections and some have non-standard key naming (e.g. "enery" typo). + */ +@Slf4j +@Getter +public class MiscConfig { + + private boolean needToUpdateAsset = true; + private boolean historyBalanceLookup = false; + private String trxReferenceBlock = "solid"; + private long trxExpirationTimeInMilliseconds = Constant.TRANSACTION_DEFAULT_EXPIRATION_TIME; + private long blockNumForEnergyLimit = 4727890L; + private String cryptoEngine = Constant.ECKey_ENGINE; + private List seedNodeIpList = new ArrayList<>(); + private Set actuatorWhitelist = Collections.emptySet(); + + public static MiscConfig fromConfig(Config config) { + MiscConfig mc = new MiscConfig(); + + // storage + mc.needToUpdateAsset = !config.hasPath("storage.needToUpdateAsset") + || config.getBoolean("storage.needToUpdateAsset"); + mc.historyBalanceLookup = config.hasPath("storage.balance.history.lookup") + && config.getBoolean("storage.balance.history.lookup"); + + // trx + mc.trxReferenceBlock = config.hasPath("trx.reference.block") + ? config.getString("trx.reference.block") : "solid"; + if (config.hasPath("trx.expiration.timeInMilliseconds") + && config.getLong("trx.expiration.timeInMilliseconds") > 0) { + mc.trxExpirationTimeInMilliseconds = config.getLong("trx.expiration.timeInMilliseconds"); + } + + // energy (note: config key has typo "enery" — preserved for backward compat) + mc.blockNumForEnergyLimit = config.hasPath("enery.limit.block.num") + ? config.getInt("enery.limit.block.num") : 4727890L; + + // crypto + mc.cryptoEngine = config.hasPath("crypto.engine") + ? config.getString("crypto.engine") : Constant.ECKey_ENGINE; + + // seed node + mc.seedNodeIpList = config.hasPath("seed.node.ip.list") + ? config.getStringList("seed.node.ip.list") : new ArrayList<>(); + + // actuator + mc.actuatorWhitelist = config.hasPath("actuator.whitelist") + ? new HashSet<>(config.getStringList("actuator.whitelist")) : Collections.emptySet(); + + return mc; + } +} 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 new file mode 100644 index 00000000000..1c049bf16bc --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/NodeConfig.java @@ -0,0 +1,537 @@ +package org.tron.core.config.args; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigBeanFactory; +import com.typesafe.config.ConfigFactory; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * Node configuration bean for the "node" section of config.conf. + * + *

This section is complex: it mixes flat scalars, dot-notation nested keys + * (e.g. "listen.port"), sub-objects (http, rpc, jsonrpc, p2p, dynamicConfig, dns), + * and list fields (active, passive, fastForward, disabledApi). + * + *

Strategy: + *

    + *
  • ConfigBeanFactory handles simple scalar fields and clean sub-objects
  • + *
  • Dot-notation fields (listen.port, connection.timeout, fetchBlock.timeout, + * solidity.threads) are read manually — HOCON parses them as nested objects, + * not flat keys, so ConfigBeanFactory cannot bind them to flat fields
  • + *
  • PBFT-named fields in sub-beans have the same JavaBean naming issue as + * CommitteeConfig — handled manually after binding
  • + *
  • List fields are read manually since ConfigBeanFactory expects bean lists
  • + *
+ */ +@Slf4j +@Getter +@Setter +@SuppressWarnings("unused") // setters used by ConfigBeanFactory via reflection +public class NodeConfig { + + // ---- Flat scalar fields (auto-bound by ConfigBeanFactory) ---- + private String trustNode = "127.0.0.1:50051"; + private boolean walletExtensionApi = true; + private int syncFetchBatchNum = 2000; + private int validateSignThreadNum = Runtime.getRuntime().availableProcessors(); + private int maxConnections = 30; + private int minConnections = 8; + private int minActiveConnections = 3; + private int maxConnectionsWithSameIp = 2; + private int maxHttpConnectNumber = 50; + private int minParticipationRate = 15; + private boolean openPrintLog = true; + private boolean openTransactionSort = false; + private int maxTps = 1000; + // "isOpenFullTcpDisconnect" in config.conf: JavaBean convention converts + // setOpenFullTcpDisconnect -> key "openFullTcpDisconnect", but config uses + // "isOpenFullTcpDisconnect". Excluded from auto-binding, read manually. + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private boolean isOpenFullTcpDisconnect = false; + + public boolean isOpenFullTcpDisconnect() { return isOpenFullTcpDisconnect; } + + // node.discovery.* and node.channel.read.timeout — dot-notation, manually read + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private boolean discoveryEnable = false; + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private boolean discoveryPersist = false; + + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private String discoveryExternalIp = ""; + + // node.shutdown.* — dot-notation, manually read + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private String shutdownBlockTime = ""; + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private long shutdownBlockHeight = -1; + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private long shutdownBlockCount = -1; + + public boolean isDiscoveryEnable() { return discoveryEnable; } + public boolean isDiscoveryPersist() { return discoveryPersist; } + public String getDiscoveryExternalIp() { return discoveryExternalIp; } + 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 = 75; + private int netMaxTrxPerSecond = 700; + private boolean nodeDetectEnable = false; + private boolean enableIpv6 = false; + private boolean effectiveCheckEnable = false; + private int maxFastForwardNum = 4; + private int tcpNettyWorkThreadNum = 0; + private int udpNettyWorkThreadNum = 1; + private int validContractProtoThreads = 2; + private int shieldedTransInPendingMaxCounts = 10; + private long blockCacheTimeout = 60; + private long receiveTcpMinDataLength = 2048; + private int channelReadTimeout = 60; + private int maxTransactionPendingSize = 2000; + private long pendingTransactionTimeout = 60000; + private int agreeNodeCount = 0; + private boolean openHistoryQueryWhenLiteFN = false; + private boolean unsolidifiedBlockCheck = false; + private int maxUnsolidifiedBlocks = 54; + private String zenTokenId = "000000"; + private boolean allowShieldedTransactionApi = true; + private double activeConnectFactor = 0.1; + private double connectFactor = 0.6; + private double disconnectNumberFactor = 0.4; + private int maxActiveNodesWithSameIp = 2; + + // ---- Dot-notation fields (manually read — HOCON treats them as nested) ---- + // Excluded from ConfigBeanFactory auto-binding because HOCON parses dot-notation + // keys (listen.port, connection.timeout, fetchBlock.timeout, solidity.threads) as + // nested objects, not flat keys. ConfigBeanFactory expects flat key "listenPort" but + // config has "listen { port }". Read manually in fromConfig(). + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private int listenPort = 18888; + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private int connectionTimeout = 2; + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private int fetchBlockTimeout = 200; + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private int solidityThreads = Runtime.getRuntime().availableProcessors(); + + public int getListenPort() { return listenPort; } + public int getConnectionTimeout() { return connectionTimeout; } + public int getFetchBlockTimeout() { return fetchBlockTimeout; } + public int getSolidityThreads() { return solidityThreads; } + + // ---- List fields (manually read) ---- + private List active = new ArrayList<>(); + private List passive = new ArrayList<>(); + private List fastForward = new ArrayList<>(); + private List disabledApi = new ArrayList<>(); + + // ---- Sub-object fields ---- + private P2pConfig p2p = new P2pConfig(); + private HttpConfig http = new HttpConfig(); + private RpcConfig rpc = new RpcConfig(); + private JsonRpcConfig jsonrpc = new JsonRpcConfig(); + private NodeBackupConfig backup = new NodeBackupConfig(); + private DynamicConfigSection dynamicConfig = new DynamicConfigSection(); + private DnsConfig dns = new DnsConfig(); + + // =========================================================================== + // Inner static classes for sub-beans + // =========================================================================== + + @Getter + @Setter + public static class P2pConfig { + private int version = 11111; + } + + @Getter + @Setter + public static class HttpConfig { + private boolean fullNodeEnable = true; + 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) + 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) + 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 = 16; + private int maxConcurrentCallsPerConnection = 100; + private int flowControlWindow = 0; + private long maxConnectionIdleInMillis = Long.MAX_VALUE; + private long maxConnectionAgeInMillis = Long.MAX_VALUE; + private int maxMessageSize = 0; + private int maxHeaderListSize = 0; + private int maxRstStream = 0; + private int secondsPerWindow = 0; + private int minEffectiveConnection = 0; + private boolean reflectionService = false; + private boolean trxCacheEnable = false; + } + + @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) + 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 = 0; + } + + @Getter + @Setter + public static class NodeBackupConfig { + private int priority = 0; + private int port = 10001; + private int keepAliveInterval = 3000; + private List members = new ArrayList<>(); + } + + @Getter + @Setter + public static class DynamicConfigSection { + private boolean enable = false; + private long checkInterval = 600; + } + + @Getter + @Setter + public static class DnsConfig { + private List treeUrls = new ArrayList<>(); + private boolean publish = false; + private String dnsDomain = ""; + private String dnsPrivate = ""; + private List knownUrls = new ArrayList<>(); + private List staticNodes = new ArrayList<>(); + private int maxMergeSize = 0; + private double changeThreshold = 0.0; + private String serverType = ""; + private String accessKeyId = ""; + private String accessKeySecret = ""; + private String aliyunDnsEndpoint = ""; + private String awsRegion = ""; + private String awsHostZoneId = ""; + } + + // =========================================================================== + // DEFAULTS + // =========================================================================== + + private static final Config DEFAULTS; + + static { + int cpus = Runtime.getRuntime().availableProcessors(); + StringBuilder sb = new StringBuilder(); + // flat scalars + sb.append("trustNode = \"127.0.0.1:50051\"\n"); + sb.append("walletExtensionApi = true\n"); + sb.append("syncFetchBatchNum = 2000\n"); + sb.append("validateSignThreadNum = ").append(cpus).append("\n"); + sb.append("maxConnections = 30\n"); + sb.append("minConnections = 8\n"); + sb.append("minActiveConnections = 3\n"); + sb.append("maxConnectionsWithSameIp = 2\n"); + sb.append("maxHttpConnectNumber = 50\n"); + sb.append("minParticipationRate = 15\n"); + sb.append("openPrintLog = true\n"); + sb.append("openTransactionSort = false\n"); + sb.append("maxTps = 1000\n"); + // isOpenFullTcpDisconnect excluded from auto-binding — read manually in fromConfig() + sb.append("inactiveThreshold = 600\n"); + sb.append("metricsEnable = false\n"); + sb.append("blockProducedTimeOut = 75\n"); + sb.append("netMaxTrxPerSecond = 700\n"); + sb.append("nodeDetectEnable = false\n"); + sb.append("enableIpv6 = false\n"); + sb.append("effectiveCheckEnable = false\n"); + sb.append("maxFastForwardNum = 4\n"); + sb.append("tcpNettyWorkThreadNum = 0\n"); + sb.append("udpNettyWorkThreadNum = 1\n"); + sb.append("validContractProtoThreads = 2\n"); + sb.append("shieldedTransInPendingMaxCounts = 10\n"); + sb.append("blockCacheTimeout = 60\n"); + sb.append("receiveTcpMinDataLength = 2048\n"); + sb.append("channelReadTimeout = 60\n"); + sb.append("maxTransactionPendingSize = 2000\n"); + sb.append("pendingTransactionTimeout = 60000\n"); + sb.append("agreeNodeCount = 0\n"); + sb.append("openHistoryQueryWhenLiteFN = false\n"); + sb.append("unsolidifiedBlockCheck = false\n"); + sb.append("maxUnsolidifiedBlocks = 54\n"); + sb.append("zenTokenId = \"000000\"\n"); + sb.append("allowShieldedTransactionApi = true\n"); + sb.append("activeConnectFactor = 0.1\n"); + sb.append("connectFactor = 0.6\n"); + sb.append("disconnectNumberFactor = 0.4\n"); + sb.append("maxActiveNodesWithSameIp = 2\n"); + // dot-notation fields (HOCON nests them automatically) + sb.append("listen { port = 18888 }\n"); + sb.append("connection { timeout = 2 }\n"); + sb.append("fetchBlock { timeout = 200 }\n"); + sb.append("solidity { threads = ").append(cpus).append(" }\n"); + // sub-objects + sb.append("p2p { version = 11111 }\n"); + sb.append("http { fullNodeEnable = true, fullNodePort = 8090,"); + sb.append(" solidityEnable = true, solidityPort = 8091,"); + sb.append(" PBFTEnable = true, PBFTPort = 8092 }\n"); + sb.append("rpc { enable = true, port = 50051,"); + sb.append(" solidityEnable = true, solidityPort = 50061,"); + sb.append(" PBFTEnable = true, PBFTPort = 50071,"); + sb.append(" thread = 16, maxConcurrentCallsPerConnection = 100,"); + sb.append(" flowControlWindow = 0, maxConnectionIdleInMillis = "); + sb.append(Long.MAX_VALUE).append(","); + sb.append(" maxConnectionAgeInMillis = ").append(Long.MAX_VALUE).append(","); + sb.append(" maxMessageSize = 0, maxHeaderListSize = 0,"); + sb.append(" maxRstStream = 0, secondsPerWindow = 0,"); + sb.append(" minEffectiveConnection = 0, reflectionService = false,"); + sb.append(" trxCacheEnable = false }\n"); + sb.append("jsonrpc { httpFullNodeEnable = false, httpFullNodePort = 8545,"); + sb.append(" httpSolidityEnable = false, httpSolidityPort = 8555,"); + sb.append(" httpPBFTEnable = false, httpPBFTPort = 8565,"); + sb.append(" maxBlockRange = 5000, maxSubTopics = 1000, maxBlockFilterNum = 50000 }\n"); + sb.append("dynamicConfig { enable = false, checkInterval = 600 }\n"); + sb.append("backup { priority = 0, port = 10001, keepAliveInterval = 3000, members = [] }\n"); + sb.append("dns { treeUrls = [], publish = false, dnsDomain = \"\","); + sb.append(" dnsPrivate = \"\", knownUrls = [], staticNodes = [],"); + sb.append(" maxMergeSize = 0, changeThreshold = 0.0, serverType = \"\","); + sb.append(" accessKeyId = \"\", accessKeySecret = \"\","); + sb.append(" aliyunDnsEndpoint = \"\", awsRegion = \"\", awsHostZoneId = \"\" }\n"); + // list fields + sb.append("active = []\n"); + sb.append("passive = []\n"); + sb.append("fastForward = []\n"); + sb.append("disabledApi = []\n"); + DEFAULTS = ConfigFactory.parseString(sb.toString()); + } + + // =========================================================================== + // Factory method + // =========================================================================== + + /** + * Create NodeConfig from the "node" section of the application config. + * + *

Dot-notation keys (listen.port, connection.timeout, fetchBlock.timeout, + * 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. + * + *

List fields (active, passive, fastForward, disabledApi) are read manually + * since ConfigBeanFactory expects typed bean lists, not string lists. + */ + public static NodeConfig fromConfig(Config config) { + Config section = config.hasPath("node") + ? config.getConfig("node").withFallback(DEFAULTS) + : DEFAULTS; + + // --- Phase 1: Auto-bind flat scalars and sub-objects --- + // ConfigBeanFactory will bind all simple fields and nested sub-beans. + // It will skip dot-notation fields (they are nested objects, not scalar keys) + // and may mis-bind PBFT fields due to JavaBean naming. + NodeConfig nc = ConfigBeanFactory.create(section, NodeConfig.class); + + // --- Phase 2: Dot-notation and naming-mismatch fields (manually read) --- + nc.listenPort = getInt(section, "listen.port", 18888); + nc.connectionTimeout = getInt(section, "connection.timeout", 2); + nc.fetchBlockTimeout = getInt(section, "fetchBlock.timeout", 200); + nc.solidityThreads = getInt(section, "solidity.threads", + Runtime.getRuntime().availableProcessors()); + nc.isOpenFullTcpDisconnect = getBool(section, "isOpenFullTcpDisconnect", false); + + // Legacy key fallback: node.maxActiveNodes (old) -> maxConnections (new) + if (section.hasPath("maxActiveNodes")) { + nc.maxConnections = section.getInt("maxActiveNodes"); + if (section.hasPath("connectFactor")) { + nc.minConnections = (int) (nc.maxConnections * section.getDouble("connectFactor")); + } + if (section.hasPath("activeConnectFactor")) { + nc.minActiveConnections = (int) (nc.maxConnections + * section.getDouble("activeConnectFactor")); + } + } + if (section.hasPath("maxActiveNodesWithSameIp")) { + nc.maxConnectionsWithSameIp = section.getInt("maxActiveNodesWithSameIp"); + } + + // Legacy key fallback: node.fullNodeAllowShieldedTransaction -> allowShieldedTransactionApi + if (section.hasPath("allowShieldedTransactionApi")) { + nc.allowShieldedTransactionApi = section.getBoolean("allowShieldedTransactionApi"); + } else if (section.hasPath("fullNodeAllowShieldedTransaction")) { + nc.allowShieldedTransactionApi = section.getBoolean("fullNodeAllowShieldedTransaction"); + } + nc.discoveryExternalIp = config.hasPath("node.discovery.external.ip") + ? config.getString("node.discovery.external.ip").trim() : ""; + + // node.discovery.* — dot-notation creates nested HOCON objects + Config discoverySection = config.hasPath("node.discovery") + ? config.getConfig("node.discovery") : ConfigFactory.empty(); + nc.discoveryEnable = getBool(discoverySection, "enable", false); + nc.discoveryPersist = getBool(discoverySection, "persist", false); + + // node.shutdown.* — dot-notation + nc.shutdownBlockTime = config.hasPath("node.shutdown.BlockTime") + ? config.getString("node.shutdown.BlockTime") : ""; + nc.shutdownBlockHeight = config.hasPath("node.shutdown.BlockHeight") + ? config.getLong("node.shutdown.BlockHeight") : -1; + nc.shutdownBlockCount = config.hasPath("node.shutdown.BlockCount") + ? config.getLong("node.shutdown.BlockCount") : -1; + + // node.channel.read.timeout — triple-dot-notation + nc.channelReadTimeout = config.hasPath("node.channel.read.timeout") + ? config.getInt("node.channel.read.timeout") : 0; + + // --- Phase 3: PBFT fields in sub-beans (manually patch) --- + // http + Config httpSection = section.hasPath("http") + ? section.getConfig("http") : ConfigFactory.empty(); + nc.http.pBFTEnable = getBool(httpSection, "PBFTEnable", true); + nc.http.pBFTPort = getInt(httpSection, "PBFTPort", 8092); + + // rpc + Config rpcSection = section.hasPath("rpc") + ? section.getConfig("rpc") : ConfigFactory.empty(); + nc.rpc.pBFTEnable = getBool(rpcSection, "PBFTEnable", true); + nc.rpc.pBFTPort = getInt(rpcSection, "PBFTPort", 50071); + + // jsonrpc + Config jsonrpcSection = section.hasPath("jsonrpc") + ? section.getConfig("jsonrpc") : ConfigFactory.empty(); + nc.jsonrpc.httpPBFTEnable = getBool(jsonrpcSection, "httpPBFTEnable", false); + nc.jsonrpc.httpPBFTPort = getInt(jsonrpcSection, "httpPBFTPort", 8565); + + // --- Phase 4: List fields (manually read) --- + nc.active = getStringList(section, "active"); + nc.passive = getStringList(section, "passive"); + nc.fastForward = getStringList(section, "fastForward"); + nc.disabledApi = getStringList(section, "disabledApi"); + + return nc; + } + + // =========================================================================== + // Helper methods for safe config reads + // =========================================================================== + + private static int getInt(Config config, String path, int defaultValue) { + return config.hasPath(path) ? config.getInt(path) : defaultValue; + } + + private static long getLong(Config config, String path, long defaultValue) { + return config.hasPath(path) ? config.getLong(path) : defaultValue; + } + + private static boolean getBool(Config config, String path, boolean defaultValue) { + return config.hasPath(path) ? config.getBoolean(path) : defaultValue; + } + + private static String getString(Config config, String path, String defaultValue) { + return config.hasPath(path) ? config.getString(path) : defaultValue; + } + + private static List getStringList(Config config, String path) { + if (config.hasPath(path)) { + return config.getStringList(path); + } + return Collections.emptyList(); + } +} 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 new file mode 100644 index 00000000000..8974060eedf --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java @@ -0,0 +1,83 @@ +package org.tron.core.config.args; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigBeanFactory; +import com.typesafe.config.ConfigFactory; +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * Rate limiter configuration bean. + * Field names match config.conf keys under "rate.limiter". + */ +@Slf4j +@Getter +@Setter +public class RateLimiterConfig { + + private GlobalConfig global = new GlobalConfig(); + private P2pRateLimitConfig p2p = new P2pRateLimitConfig(); + private List http = new ArrayList<>(); + private List rpc = new ArrayList<>(); + + @Getter + @Setter + public static class GlobalConfig { + private int qps = 50000; + private IpConfig ip = new IpConfig(); + private ApiConfig api = new ApiConfig(); + + @Getter + @Setter + public static class IpConfig { + private int qps = 10000; + } + + @Getter + @Setter + public static class ApiConfig { + private int qps = 1000; + } + } + + @Getter + @Setter + public static class P2pRateLimitConfig { + private double syncBlockChain = 3.0; + private double fetchInvData = 3.0; + private double disconnect = 1.0; + } + + @Getter + @Setter + public static class HttpRateLimitItem { + private String component = ""; + private String strategy = ""; + private String paramString = ""; + } + + @Getter + @Setter + public static class RpcRateLimitItem { + private String component = ""; + private String strategy = ""; + private String paramString = ""; + } + + private static final Config DEFAULTS = ConfigFactory.parseString( + "global { qps = 50000, ip { qps = 10000 }, api { qps = 1000 } }\n" + + "p2p { syncBlockChain = 3.0, fetchInvData = 3.0, disconnect = 1.0 }\n" + + "http = []\n" + + "rpc = []\n" + ); + + public static RateLimiterConfig fromConfig(Config config) { + 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/Storage.java b/common/src/main/java/org/tron/core/config/args/Storage.java index 116074c62ee..4fd23fa2305 100644 --- a/common/src/main/java/org/tron/core/config/args/Storage.java +++ b/common/src/main/java/org/tron/core/config/args/Storage.java @@ -46,58 +46,7 @@ @Slf4j(topic = "db") public class Storage { - /** - * Keys (names) of database config - */ - private static final String DB_DIRECTORY_CONFIG_KEY = "storage.db.directory"; - private static final String DB_ENGINE_CONFIG_KEY = "storage.db.engine"; - private static final String DB_SYNC_CONFIG_KEY = "storage.db.sync"; - private static final String INDEX_DIRECTORY_CONFIG_KEY = "storage.index.directory"; - private static final String INDEX_SWITCH_CONFIG_KEY = "storage.index.switch"; - private static final String TRANSACTIONHISTORY_SWITCH_CONFIG_KEY = "storage.transHistory.switch"; - private static final String ESTIMATED_TRANSACTIONS_CONFIG_KEY = - "storage.txCache.estimatedTransactions"; - private static final String SNAPSHOT_MAX_FLUSH_COUNT_CONFIG_KEY = "storage.snapshot.maxFlushCount"; - private static final String PROPERTIES_CONFIG_KEY = "storage.properties"; - private static final String PROPERTIES_CONFIG_DB_KEY = "storage"; - private static final String PROPERTIES_CONFIG_DEFAULT_KEY = "default"; - private static final String PROPERTIES_CONFIG_DEFAULT_M_KEY = "defaultM"; - private static final String PROPERTIES_CONFIG_DEFAULT_L_KEY = "defaultL"; - private static final String DEFAULT_TRANSACTIONHISTORY_SWITCH = "on"; - - private static final String NAME_CONFIG_KEY = "name"; - private static final String PATH_CONFIG_KEY = "path"; - private static final String CREATE_IF_MISSING_CONFIG_KEY = "createIfMissing"; - private static final String PARANOID_CHECKS_CONFIG_KEY = "paranoidChecks"; - private static final String VERITY_CHECK_SUMS_CONFIG_KEY = "verifyChecksums"; - private static final String COMPRESSION_TYPE_CONFIG_KEY = "compressionType"; - private static final String BLOCK_SIZE_CONFIG_KEY = "blockSize"; - private static final String WRITE_BUFFER_SIZE_CONFIG_KEY = "writeBufferSize"; - private static final String CACHE_SIZE_CONFIG_KEY = "cacheSize"; - private static final String MAX_OPEN_FILES_CONFIG_KEY = "maxOpenFiles"; - private static final String EVENT_SUBSCRIBE_CONTRACT_PARSE = "event.subscribe.contractParse"; - - private static final String CHECKPOINT_VERSION_KEY = "storage.checkpoint.version"; - private static final String CHECKPOINT_SYNC_KEY = "storage.checkpoint.sync"; - - private static final String CACHE_STRATEGIES = "storage.cache.strategies"; - public static final String TX_CACHE_INIT_OPTIMIZATION = "storage.txCache.initOptimization"; - - private static final String MERKLE_ROOT = "storage.merkleRoot"; - - /** - * Default values of directory - */ - private static final String DEFAULT_DB_ENGINE = "LEVELDB"; - private static final boolean DEFAULT_DB_SYNC = false; - private static final boolean DEFAULT_EVENT_SUBSCRIBE_CONTRACT_PARSE = true; - private static final String DEFAULT_DB_DIRECTORY = "database"; - private static final String DEFAULT_INDEX_DIRECTORY = "index"; private static final String DEFAULT_INDEX_SWITCH = "on"; - private static final int DEFAULT_CHECKPOINT_VERSION = 1; - private static final boolean DEFAULT_CHECKPOINT_SYNC = true; - private static final int DEFAULT_ESTIMATED_TRANSACTIONS = 1000; - private static final int DEFAULT_SNAPSHOT_MAX_FLUSH_COUNT = 1; private Config storage; /** @@ -172,21 +121,22 @@ public class Storage { // db root private final Map dbRoots = Maps.newConcurrentMap(); + /** + * All getXxxFromConfig methods now read from StorageConfig bean instead of + * manual string constants. Signatures preserved for backward compatibility. + */ + public static String getDbEngineFromConfig(final Config config) { - return config.hasPath(DB_ENGINE_CONFIG_KEY) - ? config.getString(DB_ENGINE_CONFIG_KEY) : DEFAULT_DB_ENGINE; + return StorageConfig.fromConfig(config).getDb().getEngine(); } public static Boolean getDbVersionSyncFromConfig(final Config config) { - return config.hasPath(DB_SYNC_CONFIG_KEY) - ? config.getBoolean(DB_SYNC_CONFIG_KEY) : DEFAULT_DB_SYNC; + return StorageConfig.fromConfig(config).getDb().isSync(); } public static int getSnapshotMaxFlushCountFromConfig(final Config config) { - if (!config.hasPath(SNAPSHOT_MAX_FLUSH_COUNT_CONFIG_KEY)) { - return DEFAULT_SNAPSHOT_MAX_FLUSH_COUNT; - } - int maxFlushCountConfig = config.getInt(SNAPSHOT_MAX_FLUSH_COUNT_CONFIG_KEY); + int maxFlushCountConfig = StorageConfig.fromConfig(config) + .getSnapshot().getMaxFlushCount(); if (maxFlushCountConfig <= 0) { throw new IllegalArgumentException("MaxFlushCount value can not be negative or zero!"); } @@ -197,50 +147,39 @@ public static int getSnapshotMaxFlushCountFromConfig(final Config config) { } public static Boolean getContractParseSwitchFromConfig(final Config config) { - return config.hasPath(EVENT_SUBSCRIBE_CONTRACT_PARSE) - ? config.getBoolean(EVENT_SUBSCRIBE_CONTRACT_PARSE) - : DEFAULT_EVENT_SUBSCRIBE_CONTRACT_PARSE; + // contractParse is under event.subscribe, not storage — read from EventConfig + EventConfig ec = EventConfig.fromConfig(config); + return ec.isContractParse(); } public static String getDbDirectoryFromConfig(final Config config) { - return config.hasPath(DB_DIRECTORY_CONFIG_KEY) - ? config.getString(DB_DIRECTORY_CONFIG_KEY) : DEFAULT_DB_DIRECTORY; + return StorageConfig.fromConfig(config).getDb().getDirectory(); } public static String getIndexDirectoryFromConfig(final Config config) { - return config.hasPath(INDEX_DIRECTORY_CONFIG_KEY) - ? config.getString(INDEX_DIRECTORY_CONFIG_KEY) : DEFAULT_INDEX_DIRECTORY; + return StorageConfig.fromConfig(config).getIndex().getDirectory(); } public static String getIndexSwitchFromConfig(final Config config) { - return config.hasPath(INDEX_SWITCH_CONFIG_KEY) - && StringUtils.isNotEmpty(config.getString(INDEX_SWITCH_CONFIG_KEY)) - ? config.getString(INDEX_SWITCH_CONFIG_KEY) : DEFAULT_INDEX_SWITCH; + String val = StorageConfig.fromConfig(config).getIndex().getSwitch(); + return StringUtils.isNotEmpty(val) ? val : DEFAULT_INDEX_SWITCH; } public static String getTransactionHistorySwitchFromConfig(final Config config) { - return config.hasPath(TRANSACTIONHISTORY_SWITCH_CONFIG_KEY) - ? config.getString(TRANSACTIONHISTORY_SWITCH_CONFIG_KEY) - : DEFAULT_TRANSACTIONHISTORY_SWITCH; + return StorageConfig.fromConfig(config).getTransHistory().getSwitch(); } public static int getCheckpointVersionFromConfig(final Config config) { - return config.hasPath(CHECKPOINT_VERSION_KEY) - ? config.getInt(CHECKPOINT_VERSION_KEY) - : DEFAULT_CHECKPOINT_VERSION; + return StorageConfig.fromConfig(config).getCheckpoint().getVersion(); } public static boolean getCheckpointSyncFromConfig(final Config config) { - return config.hasPath(CHECKPOINT_SYNC_KEY) - ? config.getBoolean(CHECKPOINT_SYNC_KEY) - : DEFAULT_CHECKPOINT_SYNC; + return StorageConfig.fromConfig(config).getCheckpoint().isSync(); } public static int getEstimatedTransactionsFromConfig(final Config config) { - if (!config.hasPath(ESTIMATED_TRANSACTIONS_CONFIG_KEY)) { - return DEFAULT_ESTIMATED_TRANSACTIONS; - } - int estimatedTransactions = config.getInt(ESTIMATED_TRANSACTIONS_CONFIG_KEY); + int estimatedTransactions = StorageConfig.fromConfig(config) + .getTxCache().getEstimatedTransactions(); if (estimatedTransactions > 10000) { estimatedTransactions = 10000; } else if (estimatedTransactions < 100) { @@ -250,14 +189,17 @@ public static int getEstimatedTransactionsFromConfig(final Config config) { } public static boolean getTxCacheInitOptimizationFromConfig(final Config config) { - return config.hasPath(TX_CACHE_INIT_OPTIMIZATION) - && config.getBoolean(TX_CACHE_INIT_OPTIMIZATION); + return StorageConfig.fromConfig(config).getTxCache().isInitOptimization(); } - public void setCacheStrategies(Config config) { - if (config.hasPath(CACHE_STRATEGIES)) { - config.getConfig(CACHE_STRATEGIES).resolve().entrySet().forEach(c -> + /** + * Accepts raw storage Config sub-tree because cache.strategies has dynamic keys + * (CacheType enum names) that ConfigBeanFactory cannot bind to fixed bean fields. + */ + public void setCacheStrategies(Config storageSection) { + if (storageSection.hasPath("cache.strategies")) { + storageSection.getConfig("cache.strategies").resolve().entrySet().forEach(c -> this.cacheStrategies.put(CacheType.valueOf(c.getKey()), c.getValue().unwrapped().toString())); } @@ -271,138 +213,111 @@ public Sha256Hash getDbRoot(String dbName, Sha256Hash defaultV) { return this.dbRoots.getOrDefault(dbName, defaultV); } - public void setDbRoots(Config config) { - if (config.hasPath(MERKLE_ROOT)) { - config.getConfig(MERKLE_ROOT).resolve().entrySet().forEach(c -> - this.dbRoots.put(c.getKey(), Sha256Hash.wrap( + /** + * Accepts raw storage Config sub-tree because merkleRoot has dynamic keys + * (database names) that ConfigBeanFactory cannot bind to fixed bean fields. + */ + public void setDbRoots(Config storageSection) { + if (storageSection.hasPath("merkleRoot")) { + storageSection.getConfig("merkleRoot").resolve().entrySet().forEach(c -> + this.dbRoots.put(c.getKey(), Sha256Hash.wrap( ByteString.fromHex(c.getValue().unwrapped().toString())))); } } - private Property createProperty(final ConfigObject conf) { - + /** + * Create Property from StorageConfig.PropertyConfig bean. + */ + private Property createPropertyFromBean(StorageConfig.PropertyConfig pc) { Property property = new Property(); - // Database name must be set - if (!conf.containsKey(NAME_CONFIG_KEY)) { + if (pc.getName().isEmpty()) { throw new IllegalArgumentException("[storage.properties] database name must be set."); } - property.setName(conf.get(NAME_CONFIG_KEY).unwrapped().toString()); - - // Check writable permission of path - if (conf.containsKey(PATH_CONFIG_KEY)) { - String path = conf.get(PATH_CONFIG_KEY).unwrapped().toString(); + property.setName(pc.getName()); + if (!pc.getPath().isEmpty()) { + String path = pc.getPath(); File file = new File(path); if (!file.exists() && !file.mkdirs()) { throw new IllegalArgumentException( String.format("[storage.properties] can not create storage path: %s", path)); } - if (!file.canWrite()) { throw new IllegalArgumentException( String.format("[storage.properties] permission denied to write to: %s ", path)); } - property.setPath(path); } - // Check, get and set fields of Options Options dbOptions = newDefaultDbOptions(property.getName()); - - setIfNeeded(conf, dbOptions); - + applyPropertyOptions(pc, dbOptions); property.setDbOptions(dbOptions); return property; } + /** + * Apply LevelDB options from PropertyConfig bean values. + */ + private static void applyPropertyOptions(StorageConfig.PropertyConfig pc, Options dbOptions) { + dbOptions.createIfMissing(pc.isCreateIfMissing()); + dbOptions.paranoidChecks(pc.isParanoidChecks()); + dbOptions.verifyChecksums(pc.isVerifyChecksums()); + dbOptions.compressionType( + CompressionType.getCompressionTypeByPersistentId(pc.getCompressionType())); + dbOptions.blockSize(pc.getBlockSize()); + dbOptions.writeBufferSize(pc.getWriteBufferSize()); + dbOptions.cacheSize(pc.getCacheSize()); + dbOptions.maxOpenFiles(pc.getMaxOpenFiles()); + } + + // Keep old createProperty and setIfNeeded for setDefaultDbOptions which still + // uses ConfigObject for dynamic default/defaultM/defaultL overrides private static void setIfNeeded(ConfigObject conf, Options dbOptions) { - if (conf.containsKey(CREATE_IF_MISSING_CONFIG_KEY)) { + if (conf.containsKey("createIfMissing")) { dbOptions.createIfMissing( - Boolean.parseBoolean( - conf.get(CREATE_IF_MISSING_CONFIG_KEY).unwrapped().toString() - ) - ); + Boolean.parseBoolean(conf.get("createIfMissing").unwrapped().toString())); } - - if (conf.containsKey(PARANOID_CHECKS_CONFIG_KEY)) { + if (conf.containsKey("paranoidChecks")) { dbOptions.paranoidChecks( - Boolean.parseBoolean( - conf.get(PARANOID_CHECKS_CONFIG_KEY).unwrapped().toString() - ) - ); + Boolean.parseBoolean(conf.get("paranoidChecks").unwrapped().toString())); } - - if (conf.containsKey(VERITY_CHECK_SUMS_CONFIG_KEY)) { + if (conf.containsKey("verifyChecksums")) { dbOptions.verifyChecksums( - Boolean.parseBoolean( - conf.get(VERITY_CHECK_SUMS_CONFIG_KEY).unwrapped().toString() - ) - ); + Boolean.parseBoolean(conf.get("verifyChecksums").unwrapped().toString())); } - - if (conf.containsKey(COMPRESSION_TYPE_CONFIG_KEY)) { - String param = conf.get(COMPRESSION_TYPE_CONFIG_KEY).unwrapped().toString(); - try { - dbOptions.compressionType( - CompressionType.getCompressionTypeByPersistentId(Integer.parseInt(param))); - } catch (NumberFormatException e) { - throwIllegalArgumentException(COMPRESSION_TYPE_CONFIG_KEY, Integer.class, param); - } + if (conf.containsKey("compressionType")) { + dbOptions.compressionType(CompressionType.getCompressionTypeByPersistentId( + Integer.parseInt(conf.get("compressionType").unwrapped().toString()))); } - - if (conf.containsKey(BLOCK_SIZE_CONFIG_KEY)) { - String param = conf.get(BLOCK_SIZE_CONFIG_KEY).unwrapped().toString(); - try { - dbOptions.blockSize(Integer.parseInt(param)); - } catch (NumberFormatException e) { - throwIllegalArgumentException(BLOCK_SIZE_CONFIG_KEY, Integer.class, param); - } + if (conf.containsKey("blockSize")) { + dbOptions.blockSize( + Integer.parseInt(conf.get("blockSize").unwrapped().toString())); } - - if (conf.containsKey(WRITE_BUFFER_SIZE_CONFIG_KEY)) { - String param = conf.get(WRITE_BUFFER_SIZE_CONFIG_KEY).unwrapped().toString(); - try { - dbOptions.writeBufferSize(Integer.parseInt(param)); - } catch (NumberFormatException e) { - throwIllegalArgumentException(WRITE_BUFFER_SIZE_CONFIG_KEY, Integer.class, param); - } + if (conf.containsKey("writeBufferSize")) { + dbOptions.writeBufferSize( + Integer.parseInt(conf.get("writeBufferSize").unwrapped().toString())); } - - if (conf.containsKey(CACHE_SIZE_CONFIG_KEY)) { - String param = conf.get(CACHE_SIZE_CONFIG_KEY).unwrapped().toString(); - try { - dbOptions.cacheSize(Long.parseLong(param)); - } catch (NumberFormatException e) { - throwIllegalArgumentException(CACHE_SIZE_CONFIG_KEY, Long.class, param); - } + if (conf.containsKey("cacheSize")) { + dbOptions.cacheSize( + Long.parseLong(conf.get("cacheSize").unwrapped().toString())); } - - if (conf.containsKey(MAX_OPEN_FILES_CONFIG_KEY)) { - String param = conf.get(MAX_OPEN_FILES_CONFIG_KEY).unwrapped().toString(); - try { - dbOptions.maxOpenFiles(Integer.parseInt(param)); - } catch (NumberFormatException e) { - throwIllegalArgumentException(MAX_OPEN_FILES_CONFIG_KEY, Integer.class, param); - } + if (conf.containsKey("maxOpenFiles")) { + dbOptions.maxOpenFiles( + Integer.parseInt(conf.get("maxOpenFiles").unwrapped().toString())); } } - private static void throwIllegalArgumentException(String param, Class type, String actual) { - throw new IllegalArgumentException( - String.format("[storage.properties] %s must be %s type, actual: %s.", - param, type.getSimpleName(), actual)); - } - /** - * Set propertyMap of Storage object from Config - * - * @param config Config object from "config.conf" file + * Set propertyMap of Storage object from Config via StorageConfig bean. */ - public void setPropertyMapFromConfig(final Config config) { - if (config.hasPath(PROPERTIES_CONFIG_KEY)) { - propertyMap = config.getObjectList(PROPERTIES_CONFIG_KEY).stream() - .map(this::createProperty) + /** + * Set propertyMap from StorageConfig bean list. No Config parameter needed. + */ + public void setPropertyMapFromBean(List props) { + if (props != null && !props.isEmpty()) { + propertyMap = props.stream() + .map(this::createPropertyFromBean) .collect(Collectors.toMap(Property::getName, p -> p)); } } @@ -423,28 +338,27 @@ public void deleteAllStoragePaths() { } } - public void setDefaultDbOptions(final Config config) { + /** + * Accepts raw storage Config sub-tree because default/defaultM/defaultL are + * optional nested objects with dynamic LevelDB Option fields that + * ConfigBeanFactory cannot bind to fixed bean fields. + */ + public void setDefaultDbOptions(final Config storageSection) { this.defaultDbOptions = DbOptionalsUtils.createDefaultDbOptions(); - storage = config.getConfig(PROPERTIES_CONFIG_DB_KEY); + storage = storageSection; } - public Options newDefaultDbOptions(String name ) { - // first fetch origin default - Options options = DbOptionalsUtils.newDefaultDbOptions(name, this.defaultDbOptions); + public Options newDefaultDbOptions(String name) { + Options options = DbOptionalsUtils.newDefaultDbOptions(name, this.defaultDbOptions); - // then fetch from config for default - if (storage.hasPath(PROPERTIES_CONFIG_DEFAULT_KEY)) { - setIfNeeded(storage.getObject(PROPERTIES_CONFIG_DEFAULT_KEY), options); + if (storage.hasPath("default")) { + setIfNeeded(storage.getObject("default"), options); } - - // check if has middle config - if (storage.hasPath(PROPERTIES_CONFIG_DEFAULT_M_KEY) && DbOptionalsUtils.DB_M.contains(name)) { - setIfNeeded(storage.getObject(PROPERTIES_CONFIG_DEFAULT_M_KEY), options); - + if (storage.hasPath("defaultM") && DbOptionalsUtils.DB_M.contains(name)) { + setIfNeeded(storage.getObject("defaultM"), options); } - // check if has large config - if (storage.hasPath(PROPERTIES_CONFIG_DEFAULT_L_KEY) && DbOptionalsUtils.DB_L.contains(name)) { - setIfNeeded(storage.getObject(PROPERTIES_CONFIG_DEFAULT_L_KEY), options); + if (storage.hasPath("defaultL") && DbOptionalsUtils.DB_L.contains(name)) { + setIfNeeded(storage.getObject("defaultL"), options); } return options; 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 new file mode 100644 index 00000000000..cd8774de237 --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/StorageConfig.java @@ -0,0 +1,203 @@ +package org.tron.core.config.args; + +import static org.tron.common.math.Maths.max; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigBeanFactory; +import com.typesafe.config.ConfigFactory; +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * Storage configuration bean. + * Field names match config.conf keys under the "storage" section. + * Covers db, index, properties, dbSettings, backup, checkpoint, txCache, etc. + */ +@Slf4j +@Getter +@Setter +public class StorageConfig { + + private DbConfig db = new DbConfig(); + private IndexConfig index = new IndexConfig(); + private TransHistoryConfig transHistory = new TransHistoryConfig(); + private boolean needToUpdateAsset = true; + private DbSettingsConfig dbSettings = new DbSettingsConfig(); + private BackupConfig backup = new BackupConfig(); + private BalanceConfig balance = new BalanceConfig(); + private CheckpointConfig checkpoint = new CheckpointConfig(); + private SnapshotConfig snapshot = new SnapshotConfig(); + 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; + + // LevelDB per-database option overrides (default, defaultM, defaultL) + // These are not bean-bound because they are optional nested objects with + // dynamic usage. Read manually in fromConfig(). + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private Config rawStorageConfig; + + public Config getRawStorageConfig() { + return rawStorageConfig; + } + + @Getter + @Setter + public static class DbConfig { + private String engine = "LEVELDB"; + private boolean sync = false; + private String directory = "database"; + } + + @Getter + @Setter + public static class IndexConfig { + private String directory = "index"; + // "switch" is a Java keyword, but HOCON key is "index.switch" + // ConfigBeanFactory would look for setSwitch which works fine in Java + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private String switchValue = "on"; + + public String getSwitch() { + return switchValue; + } + + public void setSwitch(String v) { + this.switchValue = v; + } + } + + @Getter + @Setter + public static class TransHistoryConfig { + // "switch" is a Java keyword — same handling as IndexConfig + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + private String switchValue = "on"; + + public String getSwitch() { + return switchValue; + } + + public void setSwitch(String v) { + this.switchValue = v; + } + } + + @Getter + @Setter + public static class DbSettingsConfig { + private int levelNumber = 7; + private int compactThreads = Runtime.getRuntime().availableProcessors(); + private int blocksize = 16; + private long maxBytesForLevelBase = 256; + private double maxBytesForLevelMultiplier = 10; + private int level0FileNumCompactionTrigger = 2; + private long targetFileSizeBase = 64; + private int targetFileSizeMultiplier = 1; + private int maxOpenFiles = 5000; + } + + @Getter + @Setter + public static class BackupConfig { + private boolean enable = false; + private String propPath = "prop.properties"; + private String bak1path = "bak1/database/"; + private String bak2path = "bak2/database/"; + private int frequency = 10000; + } + + @Getter + @Setter + public static class BalanceConfig { + private HistoryConfig history = new HistoryConfig(); + + @Getter + @Setter + public static class HistoryConfig { + private boolean lookup = false; + } + } + + @Getter + @Setter + public static class CheckpointConfig { + private int version = 1; + private boolean sync = true; + } + + @Getter + @Setter + public static class SnapshotConfig { + private int maxFlushCount = 1; + } + + @Getter + @Setter + public static class TxCacheConfig { + private int estimatedTransactions = 1000; + private boolean initOptimization = false; + } + + @Getter + @Setter + public static class PropertyConfig { + private String name = ""; + private String path = ""; + private boolean createIfMissing = true; + private boolean paranoidChecks = true; + private boolean verifyChecksums = true; + private int compressionType = 1; + private int blockSize = 4096; + private int writeBufferSize = 10485760; + private int cacheSize = 10485760; + private int maxOpenFiles = 100; + } + + private static final Config DEFAULTS; + + static { + int cpus = (int) max(Runtime.getRuntime().availableProcessors(), 1, true); + DEFAULTS = ConfigFactory.parseString( + "db { engine = LEVELDB, sync = false, directory = database }\n" + + "index { directory = index, switch = on }\n" + + "transHistory { switch = on }\n" + + "needToUpdateAsset = true\n" + + "dbSettings { levelNumber = 7, compactThreads = " + cpus + + ", blocksize = 16, maxBytesForLevelBase = 256," + + " maxBytesForLevelMultiplier = 10, level0FileNumCompactionTrigger = 2," + + " targetFileSizeBase = 64, targetFileSizeMultiplier = 1, maxOpenFiles = 5000 }\n" + + "backup { enable = false, propPath = \"prop.properties\"," + + " bak1path = \"bak1/database/\", bak2path = \"bak2/database/\"," + + " frequency = 10000 }\n" + + "balance { history { lookup = false } }\n" + + "checkpoint { version = 1, sync = true }\n" + + "snapshot { maxFlushCount = 1 }\n" + + "txCache { estimatedTransactions = 1000, initOptimization = false }\n" + + "properties = []\n" + + "merkleRoot = {}\n" + ); + } + + public static StorageConfig fromConfig(Config config) { + Config section = config.hasPath("storage") + ? config.getConfig("storage").withFallback(DEFAULTS) + : DEFAULTS; + + StorageConfig sc = ConfigBeanFactory.create(section, StorageConfig.class); + // Keep raw config for legacy LevelDB per-database option overrides (default, defaultM, defaultL) + sc.rawStorageConfig = section; + return sc; + } +} 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 new file mode 100644 index 00000000000..a9fa0ec3e6a --- /dev/null +++ b/common/src/main/java/org/tron/core/config/args/VmConfig.java @@ -0,0 +1,80 @@ +package org.tron.core.config.args; + +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; + +/** + * VM configuration bean. Field names match config.conf keys under the "vm" section. + * Bound automatically via ConfigBeanFactory — no manual key constants needed. + */ +@Slf4j +@Getter +@Setter +public class VmConfig { + + private boolean supportConstant = false; + private long maxEnergyLimitForConstant = 100_000_000L; + private int lruCacheSize = 500; + private double minTimeRatio = 0.0; + private double maxTimeRatio = 5.0; + private int longRunningTime = 10; + private boolean estimateEnergy = false; + private int estimateEnergyMaxRetry = 3; + private boolean vmTrace = false; + private boolean saveInternalTx = false; + private boolean saveFeaturedInternalTx = false; + private boolean saveCancelAllUnfreezeV2Details = false; + + private static final Config DEFAULTS = ConfigFactory.parseString( + "supportConstant = false\n" + + "maxEnergyLimitForConstant = 100000000\n" + + "lruCacheSize = 500\n" + + "minTimeRatio = 0.0\n" + + "maxTimeRatio = 5.0\n" + + "longRunningTime = 10\n" + + "estimateEnergy = false\n" + + "estimateEnergyMaxRetry = 3\n" + + "vmTrace = false\n" + + "saveInternalTx = false\n" + + "saveFeaturedInternalTx = false\n" + + "saveCancelAllUnfreezeV2Details = false\n" + ); + + /** + * Create VmConfig from the "vm" section of the application config. + */ + public static VmConfig fromConfig(Config config) { + Config vmSection = config.hasPath("vm") + ? config.getConfig("vm").withFallback(DEFAULTS) + : DEFAULTS; + VmConfig vmConfig = ConfigBeanFactory.create(vmSection, VmConfig.class); + vmConfig.postProcess(); + return vmConfig; + } + + private void postProcess() { + // clamp maxEnergyLimitForConstant + if (maxEnergyLimitForConstant < 3_000_000L) { + maxEnergyLimitForConstant = 3_000_000L; + } + + // clamp estimateEnergyMaxRetry to 0-10 + if (estimateEnergyMaxRetry < 0) { + estimateEnergyMaxRetry = 0; + } + if (estimateEnergyMaxRetry > 10) { + estimateEnergyMaxRetry = 10; + } + + // cross-field dependency warning + if (saveCancelAllUnfreezeV2Details + && (!saveInternalTx || !saveFeaturedInternalTx)) { + logger.warn("Configuring [vm.saveCancelAllUnfreezeV2Details] won't work as " + + "vm.saveInternalTx or vm.saveFeaturedInternalTx is off."); + } + } +} diff --git a/common/src/test/java/org/tron/core/config/args/BlockConfigTest.java b/common/src/test/java/org/tron/core/config/args/BlockConfigTest.java new file mode 100644 index 00000000000..51d71b6ecda --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/BlockConfigTest.java @@ -0,0 +1,54 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; +import org.tron.core.exception.TronError; + +public class BlockConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + BlockConfig bc = BlockConfig.fromConfig(empty); + assertFalse(bc.isNeedSyncCheck()); + assertEquals(21600000L, bc.getMaintenanceTimeInterval()); + assertEquals(1, bc.getCheckFrozenTime()); + } + + @Test + public void testFromConfig() { + Config config = ConfigFactory.parseString( + "block { needSyncCheck = true, maintenanceTimeInterval = 10000," + + " checkFrozenTime = 5, proposalExpireTime = 300000 }"); + BlockConfig bc = BlockConfig.fromConfig(config); + assertEquals(true, bc.isNeedSyncCheck()); + assertEquals(10000L, bc.getMaintenanceTimeInterval()); + assertEquals(5, bc.getCheckFrozenTime()); + assertEquals(300000L, bc.getProposalExpireTime()); + } + + @Test(expected = TronError.class) + public void testProposalExpireTimeTooLow() { + // MIN_PROPOSAL_EXPIRE_TIME = 0, so value must be > 0 + Config config = ConfigFactory.parseString("block { proposalExpireTime = 0 }"); + BlockConfig.fromConfig(config); + } + + @Test(expected = TronError.class) + public void testProposalExpireTimeTooHigh() { + Config config = ConfigFactory.parseString("block { proposalExpireTime = 999999999999 }"); + BlockConfig.fromConfig(config); + } + + @Test(expected = TronError.class) + public void testRejectsCommitteeProposalExpireTime() { + Config config = ConfigFactory.parseString( + "committee { proposalExpireTime = 300000 }\n" + + "block { proposalExpireTime = 300000 }"); + BlockConfig.fromConfig(config); + } +} diff --git a/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java b/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java new file mode 100644 index 00000000000..e13f4b5bc64 --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java @@ -0,0 +1,69 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; + +public class CommitteeConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + CommitteeConfig cc = CommitteeConfig.fromConfig(empty); + assertEquals(0, cc.getAllowCreationOfContracts()); + assertEquals(0, cc.getAllowPBFT()); + assertEquals(20, cc.getPBFTExpireNum()); + assertEquals(0, cc.getUnfreezeDelayDays()); + assertEquals(0, cc.getAllowDynamicEnergy()); + } + + @Test + public void testFromConfig() { + Config config = ConfigFactory.parseString( + "committee { allowCreationOfContracts = 1, allowPBFT = 1, pBFTExpireNum = 30 }"); + CommitteeConfig cc = CommitteeConfig.fromConfig(config); + assertEquals(1, cc.getAllowCreationOfContracts()); + assertEquals(1, cc.getAllowPBFT()); + assertEquals(30, cc.getPBFTExpireNum()); + } + + @Test + public void testUnfreezeDelayDaysClamped() { + Config tooHigh = ConfigFactory.parseString("committee { unfreezeDelayDays = 500 }"); + assertEquals(365, CommitteeConfig.fromConfig(tooHigh).getUnfreezeDelayDays()); + + Config tooLow = ConfigFactory.parseString("committee { unfreezeDelayDays = -10 }"); + assertEquals(0, CommitteeConfig.fromConfig(tooLow).getUnfreezeDelayDays()); + } + + @Test + public void testDynamicEnergyClamped() { + Config config = ConfigFactory.parseString("committee { allowDynamicEnergy = 5 }"); + assertEquals(1, CommitteeConfig.fromConfig(config).getAllowDynamicEnergy()); + } + + @Test + public void testDynamicEnergyThresholdClamped() { + Config config = ConfigFactory.parseString( + "committee { dynamicEnergyThreshold = 999999999999999999 }"); + assertEquals(100_000_000_000_000_000L, + CommitteeConfig.fromConfig(config).getDynamicEnergyThreshold()); + } + + @Test(expected = IllegalArgumentException.class) + public void testAllowOldRewardOptWithoutPrerequisites() { + Config config = ConfigFactory.parseString( + "committee { allowOldRewardOpt = 1 }"); + CommitteeConfig.fromConfig(config); + } + + @Test + public void testAllowOldRewardOptWithPrerequisite() { + Config config = ConfigFactory.parseString( + "committee { allowOldRewardOpt = 1, allowTvmVote = 1 }"); + CommitteeConfig cc = CommitteeConfig.fromConfig(config); + assertEquals(1, cc.getAllowOldRewardOpt()); + } +} 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 new file mode 100644 index 00000000000..f228ac8d479 --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/EventConfigTest.java @@ -0,0 +1,73 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; + +public class EventConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + EventConfig ec = EventConfig.fromConfig(empty); + assertFalse(ec.isEnable()); + assertEquals(0, ec.getVersion()); + assertEquals("", ec.getPath()); + assertTrue(ec.getTopics().isEmpty()); + } + + @Test + public void testNativeQueue() { + Config config = ConfigFactory.parseString( + "event.subscribe { enable = true," + + " native { useNativeQueue = true, bindport = 6666, sendqueuelength = 2000 } }"); + EventConfig ec = EventConfig.fromConfig(config); + assertTrue(ec.isEnable()); + assertTrue(ec.getNativeQueue().isUseNativeQueue()); + assertEquals(6666, ec.getNativeQueue().getBindport()); + assertEquals(2000, ec.getNativeQueue().getSendqueuelength()); + } + + @Test + public void testTopicsWithOptionalFields() { + Config config = ConfigFactory.parseString( + "event.subscribe { enable = true, topics = [" + + "{ triggerName = block, enable = true, topic = block }," + + "{ triggerName = transaction, enable = false, topic = tx," + + " ethCompatible = true, solidified = true, redundancy = true }" + + "] }"); + EventConfig ec = EventConfig.fromConfig(config); + assertEquals(2, ec.getTopics().size()); + + EventConfig.TopicConfig t1 = ec.getTopics().get(0); + assertEquals("block", t1.getTriggerName()); + assertTrue(t1.isEnable()); + assertFalse(t1.isEthCompatible()); // not set, default false + assertFalse(t1.isSolidified()); + assertFalse(t1.isRedundancy()); + + EventConfig.TopicConfig t2 = ec.getTopics().get(1); + assertEquals("transaction", t2.getTriggerName()); + assertTrue(t2.isEthCompatible()); + assertTrue(t2.isSolidified()); + assertTrue(t2.isRedundancy()); + } + + @Test + public void testFilter() { + Config config = ConfigFactory.parseString( + "event.subscribe { enable = true," + + " filter { fromblock = \"100\", toblock = \"200\"," + + " contractAddress = [\"addr1\", \"addr2\"]," + + " contractTopic = [\"topic1\"] } }"); + EventConfig ec = EventConfig.fromConfig(config); + assertEquals("100", ec.getFilter().getFromblock()); + assertEquals("200", ec.getFilter().getToblock()); + assertEquals(2, ec.getFilter().getContractAddress().size()); + assertEquals(1, ec.getFilter().getContractTopic().size()); + } +} 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 new file mode 100644 index 00000000000..b03ef885657 --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/GenesisConfigTest.java @@ -0,0 +1,50 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; + +public class GenesisConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + GenesisConfig gc = GenesisConfig.fromConfig(empty); + assertEquals("", gc.getTimestamp()); + assertEquals("", gc.getParentHash()); + assertTrue(gc.getAssets().isEmpty()); + assertTrue(gc.getWitnesses().isEmpty()); + } + + @Test + public void testWithAssets() { + Config config = ConfigFactory.parseString( + "genesis.block { timestamp = \"12345\", parentHash = \"0x00\"," + + " assets = [{ accountName = Zion, accountType = AssetIssue," + + " address = \"TAddr1\", balance = \"99000\" }]," + + " witnesses = [{ address = \"TWitness1\", url = \"http://test.com\"," + + " voteCount = 100 }] }"); + GenesisConfig gc = GenesisConfig.fromConfig(config); + assertEquals("12345", gc.getTimestamp()); + assertEquals("0x00", gc.getParentHash()); + assertEquals(1, gc.getAssets().size()); + assertEquals("Zion", gc.getAssets().get(0).getAccountName()); + assertEquals("TAddr1", gc.getAssets().get(0).getAddress()); + assertEquals(1, gc.getWitnesses().size()); + assertEquals("TWitness1", gc.getWitnesses().get(0).getAddress()); + assertEquals(100, gc.getWitnesses().get(0).getVoteCount()); + } + + @Test + public void testEmptyLists() { + Config config = ConfigFactory.parseString( + "genesis.block { timestamp = \"0\", parentHash = \"0x00\"," + + " assets = [], witnesses = [] }"); + GenesisConfig gc = GenesisConfig.fromConfig(config); + assertTrue(gc.getAssets().isEmpty()); + assertTrue(gc.getWitnesses().isEmpty()); + } +} diff --git a/common/src/test/java/org/tron/core/config/args/LocalWitnessConfigTest.java b/common/src/test/java/org/tron/core/config/args/LocalWitnessConfigTest.java new file mode 100644 index 00000000000..8042f82592e --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/LocalWitnessConfigTest.java @@ -0,0 +1,40 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; + +public class LocalWitnessConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + LocalWitnessConfig lw = LocalWitnessConfig.fromConfig(empty); + assertTrue(lw.getPrivateKeys().isEmpty()); + assertNull(lw.getAccountAddress()); + assertTrue(lw.getKeystores().isEmpty()); + } + + @Test + public void testWithPrivateKeys() { + Config config = ConfigFactory.parseString( + "localwitness = [\"key1\", \"key2\"]\n" + + "localWitnessAccountAddress = \"TAddr123\""); + LocalWitnessConfig lw = LocalWitnessConfig.fromConfig(config); + assertEquals(2, lw.getPrivateKeys().size()); + assertEquals("key1", lw.getPrivateKeys().get(0)); + assertEquals("TAddr123", lw.getAccountAddress()); + } + + @Test + public void testWithKeystores() { + Config config = ConfigFactory.parseString( + "localwitnesskeystore = [\"/path/to/keystore1\"]"); + LocalWitnessConfig lw = LocalWitnessConfig.fromConfig(config); + assertEquals(1, lw.getKeystores().size()); + } +} diff --git a/common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java b/common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java new file mode 100644 index 00000000000..bb9a5e97f4e --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java @@ -0,0 +1,40 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; + +public class MetricsConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + MetricsConfig mc = MetricsConfig.fromConfig(empty); + assertFalse(mc.isStorageEnable()); + assertFalse(mc.getPrometheus().isEnable()); + assertEquals(9527, mc.getPrometheus().getPort()); + assertEquals(8086, mc.getInfluxdb().getPort()); + } + + @Test + public void testFromConfig() { + Config config = ConfigFactory.parseString( + "node.metrics {" + + " storageEnable = true," + + " prometheus { enable = true, port = 9999 }," + + " influxdb { ip = \"10.0.0.1\", port = 9086, database = mydb," + + " metricsReportInterval = 30 } }"); + MetricsConfig mc = MetricsConfig.fromConfig(config); + assertTrue(mc.isStorageEnable()); + assertTrue(mc.getPrometheus().isEnable()); + assertEquals(9999, mc.getPrometheus().getPort()); + assertEquals("10.0.0.1", mc.getInfluxdb().getIp()); + assertEquals(9086, mc.getInfluxdb().getPort()); + assertEquals("mydb", mc.getInfluxdb().getDatabase()); + assertEquals(30, mc.getInfluxdb().getMetricsReportInterval()); + } +} 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 new file mode 100644 index 00000000000..31e5f499906 --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/MiscConfigTest.java @@ -0,0 +1,46 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; +import org.tron.core.Constant; + +public class MiscConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + MiscConfig mc = MiscConfig.fromConfig(empty); + assertTrue(mc.isNeedToUpdateAsset()); + assertFalse(mc.isHistoryBalanceLookup()); + assertEquals("solid", mc.getTrxReferenceBlock()); + assertEquals(Constant.TRANSACTION_DEFAULT_EXPIRATION_TIME, + mc.getTrxExpirationTimeInMilliseconds()); + assertEquals(Constant.ECKey_ENGINE, mc.getCryptoEngine()); + assertTrue(mc.getSeedNodeIpList().isEmpty()); + assertTrue(mc.getActuatorWhitelist().isEmpty()); + } + + @Test + public void testFromConfig() { + Config config = ConfigFactory.parseString( + "storage { needToUpdateAsset = false," + + " balance { history { lookup = true } } }\n" + + "trx { reference { block = head } }\n" + + "crypto { engine = sm2 }\n" + + "seed.node { ip.list = [\"1.2.3.4:18888\"] }\n" + + "actuator { whitelist = [\"CreateSmartContract\"] }"); + MiscConfig mc = MiscConfig.fromConfig(config); + assertFalse(mc.isNeedToUpdateAsset()); + assertTrue(mc.isHistoryBalanceLookup()); + assertEquals("head", mc.getTrxReferenceBlock()); + assertEquals("sm2", mc.getCryptoEngine()); + assertEquals(1, mc.getSeedNodeIpList().size()); + assertEquals(1, mc.getActuatorWhitelist().size()); + assertTrue(mc.getActuatorWhitelist().contains("CreateSmartContract")); + } +} 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 new file mode 100644 index 00000000000..df63f552c33 --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/NodeConfigTest.java @@ -0,0 +1,91 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; + +public class NodeConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + NodeConfig nc = NodeConfig.fromConfig(empty); + assertEquals(18888, nc.getListenPort()); + assertEquals(2, nc.getConnectionTimeout()); + assertEquals(200, nc.getFetchBlockTimeout()); + assertEquals(30, nc.getMaxConnections()); + assertEquals(8, nc.getMinConnections()); + assertEquals(4, nc.getMaxFastForwardNum()); + assertFalse(nc.isOpenFullTcpDisconnect()); + assertFalse(nc.isDiscoveryEnable()); + assertFalse(nc.isDiscoveryPersist()); + assertEquals(0, nc.getChannelReadTimeout()); + } + + @Test + public void testDotNotationFields() { + Config config = ConfigFactory.parseString( + "node { listen { port = 19999 }, connection { timeout = 5 }," + + " fetchBlock { timeout = 300 }, solidity { threads = 4 } }"); + NodeConfig nc = NodeConfig.fromConfig(config); + assertEquals(19999, nc.getListenPort()); + assertEquals(5, nc.getConnectionTimeout()); + assertEquals(300, nc.getFetchBlockTimeout()); + assertEquals(4, nc.getSolidityThreads()); + } + + @Test + public void testDiscoveryFields() { + Config config = ConfigFactory.parseString( + "node.discovery { enable = true, persist = true }"); + NodeConfig nc = NodeConfig.fromConfig(config); + assertTrue(nc.isDiscoveryEnable()); + assertTrue(nc.isDiscoveryPersist()); + } + + @Test + public void testHttpSubBean() { + Config config = ConfigFactory.parseString( + "node { http { fullNodeEnable = false, fullNodePort = 9090," + + " PBFTEnable = false, PBFTPort = 9092 } }"); + NodeConfig nc = NodeConfig.fromConfig(config); + assertFalse(nc.getHttp().isFullNodeEnable()); + assertEquals(9090, nc.getHttp().getFullNodePort()); + assertFalse(nc.getHttp().isPBFTEnable()); + assertEquals(9092, nc.getHttp().getPBFTPort()); + } + + @Test + public void testRpcSubBean() { + Config config = ConfigFactory.parseString( + "node { rpc { enable = false, port = 60051," + + " PBFTEnable = false, PBFTPort = 60071 } }"); + NodeConfig nc = NodeConfig.fromConfig(config); + assertFalse(nc.getRpc().isEnable()); + assertEquals(60051, nc.getRpc().getPort()); + assertFalse(nc.getRpc().isPBFTEnable()); + assertEquals(60071, nc.getRpc().getPBFTPort()); + } + + @Test + public void testBackupSubBean() { + Config config = ConfigFactory.parseString( + "node { backup { priority = 5, port = 20001, keepAliveInterval = 5000 } }"); + NodeConfig nc = NodeConfig.fromConfig(config); + assertEquals(5, nc.getBackup().getPriority()); + assertEquals(20001, nc.getBackup().getPort()); + assertEquals(5000, nc.getBackup().getKeepAliveInterval()); + } + + @Test + public void testIsOpenFullTcpDisconnect() { + Config config = ConfigFactory.parseString( + "node { isOpenFullTcpDisconnect = true }"); + NodeConfig nc = NodeConfig.fromConfig(config); + assertTrue(nc.isOpenFullTcpDisconnect()); + } +} diff --git a/common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java b/common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java new file mode 100644 index 00000000000..3e71589f3e8 --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java @@ -0,0 +1,46 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; + +public class RateLimiterConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + RateLimiterConfig rl = RateLimiterConfig.fromConfig(empty); + assertEquals(50000, rl.getGlobal().getQps()); + assertEquals(10000, rl.getGlobal().getIp().getQps()); + assertEquals(1000, rl.getGlobal().getApi().getQps()); + assertEquals(3.0, rl.getP2p().getSyncBlockChain(), 0.001); + assertEquals(3.0, rl.getP2p().getFetchInvData(), 0.001); + assertEquals(1.0, rl.getP2p().getDisconnect(), 0.001); + assertTrue(rl.getHttp().isEmpty()); + assertTrue(rl.getRpc().isEmpty()); + } + + @Test + public void testFromConfig() { + Config config = ConfigFactory.parseString( + "rate.limiter {" + + " global { qps = 100, ip { qps = 50 }, api { qps = 10 } }," + + " p2p { syncBlockChain = 5.0, disconnect = 2.0 }," + + " http = [{ component = TestServlet, strategy = QpsRateLimiterAdapter," + + " paramString = \"qps=10\" }]," + + " rpc = [{ component = TestRpc, strategy = GlobalPreemptibleAdapter," + + " paramString = \"permit=1\" }]" + + "}"); + RateLimiterConfig rl = RateLimiterConfig.fromConfig(config); + assertEquals(100, rl.getGlobal().getQps()); + assertEquals(50, rl.getGlobal().getIp().getQps()); + assertEquals(5.0, rl.getP2p().getSyncBlockChain(), 0.001); + assertEquals(1, rl.getHttp().size()); + assertEquals("TestServlet", rl.getHttp().get(0).getComponent()); + assertEquals(1, rl.getRpc().size()); + assertEquals("TestRpc", rl.getRpc().get(0).getComponent()); + } +} diff --git a/common/src/test/java/org/tron/core/config/args/StorageConfigTest.java b/common/src/test/java/org/tron/core/config/args/StorageConfigTest.java new file mode 100644 index 00000000000..8751a93c4be --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/StorageConfigTest.java @@ -0,0 +1,59 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; + +public class StorageConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + StorageConfig sc = StorageConfig.fromConfig(empty); + assertEquals("LEVELDB", sc.getDb().getEngine()); + assertFalse(sc.getDb().isSync()); + assertEquals("database", sc.getDb().getDirectory()); + assertEquals("index", sc.getIndex().getDirectory()); + assertTrue(sc.isNeedToUpdateAsset()); + assertFalse(sc.getBackup().isEnable()); + assertEquals(10000, sc.getBackup().getFrequency()); + assertEquals(7, sc.getDbSettings().getLevelNumber()); + assertEquals(5000, sc.getDbSettings().getMaxOpenFiles()); + } + + @Test + public void testFromConfig() { + Config config = ConfigFactory.parseString( + "storage { db { engine = ROCKSDB, sync = true, directory = mydb }," + + " backup { enable = true, frequency = 5000 }," + + " dbSettings { levelNumber = 5, maxOpenFiles = 3000 } }"); + StorageConfig sc = StorageConfig.fromConfig(config); + assertEquals("ROCKSDB", sc.getDb().getEngine()); + assertTrue(sc.getDb().isSync()); + assertEquals("mydb", sc.getDb().getDirectory()); + assertTrue(sc.getBackup().isEnable()); + assertEquals(5000, sc.getBackup().getFrequency()); + assertEquals(5, sc.getDbSettings().getLevelNumber()); + assertEquals(3000, sc.getDbSettings().getMaxOpenFiles()); + } + + @Test + public void testCheckpointDefaults() { + Config empty = ConfigFactory.empty(); + StorageConfig sc = StorageConfig.fromConfig(empty); + assertEquals(1, sc.getCheckpoint().getVersion()); + assertTrue(sc.getCheckpoint().isSync()); + } + + @Test + public void testBalanceHistoryLookup() { + Config config = ConfigFactory.parseString( + "storage { balance { history { lookup = true } } }"); + StorageConfig sc = StorageConfig.fromConfig(config); + assertTrue(sc.getBalance().getHistory().isLookup()); + } +} diff --git a/common/src/test/java/org/tron/core/config/args/VmConfigTest.java b/common/src/test/java/org/tron/core/config/args/VmConfigTest.java new file mode 100644 index 00000000000..42d39e023cf --- /dev/null +++ b/common/src/test/java/org/tron/core/config/args/VmConfigTest.java @@ -0,0 +1,65 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.junit.Test; + +public class VmConfigTest { + + @Test + public void testDefaults() { + Config empty = ConfigFactory.empty(); + VmConfig vm = VmConfig.fromConfig(empty); + assertFalse(vm.isSupportConstant()); + assertEquals(100_000_000L, vm.getMaxEnergyLimitForConstant()); + assertEquals(500, vm.getLruCacheSize()); + assertEquals(0.0, vm.getMinTimeRatio(), 0.001); + assertEquals(5.0, vm.getMaxTimeRatio(), 0.001); + assertEquals(10, vm.getLongRunningTime()); + assertFalse(vm.isEstimateEnergy()); + assertEquals(3, vm.getEstimateEnergyMaxRetry()); + assertFalse(vm.isVmTrace()); + assertFalse(vm.isSaveInternalTx()); + assertFalse(vm.isSaveFeaturedInternalTx()); + assertFalse(vm.isSaveCancelAllUnfreezeV2Details()); + } + + @Test + public void testFromConfig() { + Config config = ConfigFactory.parseString( + "vm { supportConstant = true, lruCacheSize = 1000, minTimeRatio = 0.5 }"); + VmConfig vm = VmConfig.fromConfig(config); + assertTrue(vm.isSupportConstant()); + assertEquals(1000, vm.getLruCacheSize()); + assertEquals(0.5, vm.getMinTimeRatio(), 0.001); + } + + @Test + public void testMaxEnergyLimitClamped() { + Config config = ConfigFactory.parseString("vm { maxEnergyLimitForConstant = 100 }"); + VmConfig vm = VmConfig.fromConfig(config); + assertEquals(3_000_000L, vm.getMaxEnergyLimitForConstant()); + } + + @Test + public void testEstimateEnergyMaxRetryClamped() { + Config tooHigh = ConfigFactory.parseString("vm { estimateEnergyMaxRetry = 50 }"); + assertEquals(10, VmConfig.fromConfig(tooHigh).getEstimateEnergyMaxRetry()); + + Config tooLow = ConfigFactory.parseString("vm { estimateEnergyMaxRetry = -5 }"); + assertEquals(0, VmConfig.fromConfig(tooLow).getEstimateEnergyMaxRetry()); + } + + @Test + public void testPartialConfig() { + Config config = ConfigFactory.parseString("vm { saveInternalTx = true }"); + VmConfig vm = VmConfig.fromConfig(config); + assertTrue(vm.isSaveInternalTx()); + assertFalse(vm.isSupportConstant()); // default + assertEquals(500, vm.getLruCacheSize()); // default + } +} 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 83d7fd2c63d..66d49467d9b 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 @@ -173,261 +173,466 @@ public static void setParam(final String[] args, final String confFileName) { } /** - * Apply platform-specific constraints after all config sources are resolved. - * ARM64 does not support LevelDB (native JNI library unavailable), - * so db.engine is forced to RocksDB regardless of config or CLI settings. + * Bridge VmConfig bean values to CommonParameter fields. + * Temporary until Phase 2 moves fields into domain config objects. */ - private static void applyPlatformConstraints() { - if (Arch.isArm64() - && !Constant.ROCKSDB.equalsIgnoreCase(PARAMETER.storage.getDbEngine())) { - logger.warn("ARM64 only supports RocksDB, ignoring db.engine='{}'", - PARAMETER.storage.getDbEngine()); - PARAMETER.storage.setDbEngine(Constant.ROCKSDB); - } + private static void applyVmConfig(VmConfig vm) { + PARAMETER.supportConstant = vm.isSupportConstant(); + PARAMETER.maxEnergyLimitForConstant = vm.getMaxEnergyLimitForConstant(); + PARAMETER.lruCacheSize = vm.getLruCacheSize(); + PARAMETER.minTimeRatio = vm.getMinTimeRatio(); + PARAMETER.maxTimeRatio = vm.getMaxTimeRatio(); + PARAMETER.longRunningTime = vm.getLongRunningTime(); + PARAMETER.estimateEnergy = vm.isEstimateEnergy(); + PARAMETER.estimateEnergyMaxRetry = vm.getEstimateEnergyMaxRetry(); + PARAMETER.vmTrace = vm.isVmTrace(); + PARAMETER.saveInternalTx = vm.isSaveInternalTx(); + PARAMETER.saveFeaturedInternalTx = vm.isSaveFeaturedInternalTx(); + PARAMETER.saveCancelAllUnfreezeV2Details = vm.isSaveCancelAllUnfreezeV2Details(); } + // Old applyStorageConfig removed — merged into applyStorageConfig() + /** - * Apply parameters from config file. + * Bridge StorageConfig bean to PARAMETER.storage fields. + * Reads all storage config from one StorageConfig bean instance. + * Config param is still needed for setDefaultDbOptions/setCacheStrategies/setDbRoots + * which use raw Config for dynamic nested objects. */ - public static void applyConfigParams( - final Config config) { - - Wallet.setAddressPreFixByte(ADD_PRE_FIX_BYTE_MAINNET); - Wallet.setAddressPreFixString(Constant.ADD_PRE_FIX_STRING_MAINNET); - - PARAMETER.cryptoEngine = config.hasPath(ConfigKey.CRYPTO_ENGINE) ? config - .getString(ConfigKey.CRYPTO_ENGINE) : Constant.ECKey_ENGINE; - - if (config.hasPath(ConfigKey.VM_SUPPORT_CONSTANT)) { - PARAMETER.supportConstant = config.getBoolean(ConfigKey.VM_SUPPORT_CONSTANT); - } - - if (config.hasPath(ConfigKey.VM_MAX_ENERGY_LIMIT_FOR_CONSTANT)) { - long configLimit = config.getLong(ConfigKey.VM_MAX_ENERGY_LIMIT_FOR_CONSTANT); - PARAMETER.maxEnergyLimitForConstant = max(ENERGY_LIMIT_IN_CONSTANT_TX, configLimit, true); - } - - if (config.hasPath(ConfigKey.VM_LRU_CACHE_SIZE)) { - PARAMETER.lruCacheSize = config.getInt(ConfigKey.VM_LRU_CACHE_SIZE); - } - - if (config.hasPath(ConfigKey.NODE_RPC_ENABLE)) { - PARAMETER.rpcEnable = config.getBoolean(ConfigKey.NODE_RPC_ENABLE); - } - - if (config.hasPath(ConfigKey.NODE_RPC_SOLIDITY_ENABLE)) { - PARAMETER.rpcSolidityEnable = config.getBoolean(ConfigKey.NODE_RPC_SOLIDITY_ENABLE); - } - - if (config.hasPath(ConfigKey.NODE_RPC_PBFT_ENABLE)) { - PARAMETER.rpcPBFTEnable = config.getBoolean(ConfigKey.NODE_RPC_PBFT_ENABLE); - } - - if (config.hasPath(ConfigKey.NODE_HTTP_FULLNODE_ENABLE)) { - PARAMETER.fullNodeHttpEnable = config.getBoolean(ConfigKey.NODE_HTTP_FULLNODE_ENABLE); - } - - if (config.hasPath(ConfigKey.NODE_HTTP_SOLIDITY_ENABLE)) { - PARAMETER.solidityNodeHttpEnable = config.getBoolean(ConfigKey.NODE_HTTP_SOLIDITY_ENABLE); - } - - if (config.hasPath(ConfigKey.NODE_HTTP_PBFT_ENABLE)) { - PARAMETER.pBFTHttpEnable = config.getBoolean(ConfigKey.NODE_HTTP_PBFT_ENABLE); - } - - if (config.hasPath(ConfigKey.NODE_JSONRPC_HTTP_FULLNODE_ENABLE)) { - PARAMETER.jsonRpcHttpFullNodeEnable = - config.getBoolean(ConfigKey.NODE_JSONRPC_HTTP_FULLNODE_ENABLE); - } - - if (config.hasPath(ConfigKey.NODE_JSONRPC_HTTP_SOLIDITY_ENABLE)) { - PARAMETER.jsonRpcHttpSolidityNodeEnable = - config.getBoolean(ConfigKey.NODE_JSONRPC_HTTP_SOLIDITY_ENABLE); - } - - if (config.hasPath(ConfigKey.NODE_JSONRPC_HTTP_PBFT_ENABLE)) { - PARAMETER.jsonRpcHttpPBFTNodeEnable = - config.getBoolean(ConfigKey.NODE_JSONRPC_HTTP_PBFT_ENABLE); - } - - if (config.hasPath(ConfigKey.NODE_JSONRPC_MAX_BLOCK_RANGE)) { - PARAMETER.jsonRpcMaxBlockRange = - config.getInt(ConfigKey.NODE_JSONRPC_MAX_BLOCK_RANGE); - } + private static void applyStorageConfig(StorageConfig sc) { + PARAMETER.storage.setDbEngine(sc.getDb().getEngine()); + PARAMETER.storage.setDbSync(sc.getDb().isSync()); + PARAMETER.storage.setDbDirectory(sc.getDb().getDirectory()); + PARAMETER.storage.setIndexDirectory(sc.getIndex().getDirectory()); + String indexSwitch = sc.getIndex().getSwitch(); + PARAMETER.storage.setIndexSwitch( + org.apache.commons.lang3.StringUtils.isNotEmpty(indexSwitch) ? indexSwitch : "on"); + PARAMETER.storage.setTransactionHistorySwitch(sc.getTransHistory().getSwitch()); + // contractParse is set in applyEventConfig — it belongs to event.subscribe domain + PARAMETER.storage.setCheckpointVersion(sc.getCheckpoint().getVersion()); + PARAMETER.storage.setCheckpointSync(sc.getCheckpoint().isSync()); + + // estimatedTransactions with range clamping [100, 10000] + int estimated = sc.getTxCache().getEstimatedTransactions(); + if (estimated > 10000) { + estimated = 10000; + } else if (estimated < 100) { + estimated = 100; + } + PARAMETER.storage.setEstimatedBlockTransactions(estimated); + PARAMETER.storage.setTxCacheInitOptimization(sc.getTxCache().isInitOptimization()); + + // snapshotMaxFlushCount with range validation + int maxFlush = sc.getSnapshot().getMaxFlushCount(); + if (maxFlush <= 0) { + throw new IllegalArgumentException("MaxFlushCount value can not be negative or zero!"); + } + if (maxFlush > 500) { + throw new IllegalArgumentException("MaxFlushCount value must not exceed 500!"); + } + PARAMETER.storage.setMaxFlushCount(maxFlush); + + // backup + StorageConfig.BackupConfig backup = sc.getBackup(); + PARAMETER.dbBackupConfig = DbBackupConfig.getInstance() + .initArgs(backup.isEnable(), backup.getPropPath(), + backup.getBak1path(), backup.getBak2path(), backup.getFrequency()); - if (config.hasPath(ConfigKey.NODE_JSONRPC_MAX_SUB_TOPICS)) { - PARAMETER.jsonRpcMaxSubTopics = - config.getInt(ConfigKey.NODE_JSONRPC_MAX_SUB_TOPICS); - } + // RocksDB settings + StorageConfig.DbSettingsConfig dbs = sc.getDbSettings(); + PARAMETER.rocksDBCustomSettings = RocksDbSettings + .initCustomSettings(dbs.getLevelNumber(), dbs.getCompactThreads(), + dbs.getBlocksize(), dbs.getMaxBytesForLevelBase(), + dbs.getMaxBytesForLevelMultiplier(), dbs.getLevel0FileNumCompactionTrigger(), + dbs.getTargetFileSizeBase(), dbs.getTargetFileSizeMultiplier(), + dbs.getMaxOpenFiles()); + RocksDbSettings.loggingSettings(); - if (config.hasPath(ConfigKey.NODE_JSONRPC_MAX_BLOCK_FILTER_NUM)) { - PARAMETER.jsonRpcMaxBlockFilterNum = - config.getInt(ConfigKey.NODE_JSONRPC_MAX_BLOCK_FILTER_NUM); - } + // Dynamic nested objects use StorageConfig's raw storage sub-tree + // setDefaultDbOptions must be called before setPropertyMapFromBean because + // createPropertyFromBean calls newDefaultDbOptions which needs defaultDbOptions initialized + PARAMETER.storage.setDefaultDbOptions(sc.getRawStorageConfig()); + PARAMETER.storage.setPropertyMapFromBean(sc.getProperties()); + PARAMETER.storage.setCacheStrategies(sc.getRawStorageConfig()); + PARAMETER.storage.setDbRoots(sc.getRawStorageConfig()); + } - if (config.hasPath(ConfigKey.VM_MIN_TIME_RATIO)) { - PARAMETER.minTimeRatio = config.getDouble(ConfigKey.VM_MIN_TIME_RATIO); - } + /** + * Bridge NodeConfig backup sub-bean to PARAMETER fields. + */ + private static void applyNodeBackupConfig(NodeConfig nc) { + NodeConfig.NodeBackupConfig b = nc.getBackup(); + PARAMETER.backupPriority = b.getPriority(); + PARAMETER.backupPort = b.getPort(); + PARAMETER.keepAliveInterval = b.getKeepAliveInterval(); + PARAMETER.backupMembers = b.getMembers(); + } - if (config.hasPath(ConfigKey.VM_MAX_TIME_RATIO)) { - PARAMETER.maxTimeRatio = config.getDouble(ConfigKey.VM_MAX_TIME_RATIO); + /** + * Bridge GenesisConfig bean to GenesisBlock business object. + * Converts raw address strings via Base58Check decoding. + */ + private static void applyGenesisConfig(GenesisConfig gc, Config config) { + if (gc.getTimestamp().isEmpty() && gc.getAssets().isEmpty()) { + PARAMETER.genesisBlock = GenesisBlock.getDefault(); + return; } - - if (config.hasPath(ConfigKey.VM_LONG_RUNNING_TIME)) { - PARAMETER.longRunningTime = config.getInt(ConfigKey.VM_LONG_RUNNING_TIME); + PARAMETER.genesisBlock = new GenesisBlock(); + PARAMETER.genesisBlock.setTimestamp(gc.getTimestamp()); + PARAMETER.genesisBlock.setParentHash(gc.getParentHash()); + + if (!gc.getAssets().isEmpty()) { + List accounts = new ArrayList<>(); + for (GenesisConfig.AssetConfig ac : gc.getAssets()) { + Account account = new Account(); + account.setAccountName(ac.getAccountName()); + account.setAccountType(ac.getAccountType()); + account.setAddress(Commons.decodeFromBase58Check(ac.getAddress())); + account.setBalance(ac.getBalance()); + accounts.add(account); + } + PARAMETER.genesisBlock.setAssets(accounts); + AccountStore.setAccount(config); + } + { + List witnesses = new ArrayList<>(); + for (GenesisConfig.WitnessConfig wc : gc.getWitnesses()) { + Witness witness = new Witness(); + witness.setAddress(Commons.decodeFromBase58Check(wc.getAddress())); + witness.setUrl(wc.getUrl()); + witness.setVoteCount(wc.getVoteCount()); + witnesses.add(witness); + } + PARAMETER.genesisBlock.setWitnesses(witnesses); } + } - PARAMETER.storage = new Storage(); - - PARAMETER.storage.setDbEngine(Storage.getDbEngineFromConfig(config)); - PARAMETER.storage.setDbSync(Storage.getDbVersionSyncFromConfig(config)); - PARAMETER.storage.setContractParseSwitch(Storage.getContractParseSwitchFromConfig(config)); - PARAMETER.storage.setDbDirectory(Storage.getDbDirectoryFromConfig(config)); - PARAMETER.storage.setIndexDirectory(Storage.getIndexDirectoryFromConfig(config)); - PARAMETER.storage.setIndexSwitch(Storage.getIndexSwitchFromConfig(config)); - PARAMETER.storage.setTransactionHistorySwitch( - Storage.getTransactionHistorySwitchFromConfig(config)); - - PARAMETER.storage - .setCheckpointVersion(Storage.getCheckpointVersionFromConfig(config)); - PARAMETER.storage - .setCheckpointSync(Storage.getCheckpointSyncFromConfig(config)); - - PARAMETER.storage.setEstimatedBlockTransactions( - Storage.getEstimatedTransactionsFromConfig(config)); - PARAMETER.storage.setTxCacheInitOptimization( - Storage.getTxCacheInitOptimizationFromConfig(config)); - PARAMETER.storage.setMaxFlushCount(Storage.getSnapshotMaxFlushCountFromConfig(config)); - - PARAMETER.storage.setDefaultDbOptions(config); - PARAMETER.storage.setPropertyMapFromConfig(config); - PARAMETER.storage.setCacheStrategies(config); - PARAMETER.storage.setDbRoots(config); - + /** + * Bridge MiscConfig bean values to CommonParameter fields. + */ + private static void applyMiscConfig(MiscConfig mc) { + PARAMETER.cryptoEngine = mc.getCryptoEngine(); + PARAMETER.needToUpdateAsset = mc.isNeedToUpdateAsset(); + PARAMETER.historyBalanceLookup = mc.isHistoryBalanceLookup(); + PARAMETER.trxReferenceBlock = mc.getTrxReferenceBlock(); + PARAMETER.trxExpirationTimeInMilliseconds = mc.getTrxExpirationTimeInMilliseconds(); + PARAMETER.blockNumForEnergyLimit = mc.getBlockNumForEnergyLimit(); + PARAMETER.actuatorSet = mc.getActuatorWhitelist(); + + // seed.node — top-level config section, not under "node" + // Config structure is arguably misplaced but preserved for backward compatibility PARAMETER.seedNode = new SeedNode(); PARAMETER.seedNode.setAddressList( - getInetSocketAddress(config, ConfigKey.SEED_NODE_IP_LIST, false)); - - if (config.hasPath(ConfigKey.GENESIS_BLOCK)) { - PARAMETER.genesisBlock = new GenesisBlock(); + mc.getSeedNodeIpList().stream() + .map(s -> org.tron.p2p.utils.NetUtil.parseInetSocketAddress(s)) + .collect(Collectors.toList())); + } - PARAMETER.genesisBlock.setTimestamp(config.getString(ConfigKey.GENESIS_BLOCK_TIMESTAMP)); - PARAMETER.genesisBlock.setParentHash(config.getString(ConfigKey.GENESIS_BLOCK_PARENTHASH)); + /** + * Bridge RateLimiterConfig bean values to CommonParameter fields. + * HTTP/RPC rate limiter lists still use getRateLimiterFromConfig() for + * conversion to RateLimiterInitialization business objects. + */ + private static void applyRateLimiterConfig(RateLimiterConfig rl, Config config) { + PARAMETER.rateLimiterGlobalQps = rl.getGlobal().getQps(); + PARAMETER.rateLimiterGlobalIpQps = rl.getGlobal().getIp().getQps(); + PARAMETER.rateLimiterGlobalApiQps = rl.getGlobal().getApi().getQps(); + PARAMETER.rateLimiterSyncBlockChain = rl.getP2p().getSyncBlockChain(); + PARAMETER.rateLimiterFetchInvData = rl.getP2p().getFetchInvData(); + PARAMETER.rateLimiterDisconnect = rl.getP2p().getDisconnect(); + + // HTTP/RPC rate limiter items: convert bean lists to business objects + RateLimiterInitialization initialization = new RateLimiterInitialization(); + ArrayList httpItems = new ArrayList<>(); + for (RateLimiterConfig.HttpRateLimitItem item : rl.getHttp()) { + httpItems.add(new RateLimiterInitialization.HttpRateLimiterItem( + item.getComponent(), item.getStrategy(), item.getParamString())); + } + initialization.setHttpMap(httpItems); + ArrayList rpcItems = new ArrayList<>(); + for (RateLimiterConfig.RpcRateLimitItem item : rl.getRpc()) { + rpcItems.add(new RateLimiterInitialization.RpcRateLimiterItem( + item.getComponent(), item.getStrategy(), item.getParamString())); + } + initialization.setRpcMap(rpcItems); + PARAMETER.rateLimiterInitialization = initialization; + } - if (config.hasPath(ConfigKey.GENESIS_BLOCK_ASSETS)) { - PARAMETER.genesisBlock.setAssets(getAccountsFromConfig(config)); - AccountStore.setAccount(config); - } - if (config.hasPath(ConfigKey.GENESIS_BLOCK_WITNESSES)) { - PARAMETER.genesisBlock.setWitnesses(getWitnessesFromConfig(config)); + /** + * Bridge EventConfig bean values to CommonParameter fields. + * Converts EventConfig (raw bean) into EventPluginConfig and FilterQuery (business objects). + */ + private static void applyEventConfig(EventConfig ec) { + PARAMETER.eventSubscribe = ec.isEnable(); + // contractParse belongs to event.subscribe but Storage object holds it + PARAMETER.storage.setContractParseSwitch(ec.isContractParse()); + + // Build EventPluginConfig from EventConfig bean + // If event.subscribe was configured, bean will have non-default values + if (ec.isEnable() || ec.getVersion() != 0 || !ec.getTopics().isEmpty() + || StringUtils.isNotEmpty(ec.getPath()) || StringUtils.isNotEmpty(ec.getServer())) { + EventPluginConfig epc = new EventPluginConfig(); + epc.setVersion(ec.getVersion()); + epc.setStartSyncBlockNum(ec.getStartSyncBlockNum()); + + // native queue + EventConfig.NativeConfig nq = ec.getNativeQueue(); + epc.setUseNativeQueue(nq.isUseNativeQueue()); + epc.setBindPort(nq.getBindport()); + epc.setSendQueueLength(nq.getSendqueuelength()); + + if (!nq.isUseNativeQueue()) { + if (StringUtils.isNotEmpty(ec.getPath())) { + epc.setPluginPath(ec.getPath().trim()); + } + if (StringUtils.isNotEmpty(ec.getServer())) { + epc.setServerAddress(ec.getServer().trim()); + } + if (StringUtils.isNotEmpty(ec.getDbconfig())) { + epc.setDbConfig(ec.getDbconfig().trim()); + } } - } else { - PARAMETER.genesisBlock = GenesisBlock.getDefault(); - } - - PARAMETER.needSyncCheck = - config.hasPath(ConfigKey.BLOCK_NEED_SYNC_CHECK) - && config.getBoolean(ConfigKey.BLOCK_NEED_SYNC_CHECK); - - PARAMETER.nodeDiscoveryEnable = - config.hasPath(ConfigKey.NODE_DISCOVERY_ENABLE) - && config.getBoolean(ConfigKey.NODE_DISCOVERY_ENABLE); - PARAMETER.nodeDiscoveryPersist = - config.hasPath(ConfigKey.NODE_DISCOVERY_PERSIST) - && config.getBoolean(ConfigKey.NODE_DISCOVERY_PERSIST); - - PARAMETER.nodeEffectiveCheckEnable = - config.hasPath(ConfigKey.NODE_EFFECTIVE_CHECK_ENABLE) - && config.getBoolean(ConfigKey.NODE_EFFECTIVE_CHECK_ENABLE); - - PARAMETER.nodeConnectionTimeout = - config.hasPath(ConfigKey.NODE_CONNECTION_TIMEOUT) - ? config.getInt(ConfigKey.NODE_CONNECTION_TIMEOUT) * 1000 - : 2000; + // topics + List triggerConfigs = new ArrayList<>(); + for (EventConfig.TopicConfig tc : ec.getTopics()) { + TriggerConfig trig = new TriggerConfig(); + trig.setTriggerName(tc.getTriggerName()); + trig.setEnabled(tc.isEnable()); + trig.setTopic(tc.getTopic()); + trig.setSolidified(tc.isSolidified()); + trig.setEthCompatible(tc.isEthCompatible()); + trig.setRedundancy(tc.isRedundancy()); + triggerConfigs.add(trig); + } + epc.setTriggerConfigList(triggerConfigs); - if (!config.hasPath(ConfigKey.NODE_FETCH_BLOCK_TIMEOUT)) { - PARAMETER.fetchBlockTimeout = 500; - } else if (config.getInt(ConfigKey.NODE_FETCH_BLOCK_TIMEOUT) > 1000) { - PARAMETER.fetchBlockTimeout = 1000; - } else if (config.getInt(ConfigKey.NODE_FETCH_BLOCK_TIMEOUT) < 100) { - PARAMETER.fetchBlockTimeout = 100; - } else { - PARAMETER.fetchBlockTimeout = config.getInt(ConfigKey.NODE_FETCH_BLOCK_TIMEOUT); + PARAMETER.eventPluginConfig = epc; } - PARAMETER.nodeChannelReadTimeout = - config.hasPath(ConfigKey.NODE_CHANNEL_READ_TIMEOUT) - ? config.getInt(ConfigKey.NODE_CHANNEL_READ_TIMEOUT) - : 0; + // Build FilterQuery from EventConfig.FilterConfig bean + EventConfig.FilterConfig fc = ec.getFilter(); + if (StringUtils.isNotEmpty(fc.getFromblock()) || StringUtils.isNotEmpty(fc.getToblock()) + || !fc.getContractAddress().isEmpty()) { + FilterQuery filter = new FilterQuery(); - if (config.hasPath(ConfigKey.NODE_MAX_ACTIVE_NODES)) { - PARAMETER.maxConnections = config.getInt(ConfigKey.NODE_MAX_ACTIVE_NODES); - } else { - PARAMETER.maxConnections = - config.hasPath(ConfigKey.NODE_MAX_CONNECTIONS) - ? config.getInt(ConfigKey.NODE_MAX_CONNECTIONS) : 30; - } + try { + filter.setFromBlock(FilterQuery.parseFromBlockNumber(fc.getFromblock().trim())); + } catch (Exception e) { + logger.error("invalid filter: fromBlockNumber: {}", fc.getFromblock(), e); + PARAMETER.eventFilter = null; + return; + } - if (config.hasPath(ConfigKey.NODE_MAX_ACTIVE_NODES) - && config.hasPath(ConfigKey.NODE_CONNECT_FACTOR)) { - PARAMETER.minConnections = (int) (PARAMETER.maxConnections - * config.getDouble(ConfigKey.NODE_CONNECT_FACTOR)); - } else { - PARAMETER.minConnections = - config.hasPath(ConfigKey.NODE_MIN_CONNECTIONS) - ? config.getInt(ConfigKey.NODE_MIN_CONNECTIONS) : 8; - } + try { + filter.setToBlock(FilterQuery.parseToBlockNumber(fc.getToblock().trim())); + } catch (Exception e) { + logger.error("invalid filter: toBlockNumber: {}", fc.getToblock(), e); + PARAMETER.eventFilter = null; + return; + } - if (config.hasPath(ConfigKey.NODE_MAX_ACTIVE_NODES) - && config.hasPath(ConfigKey.NODE_ACTIVE_CONNECT_FACTOR)) { - PARAMETER.minActiveConnections = (int) (PARAMETER.maxConnections - * config.getDouble(ConfigKey.NODE_ACTIVE_CONNECT_FACTOR)); - } else { - PARAMETER.minActiveConnections = - config.hasPath(ConfigKey.NODE_MIN_ACTIVE_CONNECTIONS) - ? config.getInt(ConfigKey.NODE_MIN_ACTIVE_CONNECTIONS) : 3; - } + filter.setContractAddressList( + fc.getContractAddress().stream() + .filter(StringUtils::isNotEmpty) + .collect(Collectors.toList())); + filter.setContractTopicList( + fc.getContractTopic().stream() + .filter(StringUtils::isNotEmpty) + .collect(Collectors.toList())); - if (config.hasPath(ConfigKey.NODE_MAX_ACTIVE_NODES_WITH_SAME_IP)) { - PARAMETER.maxConnectionsWithSameIp = - config.getInt(ConfigKey.NODE_MAX_ACTIVE_NODES_WITH_SAME_IP); - } else { - PARAMETER.maxConnectionsWithSameIp = - config.hasPath(ConfigKey.NODE_MAX_CONNECTIONS_WITH_SAME_IP) ? config - .getInt(ConfigKey.NODE_MAX_CONNECTIONS_WITH_SAME_IP) : 2; + PARAMETER.eventFilter = filter; } + } - PARAMETER.maxTps = config.hasPath(ConfigKey.NODE_MAX_TPS) - ? config.getInt(ConfigKey.NODE_MAX_TPS) : 1000; - - PARAMETER.minParticipationRate = - config.hasPath(ConfigKey.NODE_MIN_PARTICIPATION_RATE) - ? config.getInt(ConfigKey.NODE_MIN_PARTICIPATION_RATE) - : 0; + /** + * Bridge MetricsConfig bean values to CommonParameter fields. + * Note: node.metricsEnable is handled in applyNodeConfig (it's a node-level field). + */ + private static void applyMetricsConfig(MetricsConfig mc) { + PARAMETER.metricsStorageEnable = mc.isStorageEnable(); + PARAMETER.influxDbIp = mc.getInfluxdb().getIp().isEmpty() + ? Constant.LOCAL_HOST : mc.getInfluxdb().getIp(); + PARAMETER.influxDbPort = mc.getInfluxdb().getPort(); + PARAMETER.influxDbDatabase = mc.getInfluxdb().getDatabase(); + PARAMETER.metricsReportInterval = mc.getInfluxdb().getMetricsReportInterval(); + PARAMETER.metricsPrometheusEnable = mc.getPrometheus().isEnable(); + PARAMETER.metricsPrometheusPort = mc.getPrometheus().getPort(); + } - PARAMETER.p2pConfig = new P2pConfig(); - PARAMETER.nodeListenPort = - config.hasPath(ConfigKey.NODE_LISTEN_PORT) - ? config.getInt(ConfigKey.NODE_LISTEN_PORT) : 0; + /** + * Bridge CommitteeConfig bean values to CommonParameter fields. + */ + private static void applyCommitteeConfig(CommitteeConfig cc) { + PARAMETER.allowCreationOfContracts = cc.getAllowCreationOfContracts(); + PARAMETER.allowMultiSign = (int) cc.getAllowMultiSign(); + PARAMETER.allowAdaptiveEnergy = cc.getAllowAdaptiveEnergy(); + PARAMETER.allowDelegateResource = cc.getAllowDelegateResource(); + PARAMETER.allowSameTokenName = cc.getAllowSameTokenName(); + PARAMETER.allowTvmTransferTrc10 = cc.getAllowTvmTransferTrc10(); + PARAMETER.allowTvmConstantinople = cc.getAllowTvmConstantinople(); + PARAMETER.allowTvmSolidity059 = cc.getAllowTvmSolidity059(); + PARAMETER.forbidTransferToContract = cc.getForbidTransferToContract(); + PARAMETER.allowShieldedTRC20Transaction = cc.getAllowShieldedTRC20Transaction(); + PARAMETER.allowMarketTransaction = cc.getAllowMarketTransaction(); + PARAMETER.allowTransactionFeePool = cc.getAllowTransactionFeePool(); + PARAMETER.allowBlackHoleOptimization = cc.getAllowBlackHoleOptimization(); + PARAMETER.allowNewResourceModel = cc.getAllowNewResourceModel(); + PARAMETER.allowTvmIstanbul = cc.getAllowTvmIstanbul(); + PARAMETER.allowProtoFilterNum = cc.getAllowProtoFilterNum(); + PARAMETER.allowAccountStateRoot = cc.getAllowAccountStateRoot(); + PARAMETER.changedDelegation = cc.getChangedDelegation(); + PARAMETER.allowPBFT = cc.getAllowPBFT(); + PARAMETER.pBFTExpireNum = cc.getPBFTExpireNum(); + PARAMETER.allowTvmFreeze = cc.getAllowTvmFreeze(); + PARAMETER.allowTvmVote = cc.getAllowTvmVote(); + PARAMETER.allowTvmLondon = cc.getAllowTvmLondon(); + PARAMETER.allowTvmCompatibleEvm = cc.getAllowTvmCompatibleEvm(); + PARAMETER.allowHigherLimitForMaxCpuTimeOfOneTx = + cc.getAllowHigherLimitForMaxCpuTimeOfOneTx(); + PARAMETER.allowNewRewardAlgorithm = cc.getAllowNewRewardAlgorithm(); + PARAMETER.allowOptimizedReturnValueOfChainId = + cc.getAllowOptimizedReturnValueOfChainId(); + PARAMETER.allowTvmShangHai = cc.getAllowTvmShangHai(); + PARAMETER.allowOldRewardOpt = cc.getAllowOldRewardOpt(); + PARAMETER.allowEnergyAdjustment = cc.getAllowEnergyAdjustment(); + PARAMETER.allowStrictMath = cc.getAllowStrictMath(); + PARAMETER.consensusLogicOptimization = cc.getConsensusLogicOptimization(); + PARAMETER.allowTvmCancun = cc.getAllowTvmCancun(); + PARAMETER.allowTvmBlob = cc.getAllowTvmBlob(); + PARAMETER.unfreezeDelayDays = cc.getUnfreezeDelayDays(); + // allowReceiptsMerkleRoot not in CommonParameter — skip for now + PARAMETER.allowAccountAssetOptimization = cc.getAllowAccountAssetOptimization(); + PARAMETER.allowAssetOptimization = cc.getAllowAssetOptimization(); + PARAMETER.allowNewReward = cc.getAllowNewReward(); + PARAMETER.memoFee = cc.getMemoFee(); + PARAMETER.allowDelegateOptimization = cc.getAllowDelegateOptimization(); + PARAMETER.allowDynamicEnergy = cc.getAllowDynamicEnergy(); + PARAMETER.dynamicEnergyThreshold = cc.getDynamicEnergyThreshold(); + PARAMETER.dynamicEnergyIncreaseFactor = cc.getDynamicEnergyIncreaseFactor(); + PARAMETER.dynamicEnergyMaxFactor = cc.getDynamicEnergyMaxFactor(); + } - PARAMETER.nodeLanIp = PARAMETER.p2pConfig.getLanIp(); - externalIp(config); + /** + * Bridge BlockConfig bean values to CommonParameter fields. + */ + private static void applyBlockConfig(BlockConfig block) { + PARAMETER.needSyncCheck = block.isNeedSyncCheck(); + PARAMETER.maintenanceTimeInterval = block.getMaintenanceTimeInterval(); + PARAMETER.proposalExpireTime = block.getProposalExpireTime(); + PARAMETER.checkFrozenTime = block.getCheckFrozenTime(); + } - PARAMETER.nodeP2pVersion = - config.hasPath(ConfigKey.NODE_P2P_VERSION) - ? config.getInt(ConfigKey.NODE_P2P_VERSION) : 0; + /** + * Bridge NodeConfig bean values to CommonParameter fields. + * Some fields require post-binding range checks or dynamic defaults (e.g. CPUs/2), + * which are applied here after copying the bean value. + * + * @param nc the NodeConfig bean populated from config.conf "node" section + * node.discovery / node.channel.read.timeout (dot-notation paths + * not part of the NodeConfig bean) + */ + @SuppressWarnings("checkstyle:MethodLength") + private static void applyNodeConfig(NodeConfig nc) { + // ---- RPC sub-bean ---- + NodeConfig.RpcConfig rpc = nc.getRpc(); + PARAMETER.rpcEnable = rpc.isEnable(); + PARAMETER.rpcSolidityEnable = rpc.isSolidityEnable(); + PARAMETER.rpcPBFTEnable = rpc.isPBFTEnable(); + PARAMETER.rpcPort = rpc.getPort(); + PARAMETER.rpcOnSolidityPort = rpc.getSolidityPort(); + PARAMETER.rpcOnPBFTPort = rpc.getPBFTPort(); + PARAMETER.rpcThreadNum = rpc.getThread(); + if (PARAMETER.rpcThreadNum == 16) { + // default from bean — apply dynamic default: (CPUs + 1) / 2 + PARAMETER.rpcThreadNum = (Runtime.getRuntime().availableProcessors() + 1) / 2; + } + PARAMETER.maxConcurrentCallsPerConnection = rpc.getMaxConcurrentCallsPerConnection(); + if (PARAMETER.maxConcurrentCallsPerConnection == 100) { + // bean default 100 — original code used Integer.MAX_VALUE as default + PARAMETER.maxConcurrentCallsPerConnection = Integer.MAX_VALUE; + } + PARAMETER.flowControlWindow = rpc.getFlowControlWindow(); + if (PARAMETER.flowControlWindow == 0) { + PARAMETER.flowControlWindow = NettyServerBuilder.DEFAULT_FLOW_CONTROL_WINDOW; + } + PARAMETER.rpcMaxRstStream = rpc.getMaxRstStream(); + PARAMETER.rpcSecondsPerWindow = rpc.getSecondsPerWindow(); + PARAMETER.maxConnectionIdleInMillis = rpc.getMaxConnectionIdleInMillis(); + PARAMETER.maxConnectionAgeInMillis = rpc.getMaxConnectionAgeInMillis(); + PARAMETER.maxMessageSize = rpc.getMaxMessageSize(); + if (PARAMETER.maxMessageSize == 0) { + PARAMETER.maxMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE; + } + PARAMETER.maxHeaderListSize = rpc.getMaxHeaderListSize(); + if (PARAMETER.maxHeaderListSize == 0) { + PARAMETER.maxHeaderListSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE; + } + PARAMETER.isRpcReflectionServiceEnable = rpc.isReflectionService(); + PARAMETER.minEffectiveConnection = rpc.getMinEffectiveConnection(); + if (PARAMETER.minEffectiveConnection == 0) { + PARAMETER.minEffectiveConnection = 1; + } + PARAMETER.trxCacheEnable = rpc.isTrxCacheEnable(); + + // ---- HTTP sub-bean ---- + NodeConfig.HttpConfig http = nc.getHttp(); + PARAMETER.fullNodeHttpEnable = http.isFullNodeEnable(); + PARAMETER.solidityNodeHttpEnable = http.isSolidityEnable(); + PARAMETER.pBFTHttpEnable = http.isPBFTEnable(); + PARAMETER.fullNodeHttpPort = http.getFullNodePort(); + PARAMETER.solidityHttpPort = http.getSolidityPort(); + PARAMETER.pBFTHttpPort = http.getPBFTPort(); + + // ---- JSON-RPC sub-bean ---- + NodeConfig.JsonRpcConfig jsonrpc = nc.getJsonrpc(); + PARAMETER.jsonRpcHttpFullNodeEnable = jsonrpc.isHttpFullNodeEnable(); + PARAMETER.jsonRpcHttpSolidityNodeEnable = jsonrpc.isHttpSolidityEnable(); + PARAMETER.jsonRpcHttpPBFTNodeEnable = jsonrpc.isHttpPBFTEnable(); + PARAMETER.jsonRpcHttpFullNodePort = jsonrpc.getHttpFullNodePort(); + PARAMETER.jsonRpcHttpSolidityPort = jsonrpc.getHttpSolidityPort(); + PARAMETER.jsonRpcHttpPBFTPort = jsonrpc.getHttpPBFTPort(); + PARAMETER.jsonRpcMaxBlockRange = jsonrpc.getMaxBlockRange(); + PARAMETER.jsonRpcMaxSubTopics = jsonrpc.getMaxSubTopics(); + PARAMETER.jsonRpcMaxBlockFilterNum = jsonrpc.getMaxBlockFilterNum(); + + // ---- P2P sub-bean ---- + PARAMETER.nodeP2pVersion = nc.getP2p().getVersion(); + + // ---- DNS sub-bean (tree URLs only — publish config uses complex validation) ---- + PARAMETER.dnsTreeUrls = nc.getDns().getTreeUrls().isEmpty() + ? new ArrayList<>() : new ArrayList<>(nc.getDns().getTreeUrls()); + + // ---- Dynamic config sub-bean ---- + PARAMETER.dynamicConfigEnable = nc.getDynamicConfig().isEnable(); + PARAMETER.dynamicConfigCheckInterval = nc.getDynamicConfig().getCheckInterval(); + if (PARAMETER.dynamicConfigCheckInterval <= 0) { + PARAMETER.dynamicConfigCheckInterval = 600; + } - PARAMETER.nodeEnableIpv6 = - config.hasPath(ConfigKey.NODE_ENABLE_IPV6) && config.getBoolean(ConfigKey.NODE_ENABLE_IPV6); + // ---- Flat scalar fields ---- + PARAMETER.nodeEffectiveCheckEnable = nc.isEffectiveCheckEnable(); + PARAMETER.nodeConnectionTimeout = nc.getConnectionTimeout() * 1000; - PARAMETER.dnsTreeUrls = config.hasPath(ConfigKey.NODE_DNS_TREE_URLS) ? config.getStringList( - ConfigKey.NODE_DNS_TREE_URLS) : new ArrayList<>(); + // fetchBlock.timeout — range check [100, 1000], default 500 + int fetchTimeout = nc.getFetchBlockTimeout(); + if (fetchTimeout > 1000) { + fetchTimeout = 1000; + } else if (fetchTimeout < 100) { + fetchTimeout = 100; + } + PARAMETER.fetchBlockTimeout = fetchTimeout; - PARAMETER.dnsPublishConfig = loadDnsPublishConfig(config); + PARAMETER.maxConnections = nc.getMaxConnections(); + PARAMETER.minConnections = nc.getMinConnections(); + PARAMETER.minActiveConnections = nc.getMinActiveConnections(); + PARAMETER.maxConnectionsWithSameIp = nc.getMaxConnectionsWithSameIp(); + PARAMETER.maxTps = nc.getMaxTps(); + PARAMETER.minParticipationRate = nc.getMinParticipationRate(); + PARAMETER.nodeListenPort = nc.getListenPort(); + PARAMETER.nodeEnableIpv6 = nc.isEnableIpv6(); - PARAMETER.syncFetchBatchNum = config.hasPath(ConfigKey.NODE_SYNC_FETCH_BATCH_NUM) ? config - .getInt(ConfigKey.NODE_SYNC_FETCH_BATCH_NUM) : 2000; + PARAMETER.syncFetchBatchNum = nc.getSyncFetchBatchNum(); if (PARAMETER.syncFetchBatchNum > 2000) { PARAMETER.syncFetchBatchNum = 2000; } @@ -435,78 +640,9 @@ public static void applyConfigParams( PARAMETER.syncFetchBatchNum = 100; } - PARAMETER.rpcPort = - config.hasPath(ConfigKey.NODE_RPC_PORT) - ? config.getInt(ConfigKey.NODE_RPC_PORT) : 50051; - - PARAMETER.rpcOnSolidityPort = - config.hasPath(ConfigKey.NODE_RPC_SOLIDITY_PORT) - ? config.getInt(ConfigKey.NODE_RPC_SOLIDITY_PORT) : 50061; - - PARAMETER.rpcOnPBFTPort = - config.hasPath(ConfigKey.NODE_RPC_PBFT_PORT) - ? config.getInt(ConfigKey.NODE_RPC_PBFT_PORT) : 50071; - - PARAMETER.fullNodeHttpPort = - config.hasPath(ConfigKey.NODE_HTTP_FULLNODE_PORT) - ? config.getInt(ConfigKey.NODE_HTTP_FULLNODE_PORT) : 8090; - - PARAMETER.solidityHttpPort = - config.hasPath(ConfigKey.NODE_HTTP_SOLIDITY_PORT) - ? config.getInt(ConfigKey.NODE_HTTP_SOLIDITY_PORT) : 8091; - - PARAMETER.pBFTHttpPort = - config.hasPath(ConfigKey.NODE_HTTP_PBFT_PORT) - ? config.getInt(ConfigKey.NODE_HTTP_PBFT_PORT) : 8092; - - PARAMETER.jsonRpcHttpFullNodePort = - config.hasPath(ConfigKey.NODE_JSONRPC_HTTP_FULLNODE_PORT) - ? config.getInt(ConfigKey.NODE_JSONRPC_HTTP_FULLNODE_PORT) : 8545; - - PARAMETER.jsonRpcHttpSolidityPort = - config.hasPath(ConfigKey.NODE_JSONRPC_HTTP_SOLIDITY_PORT) - ? config.getInt(ConfigKey.NODE_JSONRPC_HTTP_SOLIDITY_PORT) : 8555; - - PARAMETER.jsonRpcHttpPBFTPort = - config.hasPath(ConfigKey.NODE_JSONRPC_HTTP_PBFT_PORT) - ? config.getInt(ConfigKey.NODE_JSONRPC_HTTP_PBFT_PORT) : 8565; - - PARAMETER.rpcThreadNum = - config.hasPath(ConfigKey.NODE_RPC_THREAD) ? config.getInt(ConfigKey.NODE_RPC_THREAD) - : (Runtime.getRuntime().availableProcessors() + 1) / 2; - - PARAMETER.solidityThreads = - config.hasPath(ConfigKey.NODE_SOLIDITY_THREADS) - ? config.getInt(ConfigKey.NODE_SOLIDITY_THREADS) - : Runtime.getRuntime().availableProcessors(); - - PARAMETER.maxConcurrentCallsPerConnection = - config.hasPath(ConfigKey.NODE_RPC_MAX_CONCURRENT_CALLS_PER_CONNECTION) - ? config.getInt(ConfigKey.NODE_RPC_MAX_CONCURRENT_CALLS_PER_CONNECTION) - : Integer.MAX_VALUE; - - PARAMETER.flowControlWindow = config.hasPath(ConfigKey.NODE_RPC_FLOW_CONTROL_WINDOW) - ? config.getInt(ConfigKey.NODE_RPC_FLOW_CONTROL_WINDOW) - : NettyServerBuilder.DEFAULT_FLOW_CONTROL_WINDOW; - if (config.hasPath(ConfigKey.NODE_RPC_MAX_RST_STREAM)) { - PARAMETER.rpcMaxRstStream = config.getInt(ConfigKey.NODE_RPC_MAX_RST_STREAM); - } - if (config.hasPath(ConfigKey.NODE_RPC_SECONDS_PER_WINDOW)) { - PARAMETER.rpcSecondsPerWindow = config.getInt(ConfigKey.NODE_RPC_SECONDS_PER_WINDOW); - } - - PARAMETER.maxConnectionIdleInMillis = - config.hasPath(ConfigKey.NODE_RPC_MAX_CONNECTION_IDLE_IN_MILLIS) - ? config.getLong(ConfigKey.NODE_RPC_MAX_CONNECTION_IDLE_IN_MILLIS) - : Long.MAX_VALUE; - - PARAMETER.blockProducedTimeOut = config.hasPath(ConfigKey.NODE_PRODUCED_TIMEOUT) - ? config.getInt(ConfigKey.NODE_PRODUCED_TIMEOUT) : BLOCK_PRODUCE_TIMEOUT_PERCENT; - - PARAMETER.maxHttpConnectNumber = config.hasPath(ConfigKey.NODE_MAX_HTTP_CONNECT_NUMBER) - ? config.getInt(ConfigKey.NODE_MAX_HTTP_CONNECT_NUMBER) - : NodeConstant.MAX_HTTP_CONNECT_NUMBER; + PARAMETER.solidityThreads = nc.getSolidityThreads(); + PARAMETER.blockProducedTimeOut = nc.getBlockProducedTimeOut(); if (PARAMETER.blockProducedTimeOut < 30) { PARAMETER.blockProducedTimeOut = 30; } @@ -514,245 +650,32 @@ public static void applyConfigParams( PARAMETER.blockProducedTimeOut = 100; } - PARAMETER.netMaxTrxPerSecond = config.hasPath(ConfigKey.NODE_NET_MAX_TRX_PER_SECOND) - ? config.getInt(ConfigKey.NODE_NET_MAX_TRX_PER_SECOND) - : NetConstants.NET_MAX_TRX_PER_SECOND; - - PARAMETER.maxConnectionAgeInMillis = - config.hasPath(ConfigKey.NODE_RPC_MAX_CONNECTION_AGE_IN_MILLIS) - ? config.getLong(ConfigKey.NODE_RPC_MAX_CONNECTION_AGE_IN_MILLIS) - : Long.MAX_VALUE; - - PARAMETER.maxMessageSize = config.hasPath(ConfigKey.NODE_RPC_MAX_MESSAGE_SIZE) - ? config.getInt(ConfigKey.NODE_RPC_MAX_MESSAGE_SIZE) : GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE; - - PARAMETER.maxHeaderListSize = config.hasPath(ConfigKey.NODE_RPC_MAX_HEADER_LIST_SIZE) - ? config.getInt(ConfigKey.NODE_RPC_MAX_HEADER_LIST_SIZE) - : GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE; - - PARAMETER.isRpcReflectionServiceEnable = - config.hasPath(ConfigKey.NODE_RPC_REFLECTION_SERVICE) - && config.getBoolean(ConfigKey.NODE_RPC_REFLECTION_SERVICE); - - PARAMETER.maintenanceTimeInterval = - config.hasPath(ConfigKey.BLOCK_MAINTENANCE_TIME_INTERVAL) ? config - .getInt(ConfigKey.BLOCK_MAINTENANCE_TIME_INTERVAL) : 21600000L; - - PARAMETER.proposalExpireTime = getProposalExpirationTime(config); - - PARAMETER.checkFrozenTime = - config.hasPath(ConfigKey.BLOCK_CHECK_FROZEN_TIME) ? config - .getInt(ConfigKey.BLOCK_CHECK_FROZEN_TIME) : 1; - - PARAMETER.allowCreationOfContracts = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_CREATION_OF_CONTRACTS) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_CREATION_OF_CONTRACTS) : 0; - - PARAMETER.allowMultiSign = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_MULTI_SIGN) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_MULTI_SIGN) : 0; - - PARAMETER.allowAdaptiveEnergy = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_ADAPTIVE_ENERGY) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_ADAPTIVE_ENERGY) : 0; - - PARAMETER.allowDelegateResource = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_DELEGATE_RESOURCE) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_DELEGATE_RESOURCE) : 0; - - PARAMETER.allowSameTokenName = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_SAME_TOKEN_NAME) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_SAME_TOKEN_NAME) : 0; - - PARAMETER.allowTvmTransferTrc10 = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_TRANSFER_TRC10) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_TRANSFER_TRC10) : 0; - - PARAMETER.allowTvmConstantinople = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_CONSTANTINOPLE) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_CONSTANTINOPLE) : 0; - - PARAMETER.allowTvmSolidity059 = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_SOLIDITY059) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_SOLIDITY059) : 0; - - PARAMETER.forbidTransferToContract = - config.hasPath(ConfigKey.COMMITTEE_FORBID_TRANSFER_TO_CONTRACT) ? config - .getInt(ConfigKey.COMMITTEE_FORBID_TRANSFER_TO_CONTRACT) : 0; - - PARAMETER.tcpNettyWorkThreadNum = config.hasPath(ConfigKey.NODE_TCP_NETTY_WORK_THREAD_NUM) - ? config.getInt(ConfigKey.NODE_TCP_NETTY_WORK_THREAD_NUM) : 0; - - PARAMETER.udpNettyWorkThreadNum = config.hasPath(ConfigKey.NODE_UDP_NETTY_WORK_THREAD_NUM) - ? config.getInt(ConfigKey.NODE_UDP_NETTY_WORK_THREAD_NUM) : 1; + PARAMETER.maxHttpConnectNumber = nc.getMaxHttpConnectNumber(); + PARAMETER.netMaxTrxPerSecond = nc.getNetMaxTrxPerSecond(); + PARAMETER.tcpNettyWorkThreadNum = nc.getTcpNettyWorkThreadNum(); + PARAMETER.udpNettyWorkThreadNum = nc.getUdpNettyWorkThreadNum(); if (StringUtils.isEmpty(PARAMETER.trustNodeAddr)) { - PARAMETER.trustNodeAddr = - config.hasPath(ConfigKey.NODE_TRUST_NODE) - ? config.getString(ConfigKey.NODE_TRUST_NODE) : null; - } - - PARAMETER.validateSignThreadNum = - config.hasPath(ConfigKey.NODE_VALIDATE_SIGN_THREAD_NUM) ? config - .getInt(ConfigKey.NODE_VALIDATE_SIGN_THREAD_NUM) - : Runtime.getRuntime().availableProcessors(); - - PARAMETER.walletExtensionApi = - config.hasPath(ConfigKey.NODE_WALLET_EXTENSION_API) - && config.getBoolean(ConfigKey.NODE_WALLET_EXTENSION_API); - PARAMETER.estimateEnergy = - config.hasPath(ConfigKey.VM_ESTIMATE_ENERGY) - && config.getBoolean(ConfigKey.VM_ESTIMATE_ENERGY); - PARAMETER.estimateEnergyMaxRetry = config.hasPath(ConfigKey.VM_ESTIMATE_ENERGY_MAX_RETRY) - ? config.getInt(ConfigKey.VM_ESTIMATE_ENERGY_MAX_RETRY) : 3; - if (PARAMETER.estimateEnergyMaxRetry < 0) { - PARAMETER.estimateEnergyMaxRetry = 0; - } - if (PARAMETER.estimateEnergyMaxRetry > 10) { - PARAMETER.estimateEnergyMaxRetry = 10; + PARAMETER.trustNodeAddr = nc.getTrustNode(); } - PARAMETER.receiveTcpMinDataLength = config.hasPath(ConfigKey.NODE_RECEIVE_TCP_MIN_DATA_LENGTH) - ? config.getLong(ConfigKey.NODE_RECEIVE_TCP_MIN_DATA_LENGTH) : 2048; - - PARAMETER.isOpenFullTcpDisconnect = config.hasPath(ConfigKey.NODE_IS_OPEN_FULL_TCP_DISCONNECT) - && config.getBoolean(ConfigKey.NODE_IS_OPEN_FULL_TCP_DISCONNECT); + PARAMETER.validateSignThreadNum = nc.getValidateSignThreadNum(); + PARAMETER.walletExtensionApi = nc.isWalletExtensionApi(); + PARAMETER.receiveTcpMinDataLength = nc.getReceiveTcpMinDataLength(); + PARAMETER.isOpenFullTcpDisconnect = nc.isOpenFullTcpDisconnect(); + PARAMETER.nodeDetectEnable = nc.isNodeDetectEnable(); - PARAMETER.nodeDetectEnable = config.hasPath(ConfigKey.NODE_DETECT_ENABLE) - && config.getBoolean(ConfigKey.NODE_DETECT_ENABLE); - - PARAMETER.inactiveThreshold = config.hasPath(ConfigKey.NODE_INACTIVE_THRESHOLD) - ? config.getInt(ConfigKey.NODE_INACTIVE_THRESHOLD) : 600; + PARAMETER.inactiveThreshold = nc.getInactiveThreshold(); if (PARAMETER.inactiveThreshold < 1) { PARAMETER.inactiveThreshold = 1; } - PARAMETER.maxTransactionPendingSize = - config.hasPath(ConfigKey.NODE_MAX_TRANSACTION_PENDING_SIZE) - ? config.getInt(ConfigKey.NODE_MAX_TRANSACTION_PENDING_SIZE) : 2000; - - PARAMETER.pendingTransactionTimeout = config.hasPath(ConfigKey.NODE_PENDING_TRANSACTION_TIMEOUT) - ? config.getLong(ConfigKey.NODE_PENDING_TRANSACTION_TIMEOUT) : 60_000; - - PARAMETER.needToUpdateAsset = - !config.hasPath(ConfigKey.STORAGE_NEEDTO_UPDATE_ASSET) || config - .getBoolean(ConfigKey.STORAGE_NEEDTO_UPDATE_ASSET); - PARAMETER.trxReferenceBlock = config.hasPath(ConfigKey.TRX_REFERENCE_BLOCK) - ? config.getString(ConfigKey.TRX_REFERENCE_BLOCK) : "solid"; - - PARAMETER.trxExpirationTimeInMilliseconds = - config.hasPath(ConfigKey.TRX_EXPIRATION_TIME_IN_MILLIS_SECONDS) - && config.getLong(ConfigKey.TRX_EXPIRATION_TIME_IN_MILLIS_SECONDS) > 0 - ? config.getLong(ConfigKey.TRX_EXPIRATION_TIME_IN_MILLIS_SECONDS) - : Constant.TRANSACTION_DEFAULT_EXPIRATION_TIME; - - PARAMETER.minEffectiveConnection = config.hasPath(ConfigKey.NODE_RPC_MIN_EFFECTIVE_CONNECTION) - ? config.getInt(ConfigKey.NODE_RPC_MIN_EFFECTIVE_CONNECTION) : 1; - - PARAMETER.trxCacheEnable = config.hasPath(ConfigKey.NODE_RPC_TRX_CACHE_ENABLE) - && config.getBoolean(ConfigKey.NODE_RPC_TRX_CACHE_ENABLE); - - PARAMETER.blockNumForEnergyLimit = config.hasPath(ConfigKey.ENERGY_LIMIT_BLOCK_NUM) - ? config.getInt(ConfigKey.ENERGY_LIMIT_BLOCK_NUM) : 4727890L; - - PARAMETER.vmTrace = - config.hasPath(ConfigKey.VM_TRACE) && config.getBoolean(ConfigKey.VM_TRACE); - - PARAMETER.saveInternalTx = - config.hasPath(ConfigKey.VM_SAVE_INTERNAL_TX) - && config.getBoolean(ConfigKey.VM_SAVE_INTERNAL_TX); - - PARAMETER.saveFeaturedInternalTx = - config.hasPath(ConfigKey.VM_SAVE_FEATURED_INTERNAL_TX) - && config.getBoolean(ConfigKey.VM_SAVE_FEATURED_INTERNAL_TX); - - if (!PARAMETER.saveCancelAllUnfreezeV2Details - && config.hasPath(ConfigKey.VM_SAVE_CANCEL_ALL_UNFREEZE_V2_DETAILS)) { - PARAMETER.saveCancelAllUnfreezeV2Details = - config.getBoolean(ConfigKey.VM_SAVE_CANCEL_ALL_UNFREEZE_V2_DETAILS); - } - - if (PARAMETER.saveCancelAllUnfreezeV2Details - && (!PARAMETER.saveInternalTx || !PARAMETER.saveFeaturedInternalTx)) { - logger.warn("Configuring [vm.saveCancelAllUnfreezeV2Details] won't work as " - + "vm.saveInternalTx or vm.saveFeaturedInternalTx is off."); - } - - // PARAMETER.allowShieldedTransaction = - // config.hasPath(Constant.COMMITTEE_ALLOW_SHIELDED_TRANSACTION) ? config - // .getInt(Constant.COMMITTEE_ALLOW_SHIELDED_TRANSACTION) : 0; - - PARAMETER.allowShieldedTRC20Transaction = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_SHIELDED_TRC20_TRANSACTION) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_SHIELDED_TRC20_TRANSACTION) : 0; - - PARAMETER.allowMarketTransaction = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_MARKET_TRANSACTION) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_MARKET_TRANSACTION) : 0; - - PARAMETER.allowTransactionFeePool = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TRANSACTION_FEE_POOL) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TRANSACTION_FEE_POOL) : 0; - - PARAMETER.allowBlackHoleOptimization = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_BLACK_HOLE_OPTIMIZATION) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_BLACK_HOLE_OPTIMIZATION) : 0; - - PARAMETER.allowNewResourceModel = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_NEW_RESOURCE_MODEL) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_NEW_RESOURCE_MODEL) : 0; - - PARAMETER.allowTvmIstanbul = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_ISTANBUL) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_ISTANBUL) : 0; - - PARAMETER.eventPluginConfig = - config.hasPath(ConfigKey.EVENT_SUBSCRIBE) - ? getEventPluginConfig(config) : null; - - PARAMETER.eventFilter = - config.hasPath(ConfigKey.EVENT_SUBSCRIBE_FILTER) ? getEventFilter(config) : null; - - PARAMETER.eventSubscribe = config.hasPath(ConfigKey.EVENT_SUBSCRIBE_ENABLE) - && config.getBoolean(ConfigKey.EVENT_SUBSCRIBE_ENABLE); - - if (config.hasPath(ConfigKey.ALLOW_SHIELDED_TRANSACTION_API)) { - PARAMETER.allowShieldedTransactionApi = - config.getBoolean(ConfigKey.ALLOW_SHIELDED_TRANSACTION_API); - } else if (config.hasPath(ConfigKey.NODE_FULLNODE_ALLOW_SHIELDED_TRANSACTION)) { - // for compatibility with previous configuration - PARAMETER.allowShieldedTransactionApi = - config.getBoolean(ConfigKey.NODE_FULLNODE_ALLOW_SHIELDED_TRANSACTION); - logger.warn("Configuring [node.fullNodeAllowShieldedTransaction] will be deprecated. " - + "Please use [node.allowShieldedTransactionApi] instead."); - } else { - PARAMETER.allowShieldedTransactionApi = true; - } - - PARAMETER.zenTokenId = config.hasPath(ConfigKey.NODE_ZEN_TOKENID) - ? config.getString(ConfigKey.NODE_ZEN_TOKENID) : "000000"; + PARAMETER.maxTransactionPendingSize = nc.getMaxTransactionPendingSize(); + PARAMETER.pendingTransactionTimeout = nc.getPendingTransactionTimeout(); - PARAMETER.allowProtoFilterNum = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_PROTO_FILTER_NUM) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_PROTO_FILTER_NUM) : 0; + PARAMETER.validContractProtoThreadNum = nc.getValidContractProtoThreads(); - PARAMETER.allowAccountStateRoot = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_ACCOUNT_STATE_ROOT) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_ACCOUNT_STATE_ROOT) : 0; - - PARAMETER.validContractProtoThreadNum = - config.hasPath(ConfigKey.NODE_VALID_CONTRACT_PROTO_THREADS) ? config - .getInt(ConfigKey.NODE_VALID_CONTRACT_PROTO_THREADS) - : Runtime.getRuntime().availableProcessors(); - - PARAMETER.activeNodes = getInetSocketAddress(config, ConfigKey.NODE_ACTIVE, true); - - PARAMETER.passiveNodes = getInetAddress(config, ConfigKey.NODE_PASSIVE); - - PARAMETER.fastForwardNodes = getInetSocketAddress(config, ConfigKey.NODE_FAST_FORWARD, true); - - PARAMETER.maxFastForwardNum = config.hasPath(ConfigKey.NODE_MAX_FAST_FORWARD_NUM) ? config - .getInt(ConfigKey.NODE_MAX_FAST_FORWARD_NUM) : 4; + PARAMETER.maxFastForwardNum = nc.getMaxFastForwardNum(); if (PARAMETER.maxFastForwardNum > MAX_ACTIVE_WITNESS_NUM) { PARAMETER.maxFastForwardNum = MAX_ACTIVE_WITNESS_NUM; } @@ -760,282 +683,162 @@ public static void applyConfigParams( PARAMETER.maxFastForwardNum = 1; } - PARAMETER.shieldedTransInPendingMaxCounts = - config.hasPath(ConfigKey.NODE_SHIELDED_TRANS_IN_PENDING_MAX_COUNTS) ? config - .getInt(ConfigKey.NODE_SHIELDED_TRANS_IN_PENDING_MAX_COUNTS) : 10; + PARAMETER.shieldedTransInPendingMaxCounts = nc.getShieldedTransInPendingMaxCounts(); - PARAMETER.rateLimiterGlobalQps = - config.hasPath(ConfigKey.RATE_LIMITER_GLOBAL_QPS) ? config - .getInt(ConfigKey.RATE_LIMITER_GLOBAL_QPS) : 50000; + PARAMETER.agreeNodeCount = nc.getAgreeNodeCount(); + if (PARAMETER.agreeNodeCount == 0) { + PARAMETER.agreeNodeCount = MAX_ACTIVE_WITNESS_NUM * 2 / 3 + 1; + } + PARAMETER.agreeNodeCount = PARAMETER.agreeNodeCount > MAX_ACTIVE_WITNESS_NUM + ? MAX_ACTIVE_WITNESS_NUM : PARAMETER.agreeNodeCount; - PARAMETER.rateLimiterGlobalIpQps = - config.hasPath(ConfigKey.RATE_LIMITER_GLOBAL_IP_QPS) ? config - .getInt(ConfigKey.RATE_LIMITER_GLOBAL_IP_QPS) : 10000; + PARAMETER.setOpenHistoryQueryWhenLiteFN(nc.isOpenHistoryQueryWhenLiteFN()); + PARAMETER.nodeMetricsEnable = nc.isMetricsEnable(); + PARAMETER.openPrintLog = nc.isOpenPrintLog(); + PARAMETER.openTransactionSort = nc.isOpenTransactionSort(); + PARAMETER.blockCacheTimeout = nc.getBlockCacheTimeout(); + PARAMETER.zenTokenId = nc.getZenTokenId(); + PARAMETER.allowShieldedTransactionApi = nc.isAllowShieldedTransactionApi(); - PARAMETER.rateLimiterGlobalApiQps = - config.hasPath(ConfigKey.RATE_LIMITER_GLOBAL_API_QPS) ? config - .getInt(ConfigKey.RATE_LIMITER_GLOBAL_API_QPS) : 1000; + PARAMETER.unsolidifiedBlockCheck = nc.isUnsolidifiedBlockCheck(); + PARAMETER.maxUnsolidifiedBlocks = nc.getMaxUnsolidifiedBlocks(); - PARAMETER.rateLimiterInitialization = getRateLimiterFromConfig(config); + // disabledApi list — lowercase normalization + PARAMETER.disabledApiList = nc.getDisabledApi().isEmpty() + ? Collections.emptyList() + : nc.getDisabledApi().stream().map(String::toLowerCase) + .collect(Collectors.toList()); - PARAMETER.rateLimiterSyncBlockChain = - config.hasPath(ConfigKey.RATE_LIMITER_P2P_SYNC_BLOCK_CHAIN) ? config - .getDouble(ConfigKey.RATE_LIMITER_P2P_SYNC_BLOCK_CHAIN) : 3.0; + // ---- Fields previously scattered in applyConfigParams ---- - PARAMETER.rateLimiterFetchInvData = - config.hasPath(ConfigKey.RATE_LIMITER_P2P_FETCH_INV_DATA) ? config - .getDouble(ConfigKey.RATE_LIMITER_P2P_FETCH_INV_DATA) : 3.0; + // discovery (dot-notation, read in NodeConfig.fromConfig) + PARAMETER.nodeDiscoveryEnable = nc.isDiscoveryEnable(); + PARAMETER.nodeDiscoveryPersist = nc.isDiscoveryPersist(); + PARAMETER.nodeChannelReadTimeout = nc.getChannelReadTimeout(); - PARAMETER.rateLimiterDisconnect = - config.hasPath(ConfigKey.RATE_LIMITER_P2P_DISCONNECT) ? config - .getDouble(ConfigKey.RATE_LIMITER_P2P_DISCONNECT) : 1.0; + // Legacy maxActiveNodes fallback handled in NodeConfig.fromConfig() - PARAMETER.changedDelegation = - config.hasPath(ConfigKey.COMMITTEE_CHANGED_DELEGATION) ? config - .getInt(ConfigKey.COMMITTEE_CHANGED_DELEGATION) : 0; + // p2p config and external IP + PARAMETER.p2pConfig = new P2pConfig(); + PARAMETER.nodeLanIp = PARAMETER.p2pConfig.getLanIp(); + externalIp(nc); - PARAMETER.allowPBFT = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_PBFT) ? config - .getLong(ConfigKey.COMMITTEE_ALLOW_PBFT) : 0; + // DNS publish config + PARAMETER.dnsPublishConfig = loadDnsPublishConfig(nc); - PARAMETER.pBFTExpireNum = - config.hasPath(ConfigKey.COMMITTEE_PBFT_EXPIRE_NUM) ? config - .getLong(ConfigKey.COMMITTEE_PBFT_EXPIRE_NUM) : 20; + // Shielded transaction API — legacy fallback handled in NodeConfig.fromConfig() + PARAMETER.allowShieldedTransactionApi = nc.isAllowShieldedTransactionApi(); - PARAMETER.agreeNodeCount = config.hasPath(ConfigKey.NODE_AGREE_NODE_COUNT) ? config - .getInt(ConfigKey.NODE_AGREE_NODE_COUNT) : MAX_ACTIVE_WITNESS_NUM * 2 / 3 + 1; - PARAMETER.agreeNodeCount = PARAMETER.agreeNodeCount > MAX_ACTIVE_WITNESS_NUM - ? MAX_ACTIVE_WITNESS_NUM : PARAMETER.agreeNodeCount; - if (PARAMETER.isWitness()) { - // INSTANCE.agreeNodeCount = MAX_ACTIVE_WITNESS_NUM * 2 / 3 + 1; + // Active/passive/fastForward node lists from bean with filtering + PARAMETER.activeNodes = filterInetSocketAddress(nc.getActive(), true); + PARAMETER.passiveNodes = new ArrayList<>(); + for (InetSocketAddress sa : filterInetSocketAddress(nc.getPassive(), false)) { + PARAMETER.passiveNodes.add(sa.getAddress()); } + PARAMETER.fastForwardNodes = filterInetSocketAddress(nc.getFastForward(), true); - PARAMETER.allowTvmFreeze = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_FREEZE) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_FREEZE) : 0; - - PARAMETER.allowTvmVote = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_VOTE) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_VOTE) : 0; - - PARAMETER.allowTvmLondon = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_LONDON) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_LONDON) : 0; - - PARAMETER.allowTvmCompatibleEvm = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_COMPATIBLE_EVM) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_COMPATIBLE_EVM) : 0; - - PARAMETER.allowHigherLimitForMaxCpuTimeOfOneTx = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX) : 0; - - PARAMETER.allowNewRewardAlgorithm = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_NEW_REWARD_ALGORITHM) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_NEW_REWARD_ALGORITHM) : 0; - - PARAMETER.allowOptimizedReturnValueOfChainId = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_OPTIMIZED_RETURN_VALUE_OF_CHAIN_ID) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_OPTIMIZED_RETURN_VALUE_OF_CHAIN_ID) : 0; - - initBackupProperty(config); - initRocksDbBackupProperty(config); - initRocksDbSettings(config); - - PARAMETER.actuatorSet = - config.hasPath(ConfigKey.ACTUATOR_WHITELIST) - ? new HashSet<>(config.getStringList(ConfigKey.ACTUATOR_WHITELIST)) - : Collections.emptySet(); - - if (config.hasPath(ConfigKey.NODE_METRICS_ENABLE)) { - PARAMETER.nodeMetricsEnable = config.getBoolean(ConfigKey.NODE_METRICS_ENABLE); - } - - PARAMETER.metricsStorageEnable = config.hasPath(ConfigKey.METRICS_STORAGE_ENABLE) && config - .getBoolean(ConfigKey.METRICS_STORAGE_ENABLE); - PARAMETER.influxDbIp = config.hasPath(ConfigKey.METRICS_INFLUXDB_IP) ? config - .getString(ConfigKey.METRICS_INFLUXDB_IP) : Constant.LOCAL_HOST; - PARAMETER.influxDbPort = config.hasPath(ConfigKey.METRICS_INFLUXDB_PORT) ? config - .getInt(ConfigKey.METRICS_INFLUXDB_PORT) : 8086; - PARAMETER.influxDbDatabase = config.hasPath(ConfigKey.METRICS_INFLUXDB_DATABASE) ? config - .getString(ConfigKey.METRICS_INFLUXDB_DATABASE) : "metrics"; - PARAMETER.metricsReportInterval = config.hasPath(ConfigKey.METRICS_REPORT_INTERVAL) ? config - .getInt(ConfigKey.METRICS_REPORT_INTERVAL) : 10; - - PARAMETER.metricsPrometheusEnable = - config.hasPath(ConfigKey.METRICS_PROMETHEUS_ENABLE) - && config.getBoolean(ConfigKey.METRICS_PROMETHEUS_ENABLE); - PARAMETER.metricsPrometheusPort = config.hasPath(ConfigKey.METRICS_PROMETHEUS_PORT) ? config - .getInt(ConfigKey.METRICS_PROMETHEUS_PORT) : 9527; - PARAMETER.setOpenHistoryQueryWhenLiteFN( - config.hasPath(ConfigKey.NODE_OPEN_HISTORY_QUERY_WHEN_LITEFN) - && config.getBoolean(ConfigKey.NODE_OPEN_HISTORY_QUERY_WHEN_LITEFN)); - - PARAMETER.historyBalanceLookup = config.hasPath(ConfigKey.HISTORY_BALANCE_LOOKUP) && config - .getBoolean(ConfigKey.HISTORY_BALANCE_LOOKUP); - - if (config.hasPath(ConfigKey.OPEN_PRINT_LOG)) { - PARAMETER.openPrintLog = config.getBoolean(ConfigKey.OPEN_PRINT_LOG); - } - - PARAMETER.openTransactionSort = config.hasPath(ConfigKey.OPEN_TRANSACTION_SORT) && config - .getBoolean(ConfigKey.OPEN_TRANSACTION_SORT); - - PARAMETER.allowAccountAssetOptimization = config - .hasPath(ConfigKey.ALLOW_ACCOUNT_ASSET_OPTIMIZATION) ? config - .getInt(ConfigKey.ALLOW_ACCOUNT_ASSET_OPTIMIZATION) : 0; - - PARAMETER.allowAssetOptimization = config - .hasPath(ConfigKey.ALLOW_ASSET_OPTIMIZATION) ? config - .getInt(ConfigKey.ALLOW_ASSET_OPTIMIZATION) : 0; - - PARAMETER.disabledApiList = - config.hasPath(ConfigKey.NODE_DISABLED_API_LIST) - ? config.getStringList(ConfigKey.NODE_DISABLED_API_LIST) - .stream().map(String::toLowerCase).collect(Collectors.toList()) - : Collections.emptyList(); - - if (config.hasPath(ConfigKey.NODE_SHUTDOWN_BLOCK_TIME)) { + // node.shutdown from bean (dot-notation, read in NodeConfig.fromConfig) + String shutdownBlockTime = nc.getShutdownBlockTime(); + if (!shutdownBlockTime.isEmpty()) { try { - PARAMETER.shutdownBlockTime = new CronExpression(config.getString( - ConfigKey.NODE_SHUTDOWN_BLOCK_TIME)); + PARAMETER.shutdownBlockTime = new CronExpression(shutdownBlockTime); } catch (ParseException e) { throw new TronError(e, TronError.ErrCode.AUTO_STOP_PARAMS); } } - - if (config.hasPath(ConfigKey.NODE_SHUTDOWN_BLOCK_HEIGHT)) { - PARAMETER.shutdownBlockHeight = config.getLong(ConfigKey.NODE_SHUTDOWN_BLOCK_HEIGHT); + if (nc.getShutdownBlockHeight() >= 0) { + PARAMETER.shutdownBlockHeight = nc.getShutdownBlockHeight(); } - - if (config.hasPath(ConfigKey.NODE_SHUTDOWN_BLOCK_COUNT)) { - PARAMETER.shutdownBlockCount = config.getLong(ConfigKey.NODE_SHUTDOWN_BLOCK_COUNT); + if (nc.getShutdownBlockCount() >= 0) { + PARAMETER.shutdownBlockCount = nc.getShutdownBlockCount(); } + } - if (config.hasPath(ConfigKey.BLOCK_CACHE_TIMEOUT)) { - PARAMETER.blockCacheTimeout = config.getLong(ConfigKey.BLOCK_CACHE_TIMEOUT); + /** + * Apply platform-specific constraints after all config sources are resolved. + * ARM64 does not support LevelDB (native JNI library unavailable), + * so db.engine is forced to RocksDB regardless of config or CLI settings. + */ + private static void applyPlatformConstraints() { + if (Arch.isArm64() + && !Constant.ROCKSDB.equalsIgnoreCase(PARAMETER.storage.getDbEngine())) { + logger.warn("ARM64 only supports RocksDB, ignoring db.engine='{}'", + PARAMETER.storage.getDbEngine()); + PARAMETER.storage.setDbEngine(Constant.ROCKSDB); } + } - if (config.hasPath(ConfigKey.ALLOW_NEW_REWARD)) { - PARAMETER.allowNewReward = config.getLong(ConfigKey.ALLOW_NEW_REWARD); - if (PARAMETER.allowNewReward > 1) { - PARAMETER.allowNewReward = 1; - } - if (PARAMETER.allowNewReward < 0) { - PARAMETER.allowNewReward = 0; - } - } + /** + * Apply parameters from config file. + */ + public static void applyConfigParams( + final Config config) { - if (config.hasPath(ConfigKey.MEMO_FEE)) { - PARAMETER.memoFee = config.getLong(ConfigKey.MEMO_FEE); - if (PARAMETER.memoFee > 1_000_000_000) { - PARAMETER.memoFee = 1_000_000_000; - } - if (PARAMETER.memoFee < 0) { - PARAMETER.memoFee = 0; - } - } + Wallet.setAddressPreFixByte(ADD_PRE_FIX_BYTE_MAINNET); + Wallet.setAddressPreFixString(Constant.ADD_PRE_FIX_STRING_MAINNET); - if (config.hasPath(ConfigKey.ALLOW_DELEGATE_OPTIMIZATION)) { - PARAMETER.allowDelegateOptimization = config.getLong(ConfigKey.ALLOW_DELEGATE_OPTIMIZATION); - PARAMETER.allowDelegateOptimization = min(PARAMETER.allowDelegateOptimization, 1, true); - PARAMETER.allowDelegateOptimization = max(PARAMETER.allowDelegateOptimization, 0, true); - } + // crypto.engine handled by MiscConfig - if (config.hasPath(ConfigKey.COMMITTEE_UNFREEZE_DELAY_DAYS)) { - PARAMETER.unfreezeDelayDays = config.getLong(ConfigKey.COMMITTEE_UNFREEZE_DELAY_DAYS); - if (PARAMETER.unfreezeDelayDays > 365) { - PARAMETER.unfreezeDelayDays = 365; - } - if (PARAMETER.unfreezeDelayDays < 0) { - PARAMETER.unfreezeDelayDays = 0; - } - } + // VM config: bind from config.conf "vm" section + VmConfig vmConfig = VmConfig.fromConfig(config); + applyVmConfig(vmConfig); - if (config.hasPath(ConfigKey.ALLOW_DYNAMIC_ENERGY)) { - PARAMETER.allowDynamicEnergy = config.getLong(ConfigKey.ALLOW_DYNAMIC_ENERGY); - PARAMETER.allowDynamicEnergy = min(PARAMETER.allowDynamicEnergy, 1, true); - PARAMETER.allowDynamicEnergy = max(PARAMETER.allowDynamicEnergy, 0, true); - } + // Node config: bind from config.conf "node" section + NodeConfig nodeConfig = NodeConfig.fromConfig(config); + applyNodeConfig(nodeConfig); - if (config.hasPath(ConfigKey.DYNAMIC_ENERGY_THRESHOLD)) { - PARAMETER.dynamicEnergyThreshold = config.getLong(ConfigKey.DYNAMIC_ENERGY_THRESHOLD); - PARAMETER.dynamicEnergyThreshold - = min(PARAMETER.dynamicEnergyThreshold, 100_000_000_000_000_000L, true); - PARAMETER.dynamicEnergyThreshold = max(PARAMETER.dynamicEnergyThreshold, 0, true); - } + // vm.minTimeRatio, vm.maxTimeRatio, vm.longRunningTime already handled by VmConfig above - if (config.hasPath(ConfigKey.DYNAMIC_ENERGY_INCREASE_FACTOR)) { - PARAMETER.dynamicEnergyIncreaseFactor - = config.getLong(ConfigKey.DYNAMIC_ENERGY_INCREASE_FACTOR); - PARAMETER.dynamicEnergyIncreaseFactor = - min(PARAMETER.dynamicEnergyIncreaseFactor, DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE, true); - PARAMETER.dynamicEnergyIncreaseFactor = max(PARAMETER.dynamicEnergyIncreaseFactor, 0, true); - } + // Storage config: bind from config.conf "storage" section + PARAMETER.storage = new Storage(); + StorageConfig storageConfig = StorageConfig.fromConfig(config); + applyStorageConfig(storageConfig); - if (config.hasPath(ConfigKey.DYNAMIC_ENERGY_MAX_FACTOR)) { - PARAMETER.dynamicEnergyMaxFactor - = config.getLong(ConfigKey.DYNAMIC_ENERGY_MAX_FACTOR); - PARAMETER.dynamicEnergyMaxFactor = - min(PARAMETER.dynamicEnergyMaxFactor, DYNAMIC_ENERGY_MAX_FACTOR_RANGE, true); - PARAMETER.dynamicEnergyMaxFactor = max(PARAMETER.dynamicEnergyMaxFactor, 0, true); - } + // seed.node is a top-level config section (not under "node") — config structure + // is arguably misplaced, but preserved for backward compatibility - PARAMETER.dynamicConfigEnable = config.hasPath(ConfigKey.DYNAMIC_CONFIG_ENABLE) - && config.getBoolean(ConfigKey.DYNAMIC_CONFIG_ENABLE); - if (config.hasPath(ConfigKey.DYNAMIC_CONFIG_CHECK_INTERVAL)) { - PARAMETER.dynamicConfigCheckInterval - = config.getLong(ConfigKey.DYNAMIC_CONFIG_CHECK_INTERVAL); - if (PARAMETER.dynamicConfigCheckInterval <= 0) { - PARAMETER.dynamicConfigCheckInterval = 600; - } - } else { - PARAMETER.dynamicConfigCheckInterval = 600; - } + // Genesis config: bind from config.conf "genesis.block" section + GenesisConfig genesisConfig = GenesisConfig.fromConfig(config); + applyGenesisConfig(genesisConfig, config); - PARAMETER.allowTvmShangHai = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_SHANGHAI) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_SHANGHAI) : 0; + // Block config: bind from config.conf "block" section + BlockConfig blockConfig = BlockConfig.fromConfig(config); + applyBlockConfig(blockConfig); - PARAMETER.unsolidifiedBlockCheck = - config.hasPath(ConfigKey.UNSOLIDIFIED_BLOCK_CHECK) - && config.getBoolean(ConfigKey.UNSOLIDIFIED_BLOCK_CHECK); + // node discovery, legacy fallback, p2p, dns — all handled in applyNodeConfig - PARAMETER.maxUnsolidifiedBlocks = - config.hasPath(ConfigKey.MAX_UNSOLIDIFIED_BLOCKS) ? config - .getInt(ConfigKey.MAX_UNSOLIDIFIED_BLOCKS) : 54; + // Misc config: storage, trx, energy — small domains, read via beans + MiscConfig miscConfig = MiscConfig.fromConfig(config); + applyMiscConfig(miscConfig); - long allowOldRewardOpt = config.hasPath(ConfigKey.COMMITTEE_ALLOW_OLD_REWARD_OPT) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_OLD_REWARD_OPT) : 0; - if (allowOldRewardOpt == 1 && PARAMETER.allowNewRewardAlgorithm != 1 - && PARAMETER.allowNewReward != 1 && PARAMETER.allowTvmVote != 1) { - throw new IllegalArgumentException( - "At least one of the following proposals is required to be opened first: " - + "committee.allowNewRewardAlgorithm = 1" - + " or committee.allowNewReward = 1" - + " or committee.allowTvmVote = 1."); - } - PARAMETER.allowOldRewardOpt = allowOldRewardOpt; + // vm, committee already handled above + + // Committee config: bind from config.conf "committee" section + CommitteeConfig committeeConfig = CommitteeConfig.fromConfig(config); + applyCommitteeConfig(committeeConfig); + + // shielded transaction API, active/passive/fastForward — handled in applyNodeConfig + + // Rate limiter config: bind from config.conf "rate.limiter" section + RateLimiterConfig rlConfig = RateLimiterConfig.fromConfig(config); + applyRateLimiterConfig(rlConfig, config); + + // Node backup: from NodeConfig bean + applyNodeBackupConfig(nodeConfig); - PARAMETER.allowEnergyAdjustment = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_ENERGY_ADJUSTMENT) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_ENERGY_ADJUSTMENT) : 0; + // actuatorSet already set in applyMiscConfig - PARAMETER.allowStrictMath = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_STRICT_MATH) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_STRICT_MATH) : 0; + // Metrics config: bind from config.conf "node.metrics" section + MetricsConfig metricsConfig = MetricsConfig.fromConfig(config); + applyMetricsConfig(metricsConfig); - PARAMETER.consensusLogicOptimization = - config.hasPath(ConfigKey.COMMITTEE_CONSENSUS_LOGIC_OPTIMIZATION) ? config - .getInt(ConfigKey.COMMITTEE_CONSENSUS_LOGIC_OPTIMIZATION) : 0; + // historyBalanceLookup already handled by MiscConfig above - PARAMETER.allowTvmCancun = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_CANCUN) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_CANCUN) : 0; + // node.shutdown — handled in applyNodeConfig - PARAMETER.allowTvmBlob = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_BLOB) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_BLOB) : 0; + // Event config: bind from config.conf "event.subscribe" section + EventConfig eventConfig = EventConfig.fromConfig(config); + applyEventConfig(eventConfig); PARAMETER.allowTvmOsaka = config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_OSAKA) ? config @@ -1198,26 +1001,20 @@ private static void initLocalWitnesses(Config config, CLIParameter cmd) { return; } - String witnessAddr = config.hasPath(ConfigKey.LOCAL_WITNESS_ACCOUNT_ADDRESS) - ? config.getString(ConfigKey.LOCAL_WITNESS_ACCOUNT_ADDRESS) : null; + LocalWitnessConfig lwConfig = LocalWitnessConfig.fromConfig(config); // path 2: config localwitness (private key list) - if (config.hasPath(ConfigKey.LOCAL_WITNESS)) { - List keys = config.getStringList(ConfigKey.LOCAL_WITNESS); - if (!keys.isEmpty()) { - localWitnesses = WitnessInitializer.initFromCFGPrivateKey(keys, witnessAddr); - return; - } + if (!lwConfig.getPrivateKeys().isEmpty()) { + localWitnesses = WitnessInitializer.initFromCFGPrivateKey( + lwConfig.getPrivateKeys(), lwConfig.getAccountAddress()); + return; } // path 3: config localwitnesskeystore + password - if (config.hasPath(ConfigKey.LOCAL_WITNESS_KEYSTORE)) { - List keystores = config.getStringList(ConfigKey.LOCAL_WITNESS_KEYSTORE); - if (!keystores.isEmpty()) { - localWitnesses = WitnessInitializer.initFromKeystore( - keystores, cmd.password, witnessAddr); - return; - } + if (!lwConfig.getKeystores().isEmpty()) { + localWitnesses = WitnessInitializer.initFromKeystore( + lwConfig.getKeystores(), cmd.password, lwConfig.getAccountAddress()); + return; } // no private key source configured @@ -1232,83 +1029,23 @@ public static void clearParam() { localWitnesses = null; } - private static long getProposalExpirationTime(final Config config) { - if (config.hasPath(ConfigKey.COMMITTEE_PROPOSAL_EXPIRE_TIME)) { - throw new TronError("It is not allowed to configure committee.proposalExpireTime in " - + "config.conf, please set the value in block.proposalExpireTime.", PARAMETER_INIT); - } - if (config.hasPath(ConfigKey.BLOCK_PROPOSAL_EXPIRE_TIME)) { - long proposalExpireTime = config.getLong(ConfigKey.BLOCK_PROPOSAL_EXPIRE_TIME); - if (proposalExpireTime <= MIN_PROPOSAL_EXPIRE_TIME - || proposalExpireTime >= MAX_PROPOSAL_EXPIRE_TIME) { - throw new TronError("The value[block.proposalExpireTime] is only allowed to " - + "be greater than " + MIN_PROPOSAL_EXPIRE_TIME + " and less than " - + MAX_PROPOSAL_EXPIRE_TIME + "!", PARAMETER_INIT); - } - return proposalExpireTime; - } else { - return DEFAULT_PROPOSAL_EXPIRE_TIME; - } - } + // getProposalExpirationTime removed — logic moved to BlockConfig.fromConfig() - private static List getWitnessesFromConfig(final com.typesafe.config.Config config) { - return config.getObjectList(ConfigKey.GENESIS_BLOCK_WITNESSES).stream() - .map(Args::createWitness) - .collect(Collectors.toCollection(ArrayList::new)); - } + // getWitnessesFromConfig, createWitness, getAccountsFromConfig, createAccount + // removed — logic moved to applyGenesisConfig() - private static Witness createWitness(final ConfigObject witnessAccount) { - final Witness witness = new Witness(); - witness.setAddress( - Commons.decodeFromBase58Check(witnessAccount.get("address").unwrapped().toString())); - witness.setUrl(witnessAccount.get("url").unwrapped().toString()); - witness.setVoteCount(witnessAccount.toConfig().getLong("voteCount")); - return witness; - } - - private static List getAccountsFromConfig(final com.typesafe.config.Config config) { - return config.getObjectList(ConfigKey.GENESIS_BLOCK_ASSETS).stream() - .map(Args::createAccount) - .collect(Collectors.toCollection(ArrayList::new)); - } + // getRateLimiterFromConfig removed — logic moved to applyRateLimiterConfig() - private static Account createAccount(final ConfigObject asset) { - final Account account = new Account(); - account.setAccountName(asset.get("accountName").unwrapped().toString()); - account.setAccountType(asset.get("accountType").unwrapped().toString()); - account.setAddress(Commons.decodeFromBase58Check(asset.get("address").unwrapped().toString())); - account.setBalance(asset.get("balance").unwrapped().toString()); - return account; - } + // getInetSocketAddress removed — use filterInetSocketAddress - private static RateLimiterInitialization getRateLimiterFromConfig( - final com.typesafe.config.Config config) { - RateLimiterInitialization initialization = new RateLimiterInitialization(); - if (config.hasPath(ConfigKey.RATE_LIMITER_HTTP)) { - ArrayList list1 = config - .getObjectList(ConfigKey.RATE_LIMITER_HTTP).stream() - .map(RateLimiterInitialization::createHttpItem) - .collect(Collectors.toCollection(ArrayList::new)); - initialization.setHttpMap(list1); - } - if (config.hasPath(ConfigKey.RATE_LIMITER_RPC)) { - ArrayList list2 = config - .getObjectList(ConfigKey.RATE_LIMITER_RPC).stream() - .map(RateLimiterInitialization::createRpcItem) - .collect(Collectors.toCollection(ArrayList::new)); - initialization.setRpcMap(list2); - } - return initialization; - } - - public static List getInetSocketAddress( - final com.typesafe.config.Config config, String path, boolean filter) { + /** + * Parse and optionally filter a list of address strings. + * Overload that accepts a pre-read list from a bean instead of a config path. + */ + public static List filterInetSocketAddress( + List addressList, boolean filter) { List ret = new ArrayList<>(); - if (!config.hasPath(path)) { - return ret; - } - List list = config.getStringList(path); - for (String configString : list) { + for (String configString : addressList) { InetSocketAddress inetSocketAddress = NetUtil.parseInetSocketAddress(configString); if (filter) { String ip = inetSocketAddress.getAddress().getHostAddress(); @@ -1326,149 +1063,72 @@ public static List getInetSocketAddress( return ret; } - public static List getInetAddress( - final com.typesafe.config.Config config, String path) { - List ret = new ArrayList<>(); - if (!config.hasPath(path)) { - return ret; - } - List list = config.getStringList(path); - for (String configString : list) { - InetSocketAddress inetSocketAddress = NetUtil.parseInetSocketAddress(configString); - ret.add(inetSocketAddress.getAddress()); - } - return ret; - } - - private static EventPluginConfig getEventPluginConfig( - final com.typesafe.config.Config config) { - EventPluginConfig eventPluginConfig = new EventPluginConfig(); - - if (config.hasPath(ConfigKey.EVENT_SUBSCRIBE_VERSION)) { - eventPluginConfig.setVersion(config.getInt(ConfigKey.EVENT_SUBSCRIBE_VERSION)); - } - - if (config.hasPath(ConfigKey.EVENT_SUBSCRIBE_START_SYNC_BLOCK_NUM)) { - eventPluginConfig.setStartSyncBlockNum(config - .getLong(ConfigKey.EVENT_SUBSCRIBE_START_SYNC_BLOCK_NUM)); - } - - boolean useNativeQueue = false; - int bindPort = 0; - int sendQueueLength = 0; - if (config.hasPath(ConfigKey.USE_NATIVE_QUEUE)) { - useNativeQueue = config.getBoolean(ConfigKey.USE_NATIVE_QUEUE); - - if (config.hasPath(ConfigKey.NATIVE_QUEUE_BIND_PORT)) { - bindPort = config.getInt(ConfigKey.NATIVE_QUEUE_BIND_PORT); - } - - if (config.hasPath(ConfigKey.NATIVE_QUEUE_SEND_LENGTH)) { - sendQueueLength = config.getInt(ConfigKey.NATIVE_QUEUE_SEND_LENGTH); - } - - eventPluginConfig.setUseNativeQueue(useNativeQueue); - eventPluginConfig.setBindPort(bindPort); - eventPluginConfig.setSendQueueLength(sendQueueLength); - } - - // use event plugin - if (!useNativeQueue) { - if (config.hasPath(ConfigKey.EVENT_SUBSCRIBE_PATH)) { - String pluginPath = config.getString(ConfigKey.EVENT_SUBSCRIBE_PATH); - if (StringUtils.isNotEmpty(pluginPath)) { - eventPluginConfig.setPluginPath(pluginPath.trim()); - } - } - - if (config.hasPath(ConfigKey.EVENT_SUBSCRIBE_SERVER)) { - String serverAddress = config.getString(ConfigKey.EVENT_SUBSCRIBE_SERVER); - if (StringUtils.isNotEmpty(serverAddress)) { - eventPluginConfig.setServerAddress(serverAddress.trim()); - } - } + // getInetAddress removed — use filterInetSocketAddress - if (config.hasPath(ConfigKey.EVENT_SUBSCRIBE_DB_CONFIG)) { - String dbConfig = config.getString(ConfigKey.EVENT_SUBSCRIBE_DB_CONFIG); - if (StringUtils.isNotEmpty(dbConfig)) { - eventPluginConfig.setDbConfig(dbConfig.trim()); - } - } - } + // getEventPluginConfig removed — logic moved to applyEventConfig() - if (config.hasPath(ConfigKey.EVENT_SUBSCRIBE_TOPICS)) { - List triggerConfigList = config.getObjectList(ConfigKey.EVENT_SUBSCRIBE_TOPICS) - .stream() - .map(Args::createTriggerConfig) - .collect(Collectors.toCollection(ArrayList::new)); - eventPluginConfig.setTriggerConfigList(triggerConfigList); - } - - return eventPluginConfig; - } - - - public static PublishConfig loadDnsPublishConfig(final com.typesafe.config.Config config) { + public static PublishConfig loadDnsPublishConfig(NodeConfig nodeConfig) { PublishConfig publishConfig = new PublishConfig(); - if (config.hasPath(ConfigKey.NODE_DNS_PUBLISH)) { - publishConfig.setDnsPublishEnable(config.getBoolean(ConfigKey.NODE_DNS_PUBLISH)); - } - loadDnsPublishParameters(config, publishConfig); + NodeConfig.DnsConfig dns = nodeConfig.getDns(); + publishConfig.setDnsPublishEnable(dns.isPublish()); + loadDnsPublishParameters(dns, publishConfig); return publishConfig; } + /** + * Load DNS publish parameters from bean into PublishConfig. + * Public method — called by tests and external code. + */ public static void loadDnsPublishParameters(final com.typesafe.config.Config config, PublishConfig publishConfig) { + NodeConfig nodeConfig = NodeConfig.fromConfig(config); + loadDnsPublishParameters(nodeConfig.getDns(), publishConfig); + } + + private static void loadDnsPublishParameters(NodeConfig.DnsConfig dns, + PublishConfig publishConfig) { + if (publishConfig.isDnsPublishEnable()) { - if (config.hasPath(ConfigKey.NODE_DNS_DOMAIN) && StringUtils.isNotEmpty( - config.getString(ConfigKey.NODE_DNS_DOMAIN))) { - publishConfig.setDnsDomain(config.getString(ConfigKey.NODE_DNS_DOMAIN)); + if (StringUtils.isNotEmpty(dns.getDnsDomain())) { + publishConfig.setDnsDomain(dns.getDnsDomain()); } else { - logEmptyError(ConfigKey.NODE_DNS_DOMAIN); + logEmptyError("node.dns.dnsDomain"); } - if (config.hasPath(ConfigKey.NODE_DNS_CHANGE_THRESHOLD)) { - double changeThreshold = config.getDouble(ConfigKey.NODE_DNS_CHANGE_THRESHOLD); - if (changeThreshold > 0) { - publishConfig.setChangeThreshold(changeThreshold); - } else { - logger.error("Check {}, should be bigger than 0, default 0.1", - ConfigKey.NODE_DNS_CHANGE_THRESHOLD); - } + if (dns.getChangeThreshold() > 0) { + publishConfig.setChangeThreshold(dns.getChangeThreshold()); + } else if (dns.getChangeThreshold() != 0.0) { + logger.error("Check node.dns.changeThreshold, should be bigger than 0, default 0.1"); } - if (config.hasPath(ConfigKey.NODE_DNS_MAX_MERGE_SIZE)) { - int maxMergeSize = config.getInt(ConfigKey.NODE_DNS_MAX_MERGE_SIZE); - if (maxMergeSize >= 1 && maxMergeSize <= 5) { - publishConfig.setMaxMergeSize(maxMergeSize); - } else { - logger.error("Check {}, should be [1~5], default 5", ConfigKey.NODE_DNS_MAX_MERGE_SIZE); - } + int maxMergeSize = dns.getMaxMergeSize(); + if (maxMergeSize >= 1 && maxMergeSize <= 5) { + publishConfig.setMaxMergeSize(maxMergeSize); + } else if (maxMergeSize != 0) { + logger.error("Check node.dns.maxMergeSize, should be [1~5], default 5"); } - if (config.hasPath(ConfigKey.NODE_DNS_PRIVATE) && StringUtils.isNotEmpty( - config.getString(ConfigKey.NODE_DNS_PRIVATE))) { - publishConfig.setDnsPrivate(config.getString(ConfigKey.NODE_DNS_PRIVATE)); + if (StringUtils.isNotEmpty(dns.getDnsPrivate())) { + publishConfig.setDnsPrivate(dns.getDnsPrivate()); } else { - logEmptyError(ConfigKey.NODE_DNS_PRIVATE); + logEmptyError("node.dns.dnsPrivate"); } - if (config.hasPath(ConfigKey.NODE_DNS_KNOWN_URLS)) { - publishConfig.setKnownTreeUrls(config.getStringList(ConfigKey.NODE_DNS_KNOWN_URLS)); + if (!dns.getKnownUrls().isEmpty()) { + publishConfig.setKnownTreeUrls(dns.getKnownUrls()); } - if (config.hasPath(ConfigKey.NODE_DNS_STATIC_NODES)) { + if (!dns.getStaticNodes().isEmpty()) { publishConfig.setStaticNodes( - getInetSocketAddress(config, ConfigKey.NODE_DNS_STATIC_NODES, false)); + filterInetSocketAddress(dns.getStaticNodes(), false)); } - if (config.hasPath(ConfigKey.NODE_DNS_SERVER_TYPE) && StringUtils.isNotEmpty( - config.getString(ConfigKey.NODE_DNS_SERVER_TYPE))) { - String serverType = config.getString(ConfigKey.NODE_DNS_SERVER_TYPE); + String serverType = dns.getServerType(); + if (StringUtils.isNotEmpty(serverType)) { if (!"aws".equalsIgnoreCase(serverType) && !"aliyun".equalsIgnoreCase(serverType)) { throw new IllegalArgumentException( - String.format("Check %s, must be aws or aliyun", ConfigKey.NODE_DNS_SERVER_TYPE)); + "Check node.dns.serverType, must be aws or aliyun"); } if ("aws".equalsIgnoreCase(serverType)) { publishConfig.setDnsType(DnsType.AwsRoute53); @@ -1476,38 +1136,34 @@ public static void loadDnsPublishParameters(final com.typesafe.config.Config con publishConfig.setDnsType(DnsType.AliYun); } } else { - logEmptyError(ConfigKey.NODE_DNS_SERVER_TYPE); + logEmptyError("node.dns.serverType"); } - if (config.hasPath(ConfigKey.NODE_DNS_ACCESS_KEY_ID) && StringUtils.isNotEmpty( - config.getString(ConfigKey.NODE_DNS_ACCESS_KEY_ID))) { - publishConfig.setAccessKeyId(config.getString(ConfigKey.NODE_DNS_ACCESS_KEY_ID)); + if (StringUtils.isNotEmpty(dns.getAccessKeyId())) { + publishConfig.setAccessKeyId(dns.getAccessKeyId()); } else { - logEmptyError(ConfigKey.NODE_DNS_ACCESS_KEY_ID); + logEmptyError("node.dns.accessKeyId"); } - if (config.hasPath(ConfigKey.NODE_DNS_ACCESS_KEY_SECRET) && StringUtils.isNotEmpty( - config.getString(ConfigKey.NODE_DNS_ACCESS_KEY_SECRET))) { - publishConfig.setAccessKeySecret(config.getString(ConfigKey.NODE_DNS_ACCESS_KEY_SECRET)); + if (StringUtils.isNotEmpty(dns.getAccessKeySecret())) { + publishConfig.setAccessKeySecret(dns.getAccessKeySecret()); } else { - logEmptyError(ConfigKey.NODE_DNS_ACCESS_KEY_SECRET); + logEmptyError("node.dns.accessKeySecret"); } if (publishConfig.getDnsType() == DnsType.AwsRoute53) { - if (config.hasPath(ConfigKey.NODE_DNS_AWS_REGION) && StringUtils.isNotEmpty( - config.getString(ConfigKey.NODE_DNS_AWS_REGION))) { - publishConfig.setAwsRegion(config.getString(ConfigKey.NODE_DNS_AWS_REGION)); + if (StringUtils.isNotEmpty(dns.getAwsRegion())) { + publishConfig.setAwsRegion(dns.getAwsRegion()); } else { - logEmptyError(ConfigKey.NODE_DNS_AWS_REGION); + logEmptyError("node.dns.awsRegion"); } - if (config.hasPath(ConfigKey.NODE_DNS_AWS_HOST_ZONE_ID)) { - publishConfig.setAwsHostZoneId(config.getString(ConfigKey.NODE_DNS_AWS_HOST_ZONE_ID)); + if (StringUtils.isNotEmpty(dns.getAwsHostZoneId())) { + publishConfig.setAwsHostZoneId(dns.getAwsHostZoneId()); } } else { - if (config.hasPath(ConfigKey.NODE_DNS_ALIYUN_ENDPOINT) && StringUtils.isNotEmpty( - config.getString(ConfigKey.NODE_DNS_ALIYUN_ENDPOINT))) { - publishConfig.setAliDnsEndpoint(config.getString(ConfigKey.NODE_DNS_ALIYUN_ENDPOINT)); + if (StringUtils.isNotEmpty(dns.getAliyunDnsEndpoint())) { + publishConfig.setAliDnsEndpoint(dns.getAliyunDnsEndpoint()); } else { - logEmptyError(ConfigKey.NODE_DNS_ALIYUN_ENDPOINT); + logEmptyError("node.dns.aliyunDnsEndpoint"); } } } @@ -1517,79 +1173,17 @@ private static void logEmptyError(String arg) { throw new IllegalArgumentException(String.format("Check %s, must not be null or empty", arg)); } - private static TriggerConfig createTriggerConfig(ConfigObject triggerObject) { - if (Objects.isNull(triggerObject)) { - return null; - } - - TriggerConfig triggerConfig = new TriggerConfig(); - - String triggerName = triggerObject.get("triggerName").unwrapped().toString(); - triggerConfig.setTriggerName(triggerName); - - String enabled = triggerObject.get("enable").unwrapped().toString(); - triggerConfig.setEnabled("true".equalsIgnoreCase(enabled)); - - String topic = triggerObject.get("topic").unwrapped().toString(); - triggerConfig.setTopic(topic); - - if (triggerObject.containsKey("redundancy")) { - String redundancy = triggerObject.get("redundancy").unwrapped().toString(); - triggerConfig.setRedundancy("true".equalsIgnoreCase(redundancy)); - } - - if (triggerObject.containsKey("ethCompatible")) { - String ethCompatible = triggerObject.get("ethCompatible").unwrapped().toString(); - triggerConfig.setEthCompatible("true".equalsIgnoreCase(ethCompatible)); - } - - if (triggerObject.containsKey("solidified")) { - String solidified = triggerObject.get("solidified").unwrapped().toString(); - triggerConfig.setSolidified("true".equalsIgnoreCase(solidified)); - } - - return triggerConfig; - } - - private static FilterQuery getEventFilter(final com.typesafe.config.Config config) { - FilterQuery filter = new FilterQuery(); - long fromBlockLong = 0; - long toBlockLong = 0; + // createTriggerConfig removed — logic moved to applyEventConfig() + // getEventFilter removed — logic moved to applyEventConfig() - String fromBlock = config.getString(ConfigKey.EVENT_SUBSCRIBE_FROM_BLOCK).trim(); - try { - fromBlockLong = FilterQuery.parseFromBlockNumber(fromBlock); - } catch (Exception e) { - logger.error("invalid filter: fromBlockNumber: {}", fromBlock, e); - return null; - } - filter.setFromBlock(fromBlockLong); - - String toBlock = config.getString(ConfigKey.EVENT_SUBSCRIBE_TO_BLOCK).trim(); - try { - toBlockLong = FilterQuery.parseToBlockNumber(toBlock); - } catch (Exception e) { - logger.error("invalid filter: toBlockNumber: {}", toBlock, e); - return null; - } - filter.setToBlock(toBlockLong); - - List addressList = config.getStringList(ConfigKey.EVENT_SUBSCRIBE_CONTRACT_ADDRESS); - addressList = addressList.stream().filter(address -> StringUtils.isNotEmpty(address)).collect( - Collectors.toList()); - filter.setContractAddressList(addressList); - - List topicList = config.getStringList(ConfigKey.EVENT_SUBSCRIBE_CONTRACT_TOPIC); - topicList = topicList.stream().filter(top -> StringUtils.isNotEmpty(top)).collect( - Collectors.toList()); - filter.setContractTopicList(topicList); - - return filter; + // Kept for backward compatibility — test code calls via reflection with Config param + private static void externalIp(final com.typesafe.config.Config config) { + externalIp(NodeConfig.fromConfig(config)); } - private static void externalIp(final com.typesafe.config.Config config) { - if (!config.hasPath(ConfigKey.NODE_DISCOVERY_EXTERNAL_IP) || config - .getString(ConfigKey.NODE_DISCOVERY_EXTERNAL_IP).trim().isEmpty()) { + private static void externalIp(NodeConfig nodeConfig) { + String externalIp = nodeConfig.getDiscoveryExternalIp(); + if (StringUtils.isEmpty(externalIp)) { if (PARAMETER.nodeExternalIp == null) { logger.info("External IP wasn't set, using ipv4 from libp2p"); PARAMETER.nodeExternalIp = PARAMETER.p2pConfig.getIp(); @@ -1598,69 +1192,12 @@ private static void externalIp(final com.typesafe.config.Config config) { } } } else { - PARAMETER.nodeExternalIp = config.getString(ConfigKey.NODE_DISCOVERY_EXTERNAL_IP).trim(); + PARAMETER.nodeExternalIp = externalIp; } } - private static void initRocksDbSettings(Config config) { - String prefix = ConfigKey.STORAGE_DB_SETTING; - int levelNumber = config.hasPath(prefix + "levelNumber") - ? config.getInt(prefix + "levelNumber") : 7; - int compactThreads = config.hasPath(prefix + "compactThreads") - ? config.getInt(prefix + "compactThreads") - : max(Runtime.getRuntime().availableProcessors(), 1, true); - int blocksize = config.hasPath(prefix + "blocksize") - ? config.getInt(prefix + "blocksize") : 16; - long maxBytesForLevelBase = config.hasPath(prefix + "maxBytesForLevelBase") - ? config.getInt(prefix + "maxBytesForLevelBase") : 256; - double maxBytesForLevelMultiplier = config.hasPath(prefix + "maxBytesForLevelMultiplier") - ? config.getDouble(prefix + "maxBytesForLevelMultiplier") : 10; - int level0FileNumCompactionTrigger = - config.hasPath(prefix + "level0FileNumCompactionTrigger") ? config - .getInt(prefix + "level0FileNumCompactionTrigger") : 2; - long targetFileSizeBase = config.hasPath(prefix + "targetFileSizeBase") ? config - .getLong(prefix + "targetFileSizeBase") : 64; - int targetFileSizeMultiplier = config.hasPath(prefix + "targetFileSizeMultiplier") ? config - .getInt(prefix + "targetFileSizeMultiplier") : 1; - int maxOpenFiles = config.hasPath(prefix + "maxOpenFiles") - ? config.getInt(prefix + "maxOpenFiles") : 5000; - - PARAMETER.rocksDBCustomSettings = RocksDbSettings - .initCustomSettings(levelNumber, compactThreads, blocksize, maxBytesForLevelBase, - maxBytesForLevelMultiplier, level0FileNumCompactionTrigger, - targetFileSizeBase, targetFileSizeMultiplier, maxOpenFiles); - RocksDbSettings.loggingSettings(); - } - - private static void initRocksDbBackupProperty(Config config) { - boolean enable = - config.hasPath(ConfigKey.STORAGE_BACKUP_ENABLE) - && config.getBoolean(ConfigKey.STORAGE_BACKUP_ENABLE); - String propPath = config.hasPath(ConfigKey.STORAGE_BACKUP_PROP_PATH) - ? config.getString(ConfigKey.STORAGE_BACKUP_PROP_PATH) : "prop.properties"; - String bak1path = config.hasPath(ConfigKey.STORAGE_BACKUP_BAK1PATH) - ? config.getString(ConfigKey.STORAGE_BACKUP_BAK1PATH) : "bak1/database/"; - String bak2path = config.hasPath(ConfigKey.STORAGE_BACKUP_BAK2PATH) - ? config.getString(ConfigKey.STORAGE_BACKUP_BAK2PATH) : "bak2/database/"; - int frequency = config.hasPath(ConfigKey.STORAGE_BACKUP_FREQUENCY) - ? config.getInt(ConfigKey.STORAGE_BACKUP_FREQUENCY) : 10000; - PARAMETER.dbBackupConfig = DbBackupConfig.getInstance() - .initArgs(enable, propPath, bak1path, bak2path, frequency); - } - - private static void initBackupProperty(Config config) { - PARAMETER.backupPriority = config.hasPath(ConfigKey.NODE_BACKUP_PRIORITY) - ? config.getInt(ConfigKey.NODE_BACKUP_PRIORITY) : 0; - - PARAMETER.backupPort = config.hasPath(ConfigKey.NODE_BACKUP_PORT) - ? config.getInt(ConfigKey.NODE_BACKUP_PORT) : 10001; - - PARAMETER.keepAliveInterval = config.hasPath(ConfigKey.NODE_BACKUP_KEEPALIVEINTERVAL) - ? config.getInt(ConfigKey.NODE_BACKUP_KEEPALIVEINTERVAL) : 3000; - - PARAMETER.backupMembers = config.hasPath(ConfigKey.NODE_BACKUP_MEMBERS) - ? config.getStringList(ConfigKey.NODE_BACKUP_MEMBERS) : new ArrayList<>(); - } + // initRocksDbSettings, initRocksDbBackupProperty, initBackupProperty + // removed — logic moved to applyStorageConfig() and applyNodeBackupConfig() public static void logConfig() { CommonParameter parameter = CommonParameter.getInstance(); diff --git a/framework/src/main/java/org/tron/core/config/args/ConfigKey.java b/framework/src/main/java/org/tron/core/config/args/ConfigKey.java deleted file mode 100644 index b21c9c440a4..00000000000 --- a/framework/src/main/java/org/tron/core/config/args/ConfigKey.java +++ /dev/null @@ -1,331 +0,0 @@ -package org.tron.core.config.args; - -/** - * HOCON configuration key constants. - * These map to paths in config files (e.g. config.conf) and are read by Args.setParam(). - */ -final class ConfigKey { - - private ConfigKey() { - } - - // local witness - public static final String LOCAL_WITNESS = "localwitness"; // private key - public static final String LOCAL_WITNESS_ACCOUNT_ADDRESS = "localWitnessAccountAddress"; - public static final String LOCAL_WITNESS_KEYSTORE = "localwitnesskeystore"; - - // crypto - public static final String CRYPTO_ENGINE = "crypto.engine"; - - // vm - public static final String VM_SUPPORT_CONSTANT = "vm.supportConstant"; - public static final String VM_MAX_ENERGY_LIMIT_FOR_CONSTANT = "vm.maxEnergyLimitForConstant"; - public static final String VM_LRU_CACHE_SIZE = "vm.lruCacheSize"; - public static final String VM_MIN_TIME_RATIO = "vm.minTimeRatio"; - public static final String VM_MAX_TIME_RATIO = "vm.maxTimeRatio"; - public static final String VM_LONG_RUNNING_TIME = "vm.longRunningTime"; - public static final String VM_ESTIMATE_ENERGY = "vm.estimateEnergy"; - public static final String VM_ESTIMATE_ENERGY_MAX_RETRY = "vm.estimateEnergyMaxRetry"; - public static final String VM_TRACE = "vm.vmTrace"; - public static final String VM_SAVE_INTERNAL_TX = "vm.saveInternalTx"; - public static final String VM_SAVE_FEATURED_INTERNAL_TX = "vm.saveFeaturedInternalTx"; - public static final String VM_SAVE_CANCEL_ALL_UNFREEZE_V2_DETAILS = - "vm.saveCancelAllUnfreezeV2Details"; - - // genesis - public static final String GENESIS_BLOCK = "genesis.block"; - public static final String GENESIS_BLOCK_TIMESTAMP = "genesis.block.timestamp"; - public static final String GENESIS_BLOCK_PARENTHASH = "genesis.block.parentHash"; - public static final String GENESIS_BLOCK_ASSETS = "genesis.block.assets"; - public static final String GENESIS_BLOCK_WITNESSES = "genesis.block.witnesses"; - - // block - public static final String BLOCK_NEED_SYNC_CHECK = "block.needSyncCheck"; - public static final String BLOCK_MAINTENANCE_TIME_INTERVAL = "block.maintenanceTimeInterval"; - public static final String BLOCK_PROPOSAL_EXPIRE_TIME = "block.proposalExpireTime"; - public static final String BLOCK_CHECK_FROZEN_TIME = "block.checkFrozenTime"; - public static final String BLOCK_CACHE_TIMEOUT = "node.blockCacheTimeout"; - - // node - discovery - public static final String NODE_DISCOVERY_ENABLE = "node.discovery.enable"; - public static final String NODE_DISCOVERY_PERSIST = "node.discovery.persist"; - public static final String NODE_DISCOVERY_EXTERNAL_IP = "node.discovery.external.ip"; - - // node - connection - public static final String NODE_EFFECTIVE_CHECK_ENABLE = "node.effectiveCheckEnable"; - public static final String NODE_CONNECTION_TIMEOUT = "node.connection.timeout"; - public static final String NODE_FETCH_BLOCK_TIMEOUT = "node.fetchBlock.timeout"; - public static final String NODE_CHANNEL_READ_TIMEOUT = "node.channel.read.timeout"; - public static final String NODE_MAX_CONNECTIONS = "node.maxConnections"; - public static final String NODE_MIN_CONNECTIONS = "node.minConnections"; - public static final String NODE_MIN_ACTIVE_CONNECTIONS = "node.minActiveConnections"; - public static final String NODE_MAX_CONNECTIONS_WITH_SAME_IP = "node.maxConnectionsWithSameIp"; - public static final String NODE_MIN_PARTICIPATION_RATE = "node.minParticipationRate"; - public static final String NODE_MAX_ACTIVE_NODES = "node.maxActiveNodes"; - public static final String NODE_MAX_ACTIVE_NODES_WITH_SAME_IP = "node.maxActiveNodesWithSameIp"; - public static final String NODE_CONNECT_FACTOR = "node.connectFactor"; - public static final String NODE_ACTIVE_CONNECT_FACTOR = "node.activeConnectFactor"; - public static final String NODE_IS_OPEN_FULL_TCP_DISCONNECT = "node.isOpenFullTcpDisconnect"; - public static final String NODE_INACTIVE_THRESHOLD = "node.inactiveThreshold"; - public static final String NODE_DETECT_ENABLE = "node.nodeDetectEnable"; - public static final String NODE_MAX_HTTP_CONNECT_NUMBER = "node.maxHttpConnectNumber"; - - // node - p2p - public static final String NODE_LISTEN_PORT = "node.listen.port"; - public static final String NODE_P2P_VERSION = "node.p2p.version"; - public static final String NODE_ENABLE_IPV6 = "node.enableIpv6"; - public static final String NODE_SYNC_FETCH_BATCH_NUM = "node.syncFetchBatchNum"; - public static final String NODE_MAX_TPS = "node.maxTps"; - public static final String NODE_NET_MAX_TRX_PER_SECOND = "node.netMaxTrxPerSecond"; - public static final String NODE_TCP_NETTY_WORK_THREAD_NUM = "node.tcpNettyWorkThreadNum"; - public static final String NODE_UDP_NETTY_WORK_THREAD_NUM = "node.udpNettyWorkThreadNum"; - public static final String NODE_VALIDATE_SIGN_THREAD_NUM = "node.validateSignThreadNum"; - public static final String NODE_RECEIVE_TCP_MIN_DATA_LENGTH = "node.receiveTcpMinDataLength"; - public static final String NODE_PRODUCED_TIMEOUT = "node.blockProducedTimeOut"; - public static final String NODE_MAX_TRANSACTION_PENDING_SIZE = "node.maxTransactionPendingSize"; - public static final String NODE_PENDING_TRANSACTION_TIMEOUT = "node.pendingTransactionTimeout"; - public static final String NODE_ACTIVE = "node.active"; - public static final String NODE_PASSIVE = "node.passive"; - public static final String NODE_FAST_FORWARD = "node.fastForward"; - public static final String NODE_MAX_FAST_FORWARD_NUM = "node.maxFastForwardNum"; - public static final String NODE_AGREE_NODE_COUNT = "node.agreeNodeCount"; - public static final String NODE_SOLIDITY_THREADS = "node.solidity.threads"; - public static final String NODE_TRUST_NODE = "node.trustNode"; - public static final String NODE_WALLET_EXTENSION_API = "node.walletExtensionApi"; - public static final String NODE_VALID_CONTRACT_PROTO_THREADS = "node.validContractProto.threads"; - public static final String NODE_SHIELDED_TRANS_IN_PENDING_MAX_COUNTS = - "node.shieldedTransInPendingMaxCounts"; - public static final String NODE_FULLNODE_ALLOW_SHIELDED_TRANSACTION = - "node.fullNodeAllowShieldedTransaction"; - public static final String ALLOW_SHIELDED_TRANSACTION_API = - "node.allowShieldedTransactionApi"; - public static final String NODE_ZEN_TOKENID = "node.zenTokenId"; - public static final String NODE_OPEN_HISTORY_QUERY_WHEN_LITEFN = - "node.openHistoryQueryWhenLiteFN"; - public static final String NODE_METRICS_ENABLE = "node.metricsEnable"; - public static final String NODE_DISABLED_API_LIST = "node.disabledApi"; - - // node - rpc - public static final String NODE_RPC_PORT = "node.rpc.port"; - public static final String NODE_RPC_SOLIDITY_PORT = "node.rpc.solidityPort"; - public static final String NODE_RPC_PBFT_PORT = "node.rpc.PBFTPort"; - public static final String NODE_RPC_ENABLE = "node.rpc.enable"; - public static final String NODE_RPC_SOLIDITY_ENABLE = "node.rpc.solidityEnable"; - public static final String NODE_RPC_PBFT_ENABLE = "node.rpc.PBFTEnable"; - public static final String NODE_RPC_THREAD = "node.rpc.thread"; - public static final String NODE_RPC_MAX_CONCURRENT_CALLS_PER_CONNECTION = - "node.rpc.maxConcurrentCallsPerConnection"; - public static final String NODE_RPC_FLOW_CONTROL_WINDOW = "node.rpc.flowControlWindow"; - public static final String NODE_RPC_MAX_CONNECTION_IDLE_IN_MILLIS = - "node.rpc.maxConnectionIdleInMillis"; - public static final String NODE_RPC_MAX_RST_STREAM = "node.rpc.maxRstStream"; - public static final String NODE_RPC_SECONDS_PER_WINDOW = "node.rpc.secondsPerWindow"; - public static final String NODE_RPC_MAX_CONNECTION_AGE_IN_MILLIS = - "node.rpc.maxConnectionAgeInMillis"; - public static final String NODE_RPC_MAX_MESSAGE_SIZE = "node.rpc.maxMessageSize"; - public static final String NODE_RPC_MAX_HEADER_LIST_SIZE = "node.rpc.maxHeaderListSize"; - public static final String NODE_RPC_REFLECTION_SERVICE = "node.rpc.reflectionService"; - public static final String NODE_RPC_MIN_EFFECTIVE_CONNECTION = - "node.rpc.minEffectiveConnection"; - public static final String NODE_RPC_TRX_CACHE_ENABLE = "node.rpc.trxCacheEnable"; - - // node - http - public static final String NODE_HTTP_FULLNODE_PORT = "node.http.fullNodePort"; - public static final String NODE_HTTP_SOLIDITY_PORT = "node.http.solidityPort"; - public static final String NODE_HTTP_FULLNODE_ENABLE = "node.http.fullNodeEnable"; - public static final String NODE_HTTP_SOLIDITY_ENABLE = "node.http.solidityEnable"; - public static final String NODE_HTTP_PBFT_ENABLE = "node.http.PBFTEnable"; - public static final String NODE_HTTP_PBFT_PORT = "node.http.PBFTPort"; - - // node - jsonrpc - public static final String NODE_JSONRPC_HTTP_FULLNODE_ENABLE = - "node.jsonrpc.httpFullNodeEnable"; - public static final String NODE_JSONRPC_HTTP_FULLNODE_PORT = "node.jsonrpc.httpFullNodePort"; - public static final String NODE_JSONRPC_HTTP_SOLIDITY_ENABLE = - "node.jsonrpc.httpSolidityEnable"; - public static final String NODE_JSONRPC_HTTP_SOLIDITY_PORT = "node.jsonrpc.httpSolidityPort"; - public static final String NODE_JSONRPC_HTTP_PBFT_ENABLE = "node.jsonrpc.httpPBFTEnable"; - public static final String NODE_JSONRPC_HTTP_PBFT_PORT = "node.jsonrpc.httpPBFTPort"; - public static final String NODE_JSONRPC_MAX_BLOCK_RANGE = "node.jsonrpc.maxBlockRange"; - public static final String NODE_JSONRPC_MAX_SUB_TOPICS = "node.jsonrpc.maxSubTopics"; - public static final String NODE_JSONRPC_MAX_BLOCK_FILTER_NUM = - "node.jsonrpc.maxBlockFilterNum"; - - // node - dns - public static final String NODE_DNS_TREE_URLS = "node.dns.treeUrls"; - public static final String NODE_DNS_PUBLISH = "node.dns.publish"; - public static final String NODE_DNS_DOMAIN = "node.dns.dnsDomain"; - public static final String NODE_DNS_CHANGE_THRESHOLD = "node.dns.changeThreshold"; - public static final String NODE_DNS_MAX_MERGE_SIZE = "node.dns.maxMergeSize"; - public static final String NODE_DNS_PRIVATE = "node.dns.dnsPrivate"; - public static final String NODE_DNS_KNOWN_URLS = "node.dns.knownUrls"; - public static final String NODE_DNS_STATIC_NODES = "node.dns.staticNodes"; - public static final String NODE_DNS_SERVER_TYPE = "node.dns.serverType"; - public static final String NODE_DNS_ACCESS_KEY_ID = "node.dns.accessKeyId"; - public static final String NODE_DNS_ACCESS_KEY_SECRET = "node.dns.accessKeySecret"; - public static final String NODE_DNS_ALIYUN_ENDPOINT = "node.dns.aliyunDnsEndpoint"; - public static final String NODE_DNS_AWS_REGION = "node.dns.awsRegion"; - public static final String NODE_DNS_AWS_HOST_ZONE_ID = "node.dns.awsHostZoneId"; - - // node - backup - public static final String NODE_BACKUP_PRIORITY = "node.backup.priority"; - public static final String NODE_BACKUP_PORT = "node.backup.port"; - public static final String NODE_BACKUP_KEEPALIVEINTERVAL = "node.backup.keepAliveInterval"; - public static final String NODE_BACKUP_MEMBERS = "node.backup.members"; - - // node - shutdown - public static final String NODE_SHUTDOWN_BLOCK_TIME = "node.shutdown.BlockTime"; - public static final String NODE_SHUTDOWN_BLOCK_HEIGHT = "node.shutdown.BlockHeight"; - public static final String NODE_SHUTDOWN_BLOCK_COUNT = "node.shutdown.BlockCount"; - - // node - dynamic config - public static final String DYNAMIC_CONFIG_ENABLE = "node.dynamicConfig.enable"; - public static final String DYNAMIC_CONFIG_CHECK_INTERVAL = "node.dynamicConfig.checkInterval"; - - // node - unsolidified - public static final String UNSOLIDIFIED_BLOCK_CHECK = "node.unsolidifiedBlockCheck"; - public static final String MAX_UNSOLIDIFIED_BLOCKS = "node.maxUnsolidifiedBlocks"; - - // node - misc - public static final String OPEN_PRINT_LOG = "node.openPrintLog"; - public static final String OPEN_TRANSACTION_SORT = "node.openTransactionSort"; - - // committee - public static final String COMMITTEE_ALLOW_CREATION_OF_CONTRACTS = - "committee.allowCreationOfContracts"; - public static final String COMMITTEE_ALLOW_MULTI_SIGN = "committee.allowMultiSign"; - public static final String COMMITTEE_ALLOW_ADAPTIVE_ENERGY = "committee.allowAdaptiveEnergy"; - public static final String COMMITTEE_ALLOW_DELEGATE_RESOURCE = - "committee.allowDelegateResource"; - public static final String COMMITTEE_ALLOW_SAME_TOKEN_NAME = "committee.allowSameTokenName"; - public static final String COMMITTEE_ALLOW_TVM_TRANSFER_TRC10 = - "committee.allowTvmTransferTrc10"; - public static final String COMMITTEE_ALLOW_TVM_CONSTANTINOPLE = - "committee.allowTvmConstantinople"; - public static final String COMMITTEE_ALLOW_TVM_SOLIDITY059 = "committee.allowTvmSolidity059"; - public static final String COMMITTEE_FORBID_TRANSFER_TO_CONTRACT = - "committee.forbidTransferToContract"; - public static final String COMMITTEE_ALLOW_SHIELDED_TRC20_TRANSACTION = - "committee.allowShieldedTRC20Transaction"; - public static final String COMMITTEE_ALLOW_TVM_ISTANBUL = "committee.allowTvmIstanbul"; - public static final String COMMITTEE_ALLOW_MARKET_TRANSACTION = - "committee.allowMarketTransaction"; - public static final String COMMITTEE_ALLOW_PROTO_FILTER_NUM = - "committee.allowProtoFilterNum"; - public static final String COMMITTEE_ALLOW_ACCOUNT_STATE_ROOT = - "committee.allowAccountStateRoot"; - public static final String COMMITTEE_ALLOW_PBFT = "committee.allowPBFT"; - public static final String COMMITTEE_PBFT_EXPIRE_NUM = "committee.pBFTExpireNum"; - public static final String COMMITTEE_ALLOW_TRANSACTION_FEE_POOL = - "committee.allowTransactionFeePool"; - public static final String COMMITTEE_ALLOW_BLACK_HOLE_OPTIMIZATION = - "committee.allowBlackHoleOptimization"; - public static final String COMMITTEE_ALLOW_NEW_RESOURCE_MODEL = - "committee.allowNewResourceModel"; - public static final String COMMITTEE_ALLOW_RECEIPTS_MERKLE_ROOT = - "committee.allowReceiptsMerkleRoot"; - public static final String COMMITTEE_ALLOW_TVM_FREEZE = "committee.allowTvmFreeze"; - public static final String COMMITTEE_ALLOW_TVM_VOTE = "committee.allowTvmVote"; - public static final String COMMITTEE_UNFREEZE_DELAY_DAYS = "committee.unfreezeDelayDays"; - public static final String COMMITTEE_ALLOW_TVM_LONDON = "committee.allowTvmLondon"; - public static final String COMMITTEE_ALLOW_TVM_COMPATIBLE_EVM = - "committee.allowTvmCompatibleEvm"; - public static final String COMMITTEE_ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX = - "committee.allowHigherLimitForMaxCpuTimeOfOneTx"; - public static final String COMMITTEE_ALLOW_NEW_REWARD_ALGORITHM = - "committee.allowNewRewardAlgorithm"; - public static final String COMMITTEE_ALLOW_OPTIMIZED_RETURN_VALUE_OF_CHAIN_ID = - "committee.allowOptimizedReturnValueOfChainId"; - public static final String COMMITTEE_CHANGED_DELEGATION = "committee.changedDelegation"; - public static final String COMMITTEE_ALLOW_TVM_SHANGHAI = "committee.allowTvmShangHai"; - public static final String COMMITTEE_ALLOW_OLD_REWARD_OPT = "committee.allowOldRewardOpt"; - public static final String COMMITTEE_ALLOW_ENERGY_ADJUSTMENT = - "committee.allowEnergyAdjustment"; - public static final String COMMITTEE_ALLOW_STRICT_MATH = "committee.allowStrictMath"; - public static final String COMMITTEE_CONSENSUS_LOGIC_OPTIMIZATION = - "committee.consensusLogicOptimization"; - public static final String COMMITTEE_ALLOW_TVM_CANCUN = "committee.allowTvmCancun"; - public static final String COMMITTEE_ALLOW_TVM_BLOB = "committee.allowTvmBlob"; - public static final String COMMITTEE_PROPOSAL_EXPIRE_TIME = "committee.proposalExpireTime"; - public static final String COMMITTEE_ALLOW_TVM_OSAKA = "committee.allowTvmOsaka"; - public static final String ALLOW_ACCOUNT_ASSET_OPTIMIZATION = - "committee.allowAccountAssetOptimization"; - public static final String ALLOW_ASSET_OPTIMIZATION = "committee.allowAssetOptimization"; - public static final String ALLOW_NEW_REWARD = "committee.allowNewReward"; - public static final String MEMO_FEE = "committee.memoFee"; - public static final String ALLOW_DELEGATE_OPTIMIZATION = - "committee.allowDelegateOptimization"; - public static final String ALLOW_DYNAMIC_ENERGY = "committee.allowDynamicEnergy"; - public static final String DYNAMIC_ENERGY_THRESHOLD = "committee.dynamicEnergyThreshold"; - public static final String DYNAMIC_ENERGY_INCREASE_FACTOR = - "committee.dynamicEnergyIncreaseFactor"; - public static final String DYNAMIC_ENERGY_MAX_FACTOR = "committee.dynamicEnergyMaxFactor"; - - // storage - public static final String STORAGE_NEEDTO_UPDATE_ASSET = "storage.needToUpdateAsset"; - public static final String STORAGE_BACKUP_ENABLE = "storage.backup.enable"; - public static final String STORAGE_BACKUP_PROP_PATH = "storage.backup.propPath"; - public static final String STORAGE_BACKUP_BAK1PATH = "storage.backup.bak1path"; - public static final String STORAGE_BACKUP_BAK2PATH = "storage.backup.bak2path"; - public static final String STORAGE_BACKUP_FREQUENCY = "storage.backup.frequency"; - public static final String STORAGE_DB_SETTING = "storage.dbSettings."; - public static final String HISTORY_BALANCE_LOOKUP = "storage.balance.history.lookup"; - - // event - public static final String EVENT_SUBSCRIBE = "event.subscribe"; - public static final String EVENT_SUBSCRIBE_ENABLE = "event.subscribe.enable"; - public static final String EVENT_SUBSCRIBE_FILTER = "event.subscribe.filter"; - public static final String EVENT_SUBSCRIBE_VERSION = "event.subscribe.version"; - public static final String EVENT_SUBSCRIBE_START_SYNC_BLOCK_NUM = - "event.subscribe.startSyncBlockNum"; - public static final String EVENT_SUBSCRIBE_PATH = "event.subscribe.path"; - public static final String EVENT_SUBSCRIBE_SERVER = "event.subscribe.server"; - public static final String EVENT_SUBSCRIBE_DB_CONFIG = "event.subscribe.dbconfig"; - public static final String EVENT_SUBSCRIBE_TOPICS = "event.subscribe.topics"; - public static final String EVENT_SUBSCRIBE_FROM_BLOCK = "event.subscribe.filter.fromblock"; - public static final String EVENT_SUBSCRIBE_TO_BLOCK = "event.subscribe.filter.toblock"; - public static final String EVENT_SUBSCRIBE_CONTRACT_ADDRESS = - "event.subscribe.filter.contractAddress"; - public static final String EVENT_SUBSCRIBE_CONTRACT_TOPIC = - "event.subscribe.filter.contractTopic"; - public static final String USE_NATIVE_QUEUE = "event.subscribe.native.useNativeQueue"; - public static final String NATIVE_QUEUE_BIND_PORT = "event.subscribe.native.bindport"; - public static final String NATIVE_QUEUE_SEND_LENGTH = - "event.subscribe.native.sendqueuelength"; - - // rate limiter - public static final String RATE_LIMITER = "rate.limiter"; - public static final String RATE_LIMITER_GLOBAL_QPS = "rate.limiter.global.qps"; - public static final String RATE_LIMITER_GLOBAL_IP_QPS = "rate.limiter.global.ip.qps"; - public static final String RATE_LIMITER_GLOBAL_API_QPS = "rate.limiter.global.api.qps"; - public static final String RATE_LIMITER_HTTP = "rate.limiter.http"; - public static final String RATE_LIMITER_RPC = "rate.limiter.rpc"; - public static final String RATE_LIMITER_P2P_SYNC_BLOCK_CHAIN = - "rate.limiter.p2p.syncBlockChain"; - public static final String RATE_LIMITER_P2P_FETCH_INV_DATA = "rate.limiter.p2p.fetchInvData"; - public static final String RATE_LIMITER_P2P_DISCONNECT = "rate.limiter.p2p.disconnect"; - - // metrics - public static final String METRICS_STORAGE_ENABLE = "node.metrics.storageEnable"; - public static final String METRICS_INFLUXDB_IP = "node.metrics.influxdb.ip"; - public static final String METRICS_INFLUXDB_PORT = "node.metrics.influxdb.port"; - public static final String METRICS_INFLUXDB_DATABASE = "node.metrics.influxdb.database"; - public static final String METRICS_REPORT_INTERVAL = - "node.metrics.influxdb.metricsReportInterval"; - public static final String METRICS_PROMETHEUS_ENABLE = "node.metrics.prometheus.enable"; - public static final String METRICS_PROMETHEUS_PORT = "node.metrics.prometheus.port"; - - // seed - public static final String SEED_NODE_IP_LIST = "seed.node.ip.list"; - - // transaction - public static final String TRX_REFERENCE_BLOCK = "trx.reference.block"; - public static final String TRX_EXPIRATION_TIME_IN_MILLIS_SECONDS = - "trx.expiration.timeInMilliseconds"; - - // energy - public static final String ENERGY_LIMIT_BLOCK_NUM = "enery.limit.block.num"; - - // actuator - public static final String ACTUATOR_WHITELIST = "actuator.whitelist"; -} diff --git a/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java b/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java index 04ba1b306eb..3c97b5ab3fa 100644 --- a/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java +++ b/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java @@ -68,8 +68,9 @@ public void reload() { } private void updateActiveNodes(Config config) { + NodeConfig nodeConfig = NodeConfig.fromConfig(config); List newActiveNodes = - Args.getInetSocketAddress(config, ConfigKey.NODE_ACTIVE, true); + Args.filterInetSocketAddress(nodeConfig.getActive(), true); parameter.setActiveNodes(newActiveNodes); List activeNodes = TronNetService.getP2pConfig().getActiveNodes(); activeNodes.clear(); @@ -79,7 +80,11 @@ private void updateActiveNodes(Config config) { } private void updateTrustNodes(Config config) { - List newPassiveNodes = Args.getInetAddress(config, ConfigKey.NODE_PASSIVE); + NodeConfig nodeConfig = NodeConfig.fromConfig(config); + List newPassiveNodes = new java.util.ArrayList<>(); + for (InetSocketAddress sa : Args.filterInetSocketAddress(nodeConfig.getPassive(), false)) { + newPassiveNodes.add(sa.getAddress()); + } parameter.setPassiveNodes(newPassiveNodes); List trustNodes = TronNetService.getP2pConfig().getTrustNodes(); trustNodes.clear(); 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 88d893bcf97..fdb4aa7f0e8 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 @@ -144,7 +144,7 @@ public void testIpFromLibP2p() Assert.assertEquals("46.168.1.1", configuredExternalIp); Config config = Configuration.getByFileName(TestConstants.TEST_CONF); - Config config3 = config.withoutPath(ConfigKey.NODE_DISCOVERY_EXTERNAL_IP); + Config config3 = config.withoutPath("node.discovery.external.ip"); CommonParameter.getInstance().setNodeExternalIp(null); From d24967509d3bcfeb64dd885ccebfb4ee01a69440 Mon Sep 17 00:00:00 2001 From: vividctrlalt Date: Wed, 1 Apr 2026 07:03:47 +0000 Subject: [PATCH 10/14] refactor(config): use reference.conf as single source of defaults Move all default values from scattered bean field initializers into reference.conf, making it the single source of truth for config defaults. Expose config beans as static singletons for convenient access. - Add comprehensive reference.conf with defaults for all config domains - Auto-bind discovery, PBFT, and list fields in NodeConfig - Expose config beans as static singletons (NodeConfig.getInstance() etc.) - Move postProcess logic into bean classes - Fix test configs (external.ip=null -> empty string) - Document manual-read keys with reasons in reference.conf --- .../org/tron/core/config/Configuration.java | 3 +- .../tron/core/config/args/BlockConfig.java | 12 +- .../core/config/args/CommitteeConfig.java | 57 +- .../tron/core/config/args/EventConfig.java | 20 +- .../tron/core/config/args/GenesisConfig.java | 13 +- .../tron/core/config/args/MetricsConfig.java | 12 +- .../org/tron/core/config/args/NodeConfig.java | 372 ++++---- .../core/config/args/RateLimiterConfig.java | 12 +- .../tron/core/config/args/StorageConfig.java | 40 +- .../org/tron/core/config/args/VmConfig.java | 22 +- common/src/main/resources/reference.conf | 830 ++++++++++++++++++ .../core/config/args/BlockConfigTest.java | 26 +- .../core/config/args/CommitteeConfigTest.java | 42 +- .../core/config/args/EventConfigTest.java | 19 +- .../core/config/args/GenesisConfigTest.java | 23 +- .../config/args/LocalWitnessConfigTest.java | 14 +- .../core/config/args/MetricsConfigTest.java | 12 +- .../tron/core/config/args/MiscConfigTest.java | 18 +- .../tron/core/config/args/NodeConfigTest.java | 71 +- .../config/args/RateLimiterConfigTest.java | 12 +- .../core/config/args/StorageConfigTest.java | 32 +- .../tron/core/config/args/VmConfigTest.java | 20 +- .../java/org/tron/core/config/args/Args.java | 111 +-- .../org/tron/core/config/args/ArgsTest.java | 24 +- .../tron/core/exception/TronErrorTest.java | 5 +- .../src/test/resources/config-localtest.conf | 2 +- .../test/resources/config-test-dbbackup.conf | 2 +- .../src/test/resources/config-test-index.conf | 2 +- 28 files changed, 1294 insertions(+), 534 deletions(-) create mode 100644 common/src/main/resources/reference.conf 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 d75fc8430f8..80735290b8c 100644 --- a/common/src/main/java/org/tron/core/config/Configuration.java +++ b/common/src/main/java/org/tron/core/config/Configuration.java @@ -48,7 +48,8 @@ public static com.typesafe.config.Config getByFileName( private static void resolveConfigFile(String fileName, File confFile) { if (confFile.exists()) { - config = ConfigFactory.parseFile(confFile); + config = ConfigFactory.parseFile(confFile) + .withFallback(ConfigFactory.defaultReference()); } else if (Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName) != null) { config = ConfigFactory.load(fileName); 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 eac5c974c83..4746f390e0c 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,7 +7,6 @@ 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; @@ -26,12 +25,7 @@ public class BlockConfig { private long proposalExpireTime = DEFAULT_PROPOSAL_EXPIRE_TIME; private int checkFrozenTime = 1; - private static final Config DEFAULTS = ConfigFactory.parseString( - "needSyncCheck = false\n" - + "maintenanceTimeInterval = 21600000\n" - + "proposalExpireTime = " + DEFAULT_PROPOSAL_EXPIRE_TIME + "\n" - + "checkFrozenTime = 1\n" - ); + // Defaults come from reference.conf (loaded globally via Configuration.java) /** * Create BlockConfig from the "block" section of the application config. @@ -44,9 +38,7 @@ public static BlockConfig fromConfig(Config config) { + "config.conf, please set the value in block.proposalExpireTime.", PARAMETER_INIT); } - Config blockSection = config.hasPath("block") - ? config.getConfig("block").withFallback(DEFAULTS) - : DEFAULTS; + Config blockSection = config.getConfig("block"); 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 d5c5e0c3b92..804f4f70fb8 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,7 +2,6 @@ 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; @@ -84,57 +83,7 @@ public class CommitteeConfig { // proposalExpireTime is NOT a committee field — it's in block.* and handled by BlockConfig - private static final Config DEFAULTS; - - static { - StringBuilder sb = new StringBuilder(); - sb.append("allowCreationOfContracts = 0\n"); - sb.append("allowMultiSign = 0\n"); - sb.append("allowAdaptiveEnergy = 0\n"); - sb.append("allowDelegateResource = 0\n"); - sb.append("allowSameTokenName = 0\n"); - sb.append("allowTvmTransferTrc10 = 0\n"); - sb.append("allowTvmConstantinople = 0\n"); - sb.append("allowTvmSolidity059 = 0\n"); - sb.append("forbidTransferToContract = 0\n"); - sb.append("allowShieldedTRC20Transaction = 0\n"); - sb.append("allowMarketTransaction = 0\n"); - sb.append("allowTransactionFeePool = 0\n"); - sb.append("allowBlackHoleOptimization = 0\n"); - sb.append("allowNewResourceModel = 0\n"); - sb.append("allowTvmIstanbul = 0\n"); - sb.append("allowProtoFilterNum = 0\n"); - sb.append("allowAccountStateRoot = 0\n"); - sb.append("changedDelegation = 0\n"); - sb.append("allowPBFT = 0\n"); - sb.append("pBFTExpireNum = 20\n"); - sb.append("allowTvmFreeze = 0\n"); - sb.append("allowTvmVote = 0\n"); - sb.append("allowTvmLondon = 0\n"); - sb.append("allowTvmCompatibleEvm = 0\n"); - sb.append("allowHigherLimitForMaxCpuTimeOfOneTx = 0\n"); - sb.append("allowNewRewardAlgorithm = 0\n"); - sb.append("allowOptimizedReturnValueOfChainId = 0\n"); - sb.append("allowTvmShangHai = 0\n"); - sb.append("allowOldRewardOpt = 0\n"); - sb.append("allowEnergyAdjustment = 0\n"); - sb.append("allowStrictMath = 0\n"); - sb.append("consensusLogicOptimization = 0\n"); - sb.append("allowTvmCancun = 0\n"); - sb.append("allowTvmBlob = 0\n"); - sb.append("unfreezeDelayDays = 0\n"); - sb.append("allowReceiptsMerkleRoot = 0\n"); - sb.append("allowAccountAssetOptimization = 0\n"); - sb.append("allowAssetOptimization = 0\n"); - sb.append("allowNewReward = 0\n"); - sb.append("memoFee = 0\n"); - sb.append("allowDelegateOptimization = 0\n"); - sb.append("allowDynamicEnergy = 0\n"); - sb.append("dynamicEnergyThreshold = 0\n"); - sb.append("dynamicEnergyIncreaseFactor = 0\n"); - sb.append("dynamicEnergyMaxFactor = 0\n"); - DEFAULTS = ConfigFactory.parseString(sb.toString()); - } + // Defaults come from reference.conf (loaded globally via Configuration.java) /** * Create CommitteeConfig from the "committee" section of the application config. @@ -144,9 +93,7 @@ public class CommitteeConfig { * are excluded from automatic binding and handled manually after. */ public static CommitteeConfig fromConfig(Config config) { - Config section = config.hasPath("committee") - ? config.getConfig("committee").withFallback(DEFAULTS) - : DEFAULTS; + Config section = config.getConfig("committee"); // ConfigBeanFactory derives key names from setter methods. For setPBFTExpireNum() // it expects "PBFTExpireNum" (capital P), but config.conf uses "pBFTExpireNum". 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 31cae06fd30..a1f8a81772f 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 @@ -68,21 +68,7 @@ public static class FilterConfig { private List contractTopic = new ArrayList<>(); } - private static final Config DEFAULTS = ConfigFactory.parseString( - "enable = false\n" - + "version = 0\n" - + "startSyncBlockNum = 0\n" - + "path = \"\"\n" - + "server = \"\"\n" - + "dbconfig = \"\"\n" - + "contractParse = true\n" - + "native { useNativeQueue = true, bindport = 5555, sendqueuelength = 1000 }\n" - + "topics = []\n" - + "topicDefaults { triggerName = \"\", enable = false, topic = \"\"," - + " solidified = false, ethCompatible = false, redundancy = false }\n" - + "filter { fromblock = \"\", toblock = \"\"," - + " contractAddress = [\"\"], contractTopic = [\"\"] }\n" - ); + // Defaults come from reference.conf (loaded globally via Configuration.java) /** * Create EventConfig from the "event.subscribe" section of the application config. @@ -91,9 +77,7 @@ 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.hasPath("event.subscribe") - ? config.getConfig("event.subscribe").withFallback(DEFAULTS) - : DEFAULTS; + Config section = config.getConfig("event.subscribe"); // "native" is a Java reserved word, "topics" has optional fields per item — // strip both before binding, read manually 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 97efb00ab3e..a17e06d5c0f 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,7 +2,6 @@ import com.typesafe.config.Config; import com.typesafe.config.ConfigBeanFactory; -import com.typesafe.config.ConfigFactory; import java.util.ArrayList; import java.util.List; import lombok.Getter; @@ -42,18 +41,10 @@ public static class WitnessConfig { private long voteCount = 0; } - private static final Config DEFAULTS = ConfigFactory.parseString( - "timestamp = \"\"\n" - + "parentHash = \"\"\n" - + "assets = []\n" - + "witnesses = []\n" - ); + // Defaults come from reference.conf (loaded globally via Configuration.java) public static GenesisConfig fromConfig(Config config) { - if (!config.hasPath("genesis.block")) { - return new GenesisConfig(); - } - Config section = config.getConfig("genesis.block").withFallback(DEFAULTS); + Config section = config.getConfig("genesis.block"); 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 708d23cfac8..5b504acdd1c 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,7 +2,6 @@ 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; @@ -36,20 +35,13 @@ public static class InfluxDbConfig { private int metricsReportInterval = 10; } - private static final Config DEFAULTS = ConfigFactory.parseString( - "storageEnable = false\n" - + "prometheus { enable = false, port = 9527 }\n" - + "influxdb { ip = \"\", port = 8086, database = metrics," - + " metricsReportInterval = 10 }\n" - ); + // 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.hasPath("node.metrics") - ? config.getConfig("node.metrics").withFallback(DEFAULTS) - : DEFAULTS; + Config section = config.getConfig("node.metrics"); 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 1c049bf16bc..1088fc1fb2f 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 @@ -1,33 +1,20 @@ package org.tron.core.config.args; +import static org.tron.core.config.Parameter.ChainConstant.MAX_ACTIVE_WITNESS_NUM; + import com.typesafe.config.Config; import com.typesafe.config.ConfigBeanFactory; import com.typesafe.config.ConfigFactory; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -/** - * Node configuration bean for the "node" section of config.conf. - * - *

This section is complex: it mixes flat scalars, dot-notation nested keys - * (e.g. "listen.port"), sub-objects (http, rpc, jsonrpc, p2p, dynamicConfig, dns), - * and list fields (active, passive, fastForward, disabledApi). - * - *

Strategy: - *

    - *
  • ConfigBeanFactory handles simple scalar fields and clean sub-objects
  • - *
  • Dot-notation fields (listen.port, connection.timeout, fetchBlock.timeout, - * solidity.threads) are read manually — HOCON parses them as nested objects, - * not flat keys, so ConfigBeanFactory cannot bind them to flat fields
  • - *
  • PBFT-named fields in sub-beans have the same JavaBean naming issue as - * CommitteeConfig — handled manually after binding
  • - *
  • List fields are read manually since ConfigBeanFactory expects bean lists
  • - *
- */ +// Node configuration bean for the "node" section of config.conf. +// ConfigBeanFactory auto-binds all fields including sub-beans, dot-notation keys, +// PBFT fields, and list fields. Only legacy key fallbacks and PascalCase shutdown +// keys are read manually. @Slf4j @Getter @Setter @@ -35,7 +22,7 @@ public class NodeConfig { // ---- Flat scalar fields (auto-bound by ConfigBeanFactory) ---- - private String trustNode = "127.0.0.1:50051"; + private String trustNode = ""; private boolean walletExtensionApi = true; private int syncFetchBatchNum = 2000; private int validateSignThreadNum = Runtime.getRuntime().availableProcessors(); @@ -48,28 +35,18 @@ public class NodeConfig { private boolean openPrintLog = true; private boolean openTransactionSort = false; private int maxTps = 1000; - // "isOpenFullTcpDisconnect" in config.conf: JavaBean convention converts - // setOpenFullTcpDisconnect -> key "openFullTcpDisconnect", but config uses - // "isOpenFullTcpDisconnect". Excluded from auto-binding, read manually. + // Config key "isOpenFullTcpDisconnect" cannot auto-bind — read manually in fromConfig() @Getter(lombok.AccessLevel.NONE) @Setter(lombok.AccessLevel.NONE) private boolean isOpenFullTcpDisconnect = false; public boolean isOpenFullTcpDisconnect() { return isOpenFullTcpDisconnect; } - // node.discovery.* and node.channel.read.timeout — dot-notation, manually read - @Getter(lombok.AccessLevel.NONE) - @Setter(lombok.AccessLevel.NONE) - private boolean discoveryEnable = false; - @Getter(lombok.AccessLevel.NONE) - @Setter(lombok.AccessLevel.NONE) - private boolean discoveryPersist = false; - - @Getter(lombok.AccessLevel.NONE) - @Setter(lombok.AccessLevel.NONE) - private String discoveryExternalIp = ""; + // node.discovery.* — HOCON merges into node { discovery { ... } }, auto-bound + private DiscoveryConfig discovery = new DiscoveryConfig(); - // node.shutdown.* — dot-notation, manually read + // node.shutdown.* uses PascalCase keys (BlockTime, BlockHeight, BlockCount) + // that don't match JavaBean naming. Excluded, read manually. @Getter(lombok.AccessLevel.NONE) @Setter(lombok.AccessLevel.NONE) private String shutdownBlockTime = ""; @@ -80,9 +57,9 @@ public class NodeConfig { @Setter(lombok.AccessLevel.NONE) private long shutdownBlockCount = -1; - public boolean isDiscoveryEnable() { return discoveryEnable; } - public boolean isDiscoveryPersist() { return discoveryPersist; } - public String getDiscoveryExternalIp() { return discoveryExternalIp; } + 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; } @@ -96,11 +73,11 @@ public class NodeConfig { private int maxFastForwardNum = 4; private int tcpNettyWorkThreadNum = 0; private int udpNettyWorkThreadNum = 1; - private int validContractProtoThreads = 2; + private ValidContractProtoConfig validContractProto = new ValidContractProtoConfig(); private int shieldedTransInPendingMaxCounts = 10; private long blockCacheTimeout = 60; private long receiveTcpMinDataLength = 2048; - private int channelReadTimeout = 60; + private ChannelConfig channel = new ChannelConfig(); private int maxTransactionPendingSize = 2000; private long pendingTransactionTimeout = 60000; private int agreeNodeCount = 0; @@ -114,28 +91,19 @@ public class NodeConfig { private double disconnectNumberFactor = 0.4; private int maxActiveNodesWithSameIp = 2; - // ---- Dot-notation fields (manually read — HOCON treats them as nested) ---- - // Excluded from ConfigBeanFactory auto-binding because HOCON parses dot-notation - // keys (listen.port, connection.timeout, fetchBlock.timeout, solidity.threads) as - // nested objects, not flat keys. ConfigBeanFactory expects flat key "listenPort" but - // config has "listen { port }". Read manually in fromConfig(). - @Getter(lombok.AccessLevel.NONE) - @Setter(lombok.AccessLevel.NONE) - private int listenPort = 18888; - @Getter(lombok.AccessLevel.NONE) - @Setter(lombok.AccessLevel.NONE) - private int connectionTimeout = 2; - @Getter(lombok.AccessLevel.NONE) - @Setter(lombok.AccessLevel.NONE) - private int fetchBlockTimeout = 200; - @Getter(lombok.AccessLevel.NONE) - @Setter(lombok.AccessLevel.NONE) - private int solidityThreads = Runtime.getRuntime().availableProcessors(); + // ---- Sub-beans matching config's dot-notation nested structure ---- + private ListenConfig listen = new ListenConfig(); + private ConnectionConfig connection = new ConnectionConfig(); + private FetchBlockConfig fetchBlock = new FetchBlockConfig(); + private SolidityConfig solidity = new SolidityConfig(); - public int getListenPort() { return listenPort; } - public int getConnectionTimeout() { return connectionTimeout; } - public int getFetchBlockTimeout() { return fetchBlockTimeout; } - public int getSolidityThreads() { return solidityThreads; } + // 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(); } // ---- List fields (manually read) ---- private List active = new ArrayList<>(); @@ -156,6 +124,65 @@ public class NodeConfig { // Inner static classes for sub-beans // =========================================================================== + // ---- Sub-beans for dot-notation config keys ---- + // HOCON merges dot-notation into nested objects, ConfigBeanFactory auto-binds + + @Getter + @Setter + public static class DiscoveryConfig { + private boolean enable = true; + private boolean persist = true; + private ExternalConfig external = new ExternalConfig(); + + @Getter + @Setter + public static class ExternalConfig { + private String ip = ""; + } + } + + @Getter + @Setter + public static class ListenConfig { + private int port = 18888; + } + + @Getter + @Setter + public static class ConnectionConfig { + private int timeout = 2; + } + + @Getter + @Setter + public static class FetchBlockConfig { + private int timeout = 200; + } + + @Getter + @Setter + public static class SolidityConfig { + private int threads = 8; + } + + @Getter + @Setter + public static class ChannelConfig { + private ReadConfig read = new ReadConfig(); + + @Getter + @Setter + public static class ReadConfig { + private int timeout = 0; + } + } + + @Getter + @Setter + public static class ValidContractProtoConfig { + private int threads = 2; + } + @Getter @Setter public static class P2pConfig { @@ -226,16 +253,16 @@ public void setPBFTPort(int v) { this.pBFTPort = v; } - private int thread = 16; - private int maxConcurrentCallsPerConnection = 100; - private int flowControlWindow = 0; + private int thread = 0; + private int maxConcurrentCallsPerConnection = 2147483647; + private int flowControlWindow = 1048576; private long maxConnectionIdleInMillis = Long.MAX_VALUE; private long maxConnectionAgeInMillis = Long.MAX_VALUE; - private int maxMessageSize = 0; - private int maxHeaderListSize = 0; + private int maxMessageSize = 4194304; + private int maxHeaderListSize = 8192; private int maxRstStream = 0; private int secondsPerWindow = 0; - private int minEffectiveConnection = 0; + private int minEffectiveConnection = 1; private boolean reflectionService = false; private boolean trxCacheEnable = false; } @@ -311,96 +338,7 @@ public static class DnsConfig { private String awsHostZoneId = ""; } - // =========================================================================== - // DEFAULTS - // =========================================================================== - - private static final Config DEFAULTS; - - static { - int cpus = Runtime.getRuntime().availableProcessors(); - StringBuilder sb = new StringBuilder(); - // flat scalars - sb.append("trustNode = \"127.0.0.1:50051\"\n"); - sb.append("walletExtensionApi = true\n"); - sb.append("syncFetchBatchNum = 2000\n"); - sb.append("validateSignThreadNum = ").append(cpus).append("\n"); - sb.append("maxConnections = 30\n"); - sb.append("minConnections = 8\n"); - sb.append("minActiveConnections = 3\n"); - sb.append("maxConnectionsWithSameIp = 2\n"); - sb.append("maxHttpConnectNumber = 50\n"); - sb.append("minParticipationRate = 15\n"); - sb.append("openPrintLog = true\n"); - sb.append("openTransactionSort = false\n"); - sb.append("maxTps = 1000\n"); - // isOpenFullTcpDisconnect excluded from auto-binding — read manually in fromConfig() - sb.append("inactiveThreshold = 600\n"); - sb.append("metricsEnable = false\n"); - sb.append("blockProducedTimeOut = 75\n"); - sb.append("netMaxTrxPerSecond = 700\n"); - sb.append("nodeDetectEnable = false\n"); - sb.append("enableIpv6 = false\n"); - sb.append("effectiveCheckEnable = false\n"); - sb.append("maxFastForwardNum = 4\n"); - sb.append("tcpNettyWorkThreadNum = 0\n"); - sb.append("udpNettyWorkThreadNum = 1\n"); - sb.append("validContractProtoThreads = 2\n"); - sb.append("shieldedTransInPendingMaxCounts = 10\n"); - sb.append("blockCacheTimeout = 60\n"); - sb.append("receiveTcpMinDataLength = 2048\n"); - sb.append("channelReadTimeout = 60\n"); - sb.append("maxTransactionPendingSize = 2000\n"); - sb.append("pendingTransactionTimeout = 60000\n"); - sb.append("agreeNodeCount = 0\n"); - sb.append("openHistoryQueryWhenLiteFN = false\n"); - sb.append("unsolidifiedBlockCheck = false\n"); - sb.append("maxUnsolidifiedBlocks = 54\n"); - sb.append("zenTokenId = \"000000\"\n"); - sb.append("allowShieldedTransactionApi = true\n"); - sb.append("activeConnectFactor = 0.1\n"); - sb.append("connectFactor = 0.6\n"); - sb.append("disconnectNumberFactor = 0.4\n"); - sb.append("maxActiveNodesWithSameIp = 2\n"); - // dot-notation fields (HOCON nests them automatically) - sb.append("listen { port = 18888 }\n"); - sb.append("connection { timeout = 2 }\n"); - sb.append("fetchBlock { timeout = 200 }\n"); - sb.append("solidity { threads = ").append(cpus).append(" }\n"); - // sub-objects - sb.append("p2p { version = 11111 }\n"); - sb.append("http { fullNodeEnable = true, fullNodePort = 8090,"); - sb.append(" solidityEnable = true, solidityPort = 8091,"); - sb.append(" PBFTEnable = true, PBFTPort = 8092 }\n"); - sb.append("rpc { enable = true, port = 50051,"); - sb.append(" solidityEnable = true, solidityPort = 50061,"); - sb.append(" PBFTEnable = true, PBFTPort = 50071,"); - sb.append(" thread = 16, maxConcurrentCallsPerConnection = 100,"); - sb.append(" flowControlWindow = 0, maxConnectionIdleInMillis = "); - sb.append(Long.MAX_VALUE).append(","); - sb.append(" maxConnectionAgeInMillis = ").append(Long.MAX_VALUE).append(","); - sb.append(" maxMessageSize = 0, maxHeaderListSize = 0,"); - sb.append(" maxRstStream = 0, secondsPerWindow = 0,"); - sb.append(" minEffectiveConnection = 0, reflectionService = false,"); - sb.append(" trxCacheEnable = false }\n"); - sb.append("jsonrpc { httpFullNodeEnable = false, httpFullNodePort = 8545,"); - sb.append(" httpSolidityEnable = false, httpSolidityPort = 8555,"); - sb.append(" httpPBFTEnable = false, httpPBFTPort = 8565,"); - sb.append(" maxBlockRange = 5000, maxSubTopics = 1000, maxBlockFilterNum = 50000 }\n"); - sb.append("dynamicConfig { enable = false, checkInterval = 600 }\n"); - sb.append("backup { priority = 0, port = 10001, keepAliveInterval = 3000, members = [] }\n"); - sb.append("dns { treeUrls = [], publish = false, dnsDomain = \"\","); - sb.append(" dnsPrivate = \"\", knownUrls = [], staticNodes = [],"); - sb.append(" maxMergeSize = 0, changeThreshold = 0.0, serverType = \"\","); - sb.append(" accessKeyId = \"\", accessKeySecret = \"\","); - sb.append(" aliyunDnsEndpoint = \"\", awsRegion = \"\", awsHostZoneId = \"\" }\n"); - // list fields - sb.append("active = []\n"); - sb.append("passive = []\n"); - sb.append("fastForward = []\n"); - sb.append("disabledApi = []\n"); - DEFAULTS = ConfigFactory.parseString(sb.toString()); - } + // Defaults come from reference.conf (loaded globally via Configuration.java) // =========================================================================== // Factory method @@ -420,25 +358,16 @@ public static class DnsConfig { * since ConfigBeanFactory expects typed bean lists, not string lists. */ public static NodeConfig fromConfig(Config config) { - Config section = config.hasPath("node") - ? config.getConfig("node").withFallback(DEFAULTS) - : DEFAULTS; - - // --- Phase 1: Auto-bind flat scalars and sub-objects --- - // ConfigBeanFactory will bind all simple fields and nested sub-beans. - // It will skip dot-notation fields (they are nested objects, not scalar keys) - // and may mis-bind PBFT fields due to JavaBean naming. + Config section = config.getConfig("node"); + + // Auto-bind all fields and sub-beans NodeConfig nc = ConfigBeanFactory.create(section, NodeConfig.class); - // --- Phase 2: Dot-notation and naming-mismatch fields (manually read) --- - nc.listenPort = getInt(section, "listen.port", 18888); - nc.connectionTimeout = getInt(section, "connection.timeout", 2); - nc.fetchBlockTimeout = getInt(section, "fetchBlock.timeout", 200); - nc.solidityThreads = getInt(section, "solidity.threads", - Runtime.getRuntime().availableProcessors()); + // isOpenFullTcpDisconnect: boolean "is" prefix breaks JavaBean pairing nc.isOpenFullTcpDisconnect = getBool(section, "isOpenFullTcpDisconnect", false); - // Legacy key fallback: node.maxActiveNodes (old) -> maxConnections (new) + // --- Legacy key fallbacks (backward compatibility) --- + // node.maxActiveNodes (old) -> maxConnections (new) if (section.hasPath("maxActiveNodes")) { nc.maxConnections = section.getInt("maxActiveNodes"); if (section.hasPath("connectFactor")) { @@ -459,16 +388,7 @@ public static NodeConfig fromConfig(Config config) { } else if (section.hasPath("fullNodeAllowShieldedTransaction")) { nc.allowShieldedTransactionApi = section.getBoolean("fullNodeAllowShieldedTransaction"); } - nc.discoveryExternalIp = config.hasPath("node.discovery.external.ip") - ? config.getString("node.discovery.external.ip").trim() : ""; - - // node.discovery.* — dot-notation creates nested HOCON objects - Config discoverySection = config.hasPath("node.discovery") - ? config.getConfig("node.discovery") : ConfigFactory.empty(); - nc.discoveryEnable = getBool(discoverySection, "enable", false); - nc.discoveryPersist = getBool(discoverySection, "persist", false); - - // node.shutdown.* — dot-notation + // node.shutdown.* — PascalCase keys (BlockTime, BlockHeight), cannot auto-bind nc.shutdownBlockTime = config.hasPath("node.shutdown.BlockTime") ? config.getString("node.shutdown.BlockTime") : ""; nc.shutdownBlockHeight = config.hasPath("node.shutdown.BlockHeight") @@ -476,38 +396,64 @@ public static NodeConfig fromConfig(Config config) { nc.shutdownBlockCount = config.hasPath("node.shutdown.BlockCount") ? config.getLong("node.shutdown.BlockCount") : -1; - // node.channel.read.timeout — triple-dot-notation - nc.channelReadTimeout = config.hasPath("node.channel.read.timeout") - ? config.getInt("node.channel.read.timeout") : 0; - - // --- Phase 3: PBFT fields in sub-beans (manually patch) --- - // http - Config httpSection = section.hasPath("http") - ? section.getConfig("http") : ConfigFactory.empty(); - nc.http.pBFTEnable = getBool(httpSection, "PBFTEnable", true); - nc.http.pBFTPort = getInt(httpSection, "PBFTPort", 8092); - - // rpc - Config rpcSection = section.hasPath("rpc") - ? section.getConfig("rpc") : ConfigFactory.empty(); - nc.rpc.pBFTEnable = getBool(rpcSection, "PBFTEnable", true); - nc.rpc.pBFTPort = getInt(rpcSection, "PBFTPort", 50071); - - // jsonrpc - Config jsonrpcSection = section.hasPath("jsonrpc") - ? section.getConfig("jsonrpc") : ConfigFactory.empty(); - nc.jsonrpc.httpPBFTEnable = getBool(jsonrpcSection, "httpPBFTEnable", false); - nc.jsonrpc.httpPBFTPort = getInt(jsonrpcSection, "httpPBFTPort", 8565); - - // --- Phase 4: List fields (manually read) --- - nc.active = getStringList(section, "active"); - nc.passive = getStringList(section, "passive"); - nc.fastForward = getStringList(section, "fastForward"); - nc.disabledApi = getStringList(section, "disabledApi"); + nc.postProcess(); return nc; } + /** + * Post-processing: clamping, dynamic defaults, and cross-field validation. + * Runs after ConfigBeanFactory binding and manual field reads. + */ + private void postProcess() { + // rpcThreadNum: 0 = auto-detect + if (rpc.thread == 0) { + rpc.thread = (Runtime.getRuntime().availableProcessors() + 1) / 2; + } + + // syncFetchBatchNum: clamp to [100, 2000] + if (syncFetchBatchNum > 2000) { + syncFetchBatchNum = 2000; + } + if (syncFetchBatchNum < 100) { + syncFetchBatchNum = 100; + } + + // blockProducedTimeOut: clamp to [30, 100] + if (blockProducedTimeOut < 30) { + blockProducedTimeOut = 30; + } + if (blockProducedTimeOut > 100) { + blockProducedTimeOut = 100; + } + + // inactiveThreshold: minimum 1 + if (inactiveThreshold < 1) { + inactiveThreshold = 1; + } + + // maxFastForwardNum: clamp to [1, MAX_ACTIVE_WITNESS_NUM] + if (maxFastForwardNum > MAX_ACTIVE_WITNESS_NUM) { + maxFastForwardNum = MAX_ACTIVE_WITNESS_NUM; + } + if (maxFastForwardNum < 1) { + maxFastForwardNum = 1; + } + + // agreeNodeCount: 0 = auto (2/3 + 1 of witnesses), clamp to max + if (agreeNodeCount == 0) { + agreeNodeCount = MAX_ACTIVE_WITNESS_NUM * 2 / 3 + 1; + } + if (agreeNodeCount > MAX_ACTIVE_WITNESS_NUM) { + agreeNodeCount = MAX_ACTIVE_WITNESS_NUM; + } + + // dynamicConfigCheckInterval: minimum 600 + if (dynamicConfig.checkInterval <= 0) { + dynamicConfig.checkInterval = 600; + } + } + // =========================================================================== // Helper methods for safe config reads // =========================================================================== @@ -528,10 +474,4 @@ private static String getString(Config config, String path, String defaultValue) return config.hasPath(path) ? config.getString(path) : defaultValue; } - private static List getStringList(Config config, String path) { - if (config.hasPath(path)) { - return config.getStringList(path); - } - return Collections.emptyList(); - } } 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 8974060eedf..eed5ef1898b 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,7 +2,6 @@ import com.typesafe.config.Config; import com.typesafe.config.ConfigBeanFactory; -import com.typesafe.config.ConfigFactory; import java.util.ArrayList; import java.util.List; import lombok.Getter; @@ -67,17 +66,10 @@ public static class RpcRateLimitItem { private String paramString = ""; } - private static final Config DEFAULTS = ConfigFactory.parseString( - "global { qps = 50000, ip { qps = 10000 }, api { qps = 1000 } }\n" - + "p2p { syncBlockChain = 3.0, fetchInvData = 3.0, disconnect = 1.0 }\n" - + "http = []\n" - + "rpc = []\n" - ); + // Defaults come from reference.conf (loaded globally via Configuration.java) public static RateLimiterConfig fromConfig(Config config) { - Config section = config.hasPath("rate.limiter") - ? config.getConfig("rate.limiter").withFallback(DEFAULTS) - : DEFAULTS; + Config section = config.getConfig("rate.limiter"); 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 cd8774de237..5aecebe1059 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 @@ -1,10 +1,7 @@ package org.tron.core.config.args; -import static org.tron.common.math.Maths.max; - import com.typesafe.config.Config; import com.typesafe.config.ConfigBeanFactory; -import com.typesafe.config.ConfigFactory; import java.util.ArrayList; import java.util.List; import lombok.Getter; @@ -98,12 +95,12 @@ public void setSwitch(String v) { @Setter public static class DbSettingsConfig { private int levelNumber = 7; - private int compactThreads = Runtime.getRuntime().availableProcessors(); - private int blocksize = 16; + private int compactThreads = 32; + private int blocksize = 64; private long maxBytesForLevelBase = 256; private double maxBytesForLevelMultiplier = 10; - private int level0FileNumCompactionTrigger = 2; - private long targetFileSizeBase = 64; + private int level0FileNumCompactionTrigger = 4; + private long targetFileSizeBase = 256; private int targetFileSizeMultiplier = 1; private int maxOpenFiles = 5000; } @@ -165,35 +162,10 @@ public static class PropertyConfig { private int maxOpenFiles = 100; } - private static final Config DEFAULTS; - - static { - int cpus = (int) max(Runtime.getRuntime().availableProcessors(), 1, true); - DEFAULTS = ConfigFactory.parseString( - "db { engine = LEVELDB, sync = false, directory = database }\n" - + "index { directory = index, switch = on }\n" - + "transHistory { switch = on }\n" - + "needToUpdateAsset = true\n" - + "dbSettings { levelNumber = 7, compactThreads = " + cpus - + ", blocksize = 16, maxBytesForLevelBase = 256," - + " maxBytesForLevelMultiplier = 10, level0FileNumCompactionTrigger = 2," - + " targetFileSizeBase = 64, targetFileSizeMultiplier = 1, maxOpenFiles = 5000 }\n" - + "backup { enable = false, propPath = \"prop.properties\"," - + " bak1path = \"bak1/database/\", bak2path = \"bak2/database/\"," - + " frequency = 10000 }\n" - + "balance { history { lookup = false } }\n" - + "checkpoint { version = 1, sync = true }\n" - + "snapshot { maxFlushCount = 1 }\n" - + "txCache { estimatedTransactions = 1000, initOptimization = false }\n" - + "properties = []\n" - + "merkleRoot = {}\n" - ); - } + // Defaults come from reference.conf (loaded globally via Configuration.java) public static StorageConfig fromConfig(Config config) { - Config section = config.hasPath("storage") - ? config.getConfig("storage").withFallback(DEFAULTS) - : DEFAULTS; + Config section = config.getConfig("storage"); StorageConfig sc = ConfigBeanFactory.create(section, StorageConfig.class); // Keep raw config for legacy LevelDB per-database option overrides (default, defaultM, defaultL) 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 a9fa0ec3e6a..d583cf4c601 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,7 +2,6 @@ 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; @@ -29,28 +28,13 @@ public class VmConfig { private boolean saveFeaturedInternalTx = false; private boolean saveCancelAllUnfreezeV2Details = false; - private static final Config DEFAULTS = ConfigFactory.parseString( - "supportConstant = false\n" - + "maxEnergyLimitForConstant = 100000000\n" - + "lruCacheSize = 500\n" - + "minTimeRatio = 0.0\n" - + "maxTimeRatio = 5.0\n" - + "longRunningTime = 10\n" - + "estimateEnergy = false\n" - + "estimateEnergyMaxRetry = 3\n" - + "vmTrace = false\n" - + "saveInternalTx = false\n" - + "saveFeaturedInternalTx = false\n" - + "saveCancelAllUnfreezeV2Details = false\n" - ); - /** * 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.hasPath("vm") - ? config.getConfig("vm").withFallback(DEFAULTS) - : DEFAULTS; + Config vmSection = config.getConfig("vm"); 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 new file mode 100644 index 00000000000..fcd181c53b5 --- /dev/null +++ b/common/src/main/resources/reference.conf @@ -0,0 +1,830 @@ +# ============================================================================= +# 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 = 32 + blocksize = 64 // n * KB + maxBytesForLevelBase = 256 // n * MB + maxBytesForLevelMultiplier = 10 + level0FileNumCompactionTrigger = 4 + targetFileSizeBase = 256 // 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 = true + + # 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 = true + persist = true + 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 = "" + metricsReportInterval = 10 + } +} + +node { + # Trust node for solidity node + # trustNode = "ip:port" + trustNode = "127.0.0.1:50051" + + # Expose extension api to public or not + walletExtensionApi = true + + listen.port = 18888 + connection.timeout = 2 + fetchBlock.timeout = 200 + + # Number of blocks to fetch in one batch during sync. Range: [100, 2000]. + syncFetchBatchNum = 2000 + + # Number of validate sign threads, default availableProcessors + validateSignThreadNum = 16 + + maxConnections = 30 + minConnections = 8 + minActiveConnections = 3 + maxConnectionsWithSameIp = 2 + maxHttpConnectNumber = 50 + minParticipationRate = 15 + + # 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 + disconnectNumberFactor = 0.4 + maxActiveNodesWithSameIp = 2 + 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. + solidity.threads = 8 + + # Maximum percentage of producing block interval (provides time for broadcast etc.) + blockProducedTimeOut = 75 + + # 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 + validContractProto.threads = 2 + + dns { + treeUrls = [ + # "tree://AKMQMNAJJBL73LXWPXDI4I5ZWWIZ4AWO34DWQ636QOBBXNFXH3LQS@main.trondisco.net", + ] + publish = false + dnsDomain = "" + dnsPrivate = "" + knownUrls = [] + staticNodes = [] + maxMergeSize = 5 + changeThreshold = 0.1 + 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 = true + 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 + 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/args/BlockConfigTest.java b/common/src/test/java/org/tron/core/config/args/BlockConfigTest.java index 51d71b6ecda..14645242851 100644 --- a/common/src/test/java/org/tron/core/config/args/BlockConfigTest.java +++ b/common/src/test/java/org/tron/core/config/args/BlockConfigTest.java @@ -10,18 +10,24 @@ public class BlockConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); - BlockConfig bc = BlockConfig.fromConfig(empty); - assertFalse(bc.isNeedSyncCheck()); + BlockConfig bc = BlockConfig.fromConfig(withRef()); assertEquals(21600000L, bc.getMaintenanceTimeInterval()); assertEquals(1, bc.getCheckFrozenTime()); } @Test public void testFromConfig() { - Config config = ConfigFactory.parseString( + Config config = withRef( "block { needSyncCheck = true, maintenanceTimeInterval = 10000," + " checkFrozenTime = 5, proposalExpireTime = 300000 }"); BlockConfig bc = BlockConfig.fromConfig(config); @@ -33,22 +39,18 @@ public void testFromConfig() { @Test(expected = TronError.class) public void testProposalExpireTimeTooLow() { - // MIN_PROPOSAL_EXPIRE_TIME = 0, so value must be > 0 - Config config = ConfigFactory.parseString("block { proposalExpireTime = 0 }"); - BlockConfig.fromConfig(config); + BlockConfig.fromConfig(withRef("block { proposalExpireTime = 0 }")); } @Test(expected = TronError.class) public void testProposalExpireTimeTooHigh() { - Config config = ConfigFactory.parseString("block { proposalExpireTime = 999999999999 }"); - BlockConfig.fromConfig(config); + BlockConfig.fromConfig(withRef("block { proposalExpireTime = 999999999999 }")); } @Test(expected = TronError.class) public void testRejectsCommitteeProposalExpireTime() { - Config config = ConfigFactory.parseString( + BlockConfig.fromConfig(withRef( "committee { proposalExpireTime = 300000 }\n" - + "block { proposalExpireTime = 300000 }"); - BlockConfig.fromConfig(config); + + "block { proposalExpireTime = 300000 }")); } } diff --git a/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java b/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java index e13f4b5bc64..0f0270454b7 100644 --- a/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java +++ b/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java @@ -8,10 +8,17 @@ public class CommitteeConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); - CommitteeConfig cc = CommitteeConfig.fromConfig(empty); + CommitteeConfig cc = CommitteeConfig.fromConfig(withRef()); assertEquals(0, cc.getAllowCreationOfContracts()); assertEquals(0, cc.getAllowPBFT()); assertEquals(20, cc.getPBFTExpireNum()); @@ -21,7 +28,7 @@ public void testDefaults() { @Test public void testFromConfig() { - Config config = ConfigFactory.parseString( + Config config = withRef( "committee { allowCreationOfContracts = 1, allowPBFT = 1, pBFTExpireNum = 30 }"); CommitteeConfig cc = CommitteeConfig.fromConfig(config); assertEquals(1, cc.getAllowCreationOfContracts()); @@ -31,39 +38,34 @@ public void testFromConfig() { @Test public void testUnfreezeDelayDaysClamped() { - Config tooHigh = ConfigFactory.parseString("committee { unfreezeDelayDays = 500 }"); - assertEquals(365, CommitteeConfig.fromConfig(tooHigh).getUnfreezeDelayDays()); - - Config tooLow = ConfigFactory.parseString("committee { unfreezeDelayDays = -10 }"); - assertEquals(0, CommitteeConfig.fromConfig(tooLow).getUnfreezeDelayDays()); + assertEquals(365, CommitteeConfig.fromConfig( + withRef("committee { unfreezeDelayDays = 500 }")).getUnfreezeDelayDays()); + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { unfreezeDelayDays = -10 }")).getUnfreezeDelayDays()); } @Test public void testDynamicEnergyClamped() { - Config config = ConfigFactory.parseString("committee { allowDynamicEnergy = 5 }"); - assertEquals(1, CommitteeConfig.fromConfig(config).getAllowDynamicEnergy()); + assertEquals(1, CommitteeConfig.fromConfig( + withRef("committee { allowDynamicEnergy = 5 }")).getAllowDynamicEnergy()); } @Test public void testDynamicEnergyThresholdClamped() { - Config config = ConfigFactory.parseString( - "committee { dynamicEnergyThreshold = 999999999999999999 }"); - assertEquals(100_000_000_000_000_000L, - CommitteeConfig.fromConfig(config).getDynamicEnergyThreshold()); + assertEquals(100_000_000_000_000_000L, CommitteeConfig.fromConfig( + withRef("committee { dynamicEnergyThreshold = 999999999999999999 }")) + .getDynamicEnergyThreshold()); } @Test(expected = IllegalArgumentException.class) public void testAllowOldRewardOptWithoutPrerequisites() { - Config config = ConfigFactory.parseString( - "committee { allowOldRewardOpt = 1 }"); - CommitteeConfig.fromConfig(config); + CommitteeConfig.fromConfig(withRef("committee { allowOldRewardOpt = 1 }")); } @Test public void testAllowOldRewardOptWithPrerequisite() { - Config config = ConfigFactory.parseString( - "committee { allowOldRewardOpt = 1, allowTvmVote = 1 }"); - CommitteeConfig cc = CommitteeConfig.fromConfig(config); + CommitteeConfig cc = CommitteeConfig.fromConfig( + withRef("committee { allowOldRewardOpt = 1, allowTvmVote = 1 }")); assertEquals(1, cc.getAllowOldRewardOpt()); } } 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 f228ac8d479..361d9f48581 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 @@ -10,19 +10,28 @@ public class EventConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); + Config empty = withRef(); EventConfig ec = EventConfig.fromConfig(empty); + // reference.conf has event.subscribe with enable=false, topics with 7 entries assertFalse(ec.isEnable()); assertEquals(0, ec.getVersion()); assertEquals("", ec.getPath()); - assertTrue(ec.getTopics().isEmpty()); + assertFalse(ec.getTopics().isEmpty()); // reference.conf has default topic entries } @Test public void testNativeQueue() { - Config config = ConfigFactory.parseString( + Config config = withRef( "event.subscribe { enable = true," + " native { useNativeQueue = true, bindport = 6666, sendqueuelength = 2000 } }"); EventConfig ec = EventConfig.fromConfig(config); @@ -34,7 +43,7 @@ public void testNativeQueue() { @Test public void testTopicsWithOptionalFields() { - Config config = ConfigFactory.parseString( + Config config = withRef( "event.subscribe { enable = true, topics = [" + "{ triggerName = block, enable = true, topic = block }," + "{ triggerName = transaction, enable = false, topic = tx," @@ -59,7 +68,7 @@ public void testTopicsWithOptionalFields() { @Test public void testFilter() { - Config config = ConfigFactory.parseString( + Config config = withRef( "event.subscribe { enable = true," + " filter { fromblock = \"100\", toblock = \"200\"," + " contractAddress = [\"addr1\", \"addr2\"]," 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 b03ef885657..5e653a79b7f 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 @@ -1,6 +1,7 @@ package org.tron.core.config.args; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import com.typesafe.config.Config; @@ -9,19 +10,27 @@ public class GenesisConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); + Config empty = withRef(); GenesisConfig gc = GenesisConfig.fromConfig(empty); - assertEquals("", gc.getTimestamp()); - assertEquals("", gc.getParentHash()); - assertTrue(gc.getAssets().isEmpty()); - assertTrue(gc.getWitnesses().isEmpty()); + // 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 } @Test public void testWithAssets() { - Config config = ConfigFactory.parseString( + Config config = withRef( "genesis.block { timestamp = \"12345\", parentHash = \"0x00\"," + " assets = [{ accountName = Zion, accountType = AssetIssue," + " address = \"TAddr1\", balance = \"99000\" }]," @@ -40,7 +49,7 @@ public void testWithAssets() { @Test public void testEmptyLists() { - Config config = ConfigFactory.parseString( + Config config = withRef( "genesis.block { timestamp = \"0\", parentHash = \"0x00\"," + " assets = [], witnesses = [] }"); GenesisConfig gc = GenesisConfig.fromConfig(config); diff --git a/common/src/test/java/org/tron/core/config/args/LocalWitnessConfigTest.java b/common/src/test/java/org/tron/core/config/args/LocalWitnessConfigTest.java index 8042f82592e..0c163ef31f7 100644 --- a/common/src/test/java/org/tron/core/config/args/LocalWitnessConfigTest.java +++ b/common/src/test/java/org/tron/core/config/args/LocalWitnessConfigTest.java @@ -10,9 +10,17 @@ public class LocalWitnessConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); + Config empty = withRef(); LocalWitnessConfig lw = LocalWitnessConfig.fromConfig(empty); assertTrue(lw.getPrivateKeys().isEmpty()); assertNull(lw.getAccountAddress()); @@ -21,7 +29,7 @@ public void testDefaults() { @Test public void testWithPrivateKeys() { - Config config = ConfigFactory.parseString( + Config config = withRef( "localwitness = [\"key1\", \"key2\"]\n" + "localWitnessAccountAddress = \"TAddr123\""); LocalWitnessConfig lw = LocalWitnessConfig.fromConfig(config); @@ -32,7 +40,7 @@ public void testWithPrivateKeys() { @Test public void testWithKeystores() { - Config config = ConfigFactory.parseString( + Config config = withRef( "localwitnesskeystore = [\"/path/to/keystore1\"]"); LocalWitnessConfig lw = LocalWitnessConfig.fromConfig(config); assertEquals(1, lw.getKeystores().size()); diff --git a/common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java b/common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java index bb9a5e97f4e..b641e4d1924 100644 --- a/common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java +++ b/common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java @@ -10,9 +10,17 @@ public class MetricsConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); + Config empty = withRef(); MetricsConfig mc = MetricsConfig.fromConfig(empty); assertFalse(mc.isStorageEnable()); assertFalse(mc.getPrometheus().isEnable()); @@ -22,7 +30,7 @@ public void testDefaults() { @Test public void testFromConfig() { - Config config = ConfigFactory.parseString( + Config config = withRef( "node.metrics {" + " storageEnable = true," + " prometheus { enable = true, port = 9999 }," 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 31e5f499906..ed369d6c35f 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 @@ -11,23 +11,33 @@ public class MiscConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); + Config empty = withRef(); MiscConfig mc = MiscConfig.fromConfig(empty); assertTrue(mc.isNeedToUpdateAsset()); assertFalse(mc.isHistoryBalanceLookup()); assertEquals("solid", mc.getTrxReferenceBlock()); assertEquals(Constant.TRANSACTION_DEFAULT_EXPIRATION_TIME, mc.getTrxExpirationTimeInMilliseconds()); - assertEquals(Constant.ECKey_ENGINE, mc.getCryptoEngine()); - assertTrue(mc.getSeedNodeIpList().isEmpty()); + // 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()); assertTrue(mc.getActuatorWhitelist().isEmpty()); } @Test public void testFromConfig() { - Config config = ConfigFactory.parseString( + Config config = withRef( "storage { needToUpdateAsset = false," + " balance { history { lookup = true } } }\n" + "trx { reference { block = head } }\n" 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 df63f552c33..9a633b7d63a 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 @@ -10,9 +10,17 @@ public class NodeConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); + Config empty = withRef(); NodeConfig nc = NodeConfig.fromConfig(empty); assertEquals(18888, nc.getListenPort()); assertEquals(2, nc.getConnectionTimeout()); @@ -21,14 +29,15 @@ public void testDefaults() { assertEquals(8, nc.getMinConnections()); assertEquals(4, nc.getMaxFastForwardNum()); assertFalse(nc.isOpenFullTcpDisconnect()); - assertFalse(nc.isDiscoveryEnable()); - assertFalse(nc.isDiscoveryPersist()); + // reference.conf has node.discovery.enable=true, persist=true + assertTrue(nc.isDiscoveryEnable()); + assertTrue(nc.isDiscoveryPersist()); assertEquals(0, nc.getChannelReadTimeout()); } @Test public void testDotNotationFields() { - Config config = ConfigFactory.parseString( + Config config = withRef( "node { listen { port = 19999 }, connection { timeout = 5 }," + " fetchBlock { timeout = 300 }, solidity { threads = 4 } }"); NodeConfig nc = NodeConfig.fromConfig(config); @@ -40,7 +49,7 @@ public void testDotNotationFields() { @Test public void testDiscoveryFields() { - Config config = ConfigFactory.parseString( + Config config = withRef( "node.discovery { enable = true, persist = true }"); NodeConfig nc = NodeConfig.fromConfig(config); assertTrue(nc.isDiscoveryEnable()); @@ -49,7 +58,7 @@ public void testDiscoveryFields() { @Test public void testHttpSubBean() { - Config config = ConfigFactory.parseString( + Config config = withRef( "node { http { fullNodeEnable = false, fullNodePort = 9090," + " PBFTEnable = false, PBFTPort = 9092 } }"); NodeConfig nc = NodeConfig.fromConfig(config); @@ -61,7 +70,7 @@ public void testHttpSubBean() { @Test public void testRpcSubBean() { - Config config = ConfigFactory.parseString( + Config config = withRef( "node { rpc { enable = false, port = 60051," + " PBFTEnable = false, PBFTPort = 60071 } }"); NodeConfig nc = NodeConfig.fromConfig(config); @@ -73,7 +82,7 @@ public void testRpcSubBean() { @Test public void testBackupSubBean() { - Config config = ConfigFactory.parseString( + Config config = withRef( "node { backup { priority = 5, port = 20001, keepAliveInterval = 5000 } }"); NodeConfig nc = NodeConfig.fromConfig(config); assertEquals(5, nc.getBackup().getPriority()); @@ -83,9 +92,53 @@ public void testBackupSubBean() { @Test public void testIsOpenFullTcpDisconnect() { - Config config = ConfigFactory.parseString( + Config config = withRef( "node { isOpenFullTcpDisconnect = true }"); NodeConfig nc = NodeConfig.fromConfig(config); assertTrue(nc.isOpenFullTcpDisconnect()); } + + @Test + public void testRpcDefaultsFromReference() { + Config empty = withRef(); + NodeConfig nc = NodeConfig.fromConfig(empty); + NodeConfig.RpcConfig rpc = nc.getRpc(); + + // reference.conf provides actual final defaults, no sentinel conversion needed + assertEquals(2147483647, rpc.getMaxConcurrentCallsPerConnection()); + assertEquals(1048576, rpc.getFlowControlWindow()); + assertEquals(9223372036854775807L, rpc.getMaxConnectionIdleInMillis()); + assertEquals(9223372036854775807L, rpc.getMaxConnectionAgeInMillis()); + assertEquals(4194304, rpc.getMaxMessageSize()); + assertEquals(8192, rpc.getMaxHeaderListSize()); + assertEquals(1, rpc.getMinEffectiveConnection()); + // thread=0 in reference.conf triggers auto-detect in postProcess + assertTrue(rpc.getThread() > 0); + } + + @Test + public void testRpcUserOverrideZeroNotConverted() { + // Users can explicitly set 0 to disable connection checks (e.g. system-test) + Config config = withRef( + "node { rpc { minEffectiveConnection = 0 } }"); + NodeConfig nc = NodeConfig.fromConfig(config); + assertEquals(0, nc.getRpc().getMinEffectiveConnection()); + } + + @Test + public void testRpcUserOverrideExplicitValues() { + Config config = withRef( + "node { rpc { thread = 32," + + " maxConcurrentCallsPerConnection = 50," + + " flowControlWindow = 2097152," + + " maxMessageSize = 8388608," + + " maxHeaderListSize = 16384 } }"); + NodeConfig nc = NodeConfig.fromConfig(config); + NodeConfig.RpcConfig rpc = nc.getRpc(); + assertEquals(32, rpc.getThread()); + assertEquals(50, rpc.getMaxConcurrentCallsPerConnection()); + assertEquals(2097152, rpc.getFlowControlWindow()); + assertEquals(8388608, rpc.getMaxMessageSize()); + assertEquals(16384, rpc.getMaxHeaderListSize()); + } } diff --git a/common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java b/common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java index 3e71589f3e8..7b4d8a87d45 100644 --- a/common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java +++ b/common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java @@ -9,9 +9,17 @@ public class RateLimiterConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); + Config empty = withRef(); RateLimiterConfig rl = RateLimiterConfig.fromConfig(empty); assertEquals(50000, rl.getGlobal().getQps()); assertEquals(10000, rl.getGlobal().getIp().getQps()); @@ -25,7 +33,7 @@ public void testDefaults() { @Test public void testFromConfig() { - Config config = ConfigFactory.parseString( + Config config = withRef( "rate.limiter {" + " global { qps = 100, ip { qps = 50 }, api { qps = 10 } }," + " p2p { syncBlockChain = 5.0, disconnect = 2.0 }," diff --git a/common/src/test/java/org/tron/core/config/args/StorageConfigTest.java b/common/src/test/java/org/tron/core/config/args/StorageConfigTest.java index 8751a93c4be..9301d673a72 100644 --- a/common/src/test/java/org/tron/core/config/args/StorageConfigTest.java +++ b/common/src/test/java/org/tron/core/config/args/StorageConfigTest.java @@ -10,9 +10,17 @@ public class StorageConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); + Config empty = withRef(); StorageConfig sc = StorageConfig.fromConfig(empty); assertEquals("LEVELDB", sc.getDb().getEngine()); assertFalse(sc.getDb().isSync()); @@ -27,7 +35,7 @@ public void testDefaults() { @Test public void testFromConfig() { - Config config = ConfigFactory.parseString( + Config config = withRef( "storage { db { engine = ROCKSDB, sync = true, directory = mydb }," + " backup { enable = true, frequency = 5000 }," + " dbSettings { levelNumber = 5, maxOpenFiles = 3000 } }"); @@ -43,15 +51,31 @@ public void testFromConfig() { @Test public void testCheckpointDefaults() { - Config empty = ConfigFactory.empty(); + Config empty = withRef(); StorageConfig sc = StorageConfig.fromConfig(empty); assertEquals(1, sc.getCheckpoint().getVersion()); assertTrue(sc.getCheckpoint().isSync()); } + @Test + public void testDbSettingsDefaults() { + Config empty = withRef(); + StorageConfig sc = StorageConfig.fromConfig(empty); + StorageConfig.DbSettingsConfig ds = sc.getDbSettings(); + assertEquals(7, ds.getLevelNumber()); + assertEquals(32, ds.getCompactThreads()); + assertEquals(64, ds.getBlocksize()); + assertEquals(256, ds.getMaxBytesForLevelBase()); + assertEquals(10, ds.getMaxBytesForLevelMultiplier(), 0.01); + assertEquals(4, ds.getLevel0FileNumCompactionTrigger()); + assertEquals(256, ds.getTargetFileSizeBase()); + assertEquals(1, ds.getTargetFileSizeMultiplier()); + assertEquals(5000, ds.getMaxOpenFiles()); + } + @Test public void testBalanceHistoryLookup() { - Config config = ConfigFactory.parseString( + Config config = withRef( "storage { balance { history { lookup = true } } }"); StorageConfig sc = StorageConfig.fromConfig(config); assertTrue(sc.getBalance().getHistory().isLookup()); diff --git a/common/src/test/java/org/tron/core/config/args/VmConfigTest.java b/common/src/test/java/org/tron/core/config/args/VmConfigTest.java index 42d39e023cf..0e7c5438580 100644 --- a/common/src/test/java/org/tron/core/config/args/VmConfigTest.java +++ b/common/src/test/java/org/tron/core/config/args/VmConfigTest.java @@ -10,9 +10,17 @@ public class VmConfigTest { + private static Config withRef(String hocon) { + return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); + } + + private static Config withRef() { + return ConfigFactory.defaultReference(); + } + @Test public void testDefaults() { - Config empty = ConfigFactory.empty(); + Config empty = withRef(); VmConfig vm = VmConfig.fromConfig(empty); assertFalse(vm.isSupportConstant()); assertEquals(100_000_000L, vm.getMaxEnergyLimitForConstant()); @@ -30,7 +38,7 @@ public void testDefaults() { @Test public void testFromConfig() { - Config config = ConfigFactory.parseString( + Config config = withRef( "vm { supportConstant = true, lruCacheSize = 1000, minTimeRatio = 0.5 }"); VmConfig vm = VmConfig.fromConfig(config); assertTrue(vm.isSupportConstant()); @@ -40,23 +48,23 @@ public void testFromConfig() { @Test public void testMaxEnergyLimitClamped() { - Config config = ConfigFactory.parseString("vm { maxEnergyLimitForConstant = 100 }"); + Config config = withRef("vm { maxEnergyLimitForConstant = 100 }"); VmConfig vm = VmConfig.fromConfig(config); assertEquals(3_000_000L, vm.getMaxEnergyLimitForConstant()); } @Test public void testEstimateEnergyMaxRetryClamped() { - Config tooHigh = ConfigFactory.parseString("vm { estimateEnergyMaxRetry = 50 }"); + Config tooHigh = withRef("vm { estimateEnergyMaxRetry = 50 }"); assertEquals(10, VmConfig.fromConfig(tooHigh).getEstimateEnergyMaxRetry()); - Config tooLow = ConfigFactory.parseString("vm { estimateEnergyMaxRetry = -5 }"); + Config tooLow = withRef("vm { estimateEnergyMaxRetry = -5 }"); assertEquals(0, VmConfig.fromConfig(tooLow).getEstimateEnergyMaxRetry()); } @Test public void testPartialConfig() { - Config config = ConfigFactory.parseString("vm { saveInternalTx = true }"); + Config config = withRef("vm { saveInternalTx = true }"); VmConfig vm = VmConfig.fromConfig(config); assertTrue(vm.isSaveInternalTx()); assertFalse(vm.isSupportConstant()); // default 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 66d49467d9b..86d9475ce28 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 @@ -121,6 +121,29 @@ public class Args extends CommonParameter { @Getter private static String configFilePath = ""; + // Singleton config beans — populated at startup, read-only after init. + // New code can read directly from these beans instead of CommonParameter. + @Getter + private static NodeConfig nodeConfig; + @Getter + private static VmConfig vmConfig; + @Getter + private static BlockConfig blockConfig; + @Getter + private static CommitteeConfig committeeConfig; + @Getter + private static StorageConfig storageConfig; + @Getter + private static GenesisConfig genesisConfig; + @Getter + private static MiscConfig miscConfig; + @Getter + private static RateLimiterConfig rateLimiterConfig; + @Getter + private static MetricsConfig metricsConfig; + @Getter + private static EventConfig eventConfig; + @Getter @Setter private static LocalWitnesses localWitnesses = new LocalWitnesses(); @@ -543,36 +566,16 @@ private static void applyNodeConfig(NodeConfig nc) { PARAMETER.rpcOnSolidityPort = rpc.getSolidityPort(); PARAMETER.rpcOnPBFTPort = rpc.getPBFTPort(); PARAMETER.rpcThreadNum = rpc.getThread(); - if (PARAMETER.rpcThreadNum == 16) { - // default from bean — apply dynamic default: (CPUs + 1) / 2 - PARAMETER.rpcThreadNum = (Runtime.getRuntime().availableProcessors() + 1) / 2; - } PARAMETER.maxConcurrentCallsPerConnection = rpc.getMaxConcurrentCallsPerConnection(); - if (PARAMETER.maxConcurrentCallsPerConnection == 100) { - // bean default 100 — original code used Integer.MAX_VALUE as default - PARAMETER.maxConcurrentCallsPerConnection = Integer.MAX_VALUE; - } PARAMETER.flowControlWindow = rpc.getFlowControlWindow(); - if (PARAMETER.flowControlWindow == 0) { - PARAMETER.flowControlWindow = NettyServerBuilder.DEFAULT_FLOW_CONTROL_WINDOW; - } PARAMETER.rpcMaxRstStream = rpc.getMaxRstStream(); PARAMETER.rpcSecondsPerWindow = rpc.getSecondsPerWindow(); PARAMETER.maxConnectionIdleInMillis = rpc.getMaxConnectionIdleInMillis(); PARAMETER.maxConnectionAgeInMillis = rpc.getMaxConnectionAgeInMillis(); PARAMETER.maxMessageSize = rpc.getMaxMessageSize(); - if (PARAMETER.maxMessageSize == 0) { - PARAMETER.maxMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE; - } PARAMETER.maxHeaderListSize = rpc.getMaxHeaderListSize(); - if (PARAMETER.maxHeaderListSize == 0) { - PARAMETER.maxHeaderListSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE; - } PARAMETER.isRpcReflectionServiceEnable = rpc.isReflectionService(); PARAMETER.minEffectiveConnection = rpc.getMinEffectiveConnection(); - if (PARAMETER.minEffectiveConnection == 0) { - PARAMETER.minEffectiveConnection = 1; - } PARAMETER.trxCacheEnable = rpc.isTrxCacheEnable(); // ---- HTTP sub-bean ---- @@ -606,9 +609,6 @@ private static void applyNodeConfig(NodeConfig nc) { // ---- Dynamic config sub-bean ---- PARAMETER.dynamicConfigEnable = nc.getDynamicConfig().isEnable(); PARAMETER.dynamicConfigCheckInterval = nc.getDynamicConfig().getCheckInterval(); - if (PARAMETER.dynamicConfigCheckInterval <= 0) { - PARAMETER.dynamicConfigCheckInterval = 600; - } // ---- Flat scalar fields ---- PARAMETER.nodeEffectiveCheckEnable = nc.isEffectiveCheckEnable(); @@ -633,22 +633,8 @@ private static void applyNodeConfig(NodeConfig nc) { PARAMETER.nodeEnableIpv6 = nc.isEnableIpv6(); PARAMETER.syncFetchBatchNum = nc.getSyncFetchBatchNum(); - if (PARAMETER.syncFetchBatchNum > 2000) { - PARAMETER.syncFetchBatchNum = 2000; - } - if (PARAMETER.syncFetchBatchNum < 100) { - PARAMETER.syncFetchBatchNum = 100; - } - PARAMETER.solidityThreads = nc.getSolidityThreads(); - PARAMETER.blockProducedTimeOut = nc.getBlockProducedTimeOut(); - if (PARAMETER.blockProducedTimeOut < 30) { - PARAMETER.blockProducedTimeOut = 30; - } - if (PARAMETER.blockProducedTimeOut > 100) { - PARAMETER.blockProducedTimeOut = 100; - } PARAMETER.maxHttpConnectNumber = nc.getMaxHttpConnectNumber(); PARAMETER.netMaxTrxPerSecond = nc.getNetMaxTrxPerSecond(); @@ -656,7 +642,8 @@ private static void applyNodeConfig(NodeConfig nc) { PARAMETER.udpNettyWorkThreadNum = nc.getUdpNettyWorkThreadNum(); if (StringUtils.isEmpty(PARAMETER.trustNodeAddr)) { - PARAMETER.trustNodeAddr = nc.getTrustNode(); + String trustNode = nc.getTrustNode(); + PARAMETER.trustNodeAddr = StringUtils.isEmpty(trustNode) ? null : trustNode; } PARAMETER.validateSignThreadNum = nc.getValidateSignThreadNum(); @@ -666,9 +653,6 @@ private static void applyNodeConfig(NodeConfig nc) { PARAMETER.nodeDetectEnable = nc.isNodeDetectEnable(); PARAMETER.inactiveThreshold = nc.getInactiveThreshold(); - if (PARAMETER.inactiveThreshold < 1) { - PARAMETER.inactiveThreshold = 1; - } PARAMETER.maxTransactionPendingSize = nc.getMaxTransactionPendingSize(); PARAMETER.pendingTransactionTimeout = nc.getPendingTransactionTimeout(); @@ -676,21 +660,8 @@ private static void applyNodeConfig(NodeConfig nc) { PARAMETER.validContractProtoThreadNum = nc.getValidContractProtoThreads(); PARAMETER.maxFastForwardNum = nc.getMaxFastForwardNum(); - if (PARAMETER.maxFastForwardNum > MAX_ACTIVE_WITNESS_NUM) { - PARAMETER.maxFastForwardNum = MAX_ACTIVE_WITNESS_NUM; - } - if (PARAMETER.maxFastForwardNum < 1) { - PARAMETER.maxFastForwardNum = 1; - } - PARAMETER.shieldedTransInPendingMaxCounts = nc.getShieldedTransInPendingMaxCounts(); - PARAMETER.agreeNodeCount = nc.getAgreeNodeCount(); - if (PARAMETER.agreeNodeCount == 0) { - PARAMETER.agreeNodeCount = MAX_ACTIVE_WITNESS_NUM * 2 / 3 + 1; - } - PARAMETER.agreeNodeCount = PARAMETER.agreeNodeCount > MAX_ACTIVE_WITNESS_NUM - ? MAX_ACTIVE_WITNESS_NUM : PARAMETER.agreeNodeCount; PARAMETER.setOpenHistoryQueryWhenLiteFN(nc.isOpenHistoryQueryWhenLiteFN()); PARAMETER.nodeMetricsEnable = nc.isMetricsEnable(); @@ -780,48 +751,48 @@ public static void applyConfigParams( // crypto.engine handled by MiscConfig // VM config: bind from config.conf "vm" section - VmConfig vmConfig = VmConfig.fromConfig(config); + vmConfig = VmConfig.fromConfig(config); applyVmConfig(vmConfig); // Node config: bind from config.conf "node" section - NodeConfig nodeConfig = NodeConfig.fromConfig(config); + nodeConfig = NodeConfig.fromConfig(config); applyNodeConfig(nodeConfig); // vm.minTimeRatio, vm.maxTimeRatio, vm.longRunningTime already handled by VmConfig above // Storage config: bind from config.conf "storage" section PARAMETER.storage = new Storage(); - StorageConfig storageConfig = StorageConfig.fromConfig(config); + storageConfig = StorageConfig.fromConfig(config); applyStorageConfig(storageConfig); // seed.node is a top-level config section (not under "node") — config structure // is arguably misplaced, but preserved for backward compatibility // Genesis config: bind from config.conf "genesis.block" section - GenesisConfig genesisConfig = GenesisConfig.fromConfig(config); + genesisConfig = GenesisConfig.fromConfig(config); applyGenesisConfig(genesisConfig, config); // Block config: bind from config.conf "block" section - BlockConfig blockConfig = BlockConfig.fromConfig(config); + blockConfig = BlockConfig.fromConfig(config); applyBlockConfig(blockConfig); // node discovery, legacy fallback, p2p, dns — all handled in applyNodeConfig // Misc config: storage, trx, energy — small domains, read via beans - MiscConfig miscConfig = MiscConfig.fromConfig(config); + miscConfig = MiscConfig.fromConfig(config); applyMiscConfig(miscConfig); // vm, committee already handled above // Committee config: bind from config.conf "committee" section - CommitteeConfig committeeConfig = CommitteeConfig.fromConfig(config); + committeeConfig = CommitteeConfig.fromConfig(config); applyCommitteeConfig(committeeConfig); // shielded transaction API, active/passive/fastForward — handled in applyNodeConfig // Rate limiter config: bind from config.conf "rate.limiter" section - RateLimiterConfig rlConfig = RateLimiterConfig.fromConfig(config); - applyRateLimiterConfig(rlConfig, config); + rateLimiterConfig = RateLimiterConfig.fromConfig(config); + applyRateLimiterConfig(rateLimiterConfig, config); // Node backup: from NodeConfig bean applyNodeBackupConfig(nodeConfig); @@ -829,7 +800,7 @@ public static void applyConfigParams( // actuatorSet already set in applyMiscConfig // Metrics config: bind from config.conf "node.metrics" section - MetricsConfig metricsConfig = MetricsConfig.fromConfig(config); + metricsConfig = MetricsConfig.fromConfig(config); applyMetricsConfig(metricsConfig); // historyBalanceLookup already handled by MiscConfig above @@ -837,7 +808,7 @@ public static void applyConfigParams( // node.shutdown — handled in applyNodeConfig // Event config: bind from config.conf "event.subscribe" section - EventConfig eventConfig = EventConfig.fromConfig(config); + eventConfig = EventConfig.fromConfig(config); applyEventConfig(eventConfig); PARAMETER.allowTvmOsaka = @@ -1027,6 +998,16 @@ public static void clearParam() { CommonParameter.reset(); configFilePath = ""; localWitnesses = null; + nodeConfig = null; + vmConfig = null; + blockConfig = null; + committeeConfig = null; + storageConfig = null; + genesisConfig = null; + miscConfig = null; + rateLimiterConfig = null; + metricsConfig = null; + eventConfig = null; } // getProposalExpirationTime removed — logic moved to BlockConfig.fromConfig() 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 fdb4aa7f0e8..a69a483cf3b 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,9 @@ public void testInitService() { Map storage = new HashMap<>(); // avoid the exception for the missing storage storage.put("storage.db.directory", "database"); - Config config = ConfigFactory.defaultOverrides().withFallback(ConfigFactory.parseMap(storage)); + Config config = ConfigFactory.defaultOverrides() + .withFallback(ConfigFactory.parseMap(storage)) + .withFallback(ConfigFactory.defaultReference()); // test default value Args.applyConfigParams(config); Assert.assertTrue(Args.getInstance().isRpcEnable()); @@ -193,7 +195,9 @@ public void testInitService() { storage.put("node.jsonrpc.httpPBFTEnable", "true"); storage.put("node.jsonrpc.maxBlockRange", "10"); storage.put("node.jsonrpc.maxSubTopics", "20"); - config = ConfigFactory.defaultOverrides().withFallback(ConfigFactory.parseMap(storage)); + config = ConfigFactory.defaultOverrides() + .withFallback(ConfigFactory.parseMap(storage)) + .withFallback(ConfigFactory.defaultReference()); // test value Args.applyConfigParams(config); Assert.assertTrue(Args.getInstance().isRpcEnable()); @@ -220,7 +224,9 @@ public void testInitService() { storage.put("node.jsonrpc.httpPBFTEnable", "false"); storage.put("node.jsonrpc.maxBlockRange", "5000"); storage.put("node.jsonrpc.maxSubTopics", "1000"); - config = ConfigFactory.defaultOverrides().withFallback(ConfigFactory.parseMap(storage)); + config = ConfigFactory.defaultOverrides() + .withFallback(ConfigFactory.parseMap(storage)) + .withFallback(ConfigFactory.defaultReference()); // test value Args.applyConfigParams(config); Assert.assertFalse(Args.getInstance().isRpcEnable()); @@ -247,7 +253,9 @@ public void testInitService() { storage.put("node.jsonrpc.httpPBFTEnable", "true"); storage.put("node.jsonrpc.maxBlockRange", "30"); storage.put("node.jsonrpc.maxSubTopics", "40"); - config = ConfigFactory.defaultOverrides().withFallback(ConfigFactory.parseMap(storage)); + config = ConfigFactory.defaultOverrides() + .withFallback(ConfigFactory.parseMap(storage)) + .withFallback(ConfigFactory.defaultReference()); // test value Args.applyConfigParams(config); Assert.assertFalse(Args.getInstance().isRpcEnable()); @@ -265,7 +273,9 @@ public void testInitService() { // test set invalid value storage.put("node.jsonrpc.maxBlockRange", "0"); storage.put("node.jsonrpc.maxSubTopics", "0"); - config = ConfigFactory.defaultOverrides().withFallback(ConfigFactory.parseMap(storage)); + config = ConfigFactory.defaultOverrides() + .withFallback(ConfigFactory.parseMap(storage)) + .withFallback(ConfigFactory.defaultReference()); // check value Args.applyConfigParams(config); Assert.assertEquals(0, Args.getInstance().getJsonRpcMaxBlockRange()); @@ -274,7 +284,9 @@ public void testInitService() { // test set invalid value storage.put("node.jsonrpc.maxBlockRange", "-2"); storage.put("node.jsonrpc.maxSubTopics", "-4"); - config = ConfigFactory.defaultOverrides().withFallback(ConfigFactory.parseMap(storage)); + config = ConfigFactory.defaultOverrides() + .withFallback(ConfigFactory.parseMap(storage)) + .withFallback(ConfigFactory.defaultReference()); // check value Args.applyConfigParams(config); Assert.assertEquals(-2, Args.getInstance().getJsonRpcMaxBlockRange()); diff --git a/framework/src/test/java/org/tron/core/exception/TronErrorTest.java b/framework/src/test/java/org/tron/core/exception/TronErrorTest.java index e965ae3fd60..344b6dd946d 100644 --- a/framework/src/test/java/org/tron/core/exception/TronErrorTest.java +++ b/framework/src/test/java/org/tron/core/exception/TronErrorTest.java @@ -139,8 +139,9 @@ public void shutdownBlockTimeInitTest() { Map params = new HashMap<>(); params.put("node.shutdown.BlockTime", "0"); params.put("storage.db.directory", "database"); - Config config = ConfigFactory.defaultOverrides().withFallback( - ConfigFactory.parseMap(params)); + Config config = ConfigFactory.defaultOverrides() + .withFallback(ConfigFactory.parseMap(params)) + .withFallback(ConfigFactory.defaultReference()); TronError thrown = assertThrows(TronError.class, () -> Args.applyConfigParams(config)); assertEquals(TronError.ErrCode.AUTO_STOP_PARAMS, thrown.getErrCode()); } diff --git a/framework/src/test/resources/config-localtest.conf b/framework/src/test/resources/config-localtest.conf index f1f40dead76..53a78d3e4c6 100644 --- a/framework/src/test/resources/config-localtest.conf +++ b/framework/src/test/resources/config-localtest.conf @@ -57,7 +57,7 @@ storage { node.discovery = { enable = true persist = true - external.ip = null + external.ip = "" } node.backup { diff --git a/framework/src/test/resources/config-test-dbbackup.conf b/framework/src/test/resources/config-test-dbbackup.conf index 44dd0164b2d..b660965f3e9 100644 --- a/framework/src/test/resources/config-test-dbbackup.conf +++ b/framework/src/test/resources/config-test-dbbackup.conf @@ -60,7 +60,7 @@ storage { node.discovery = { enable = true persist = true - external.ip = null + external.ip = "" } node.backup { diff --git a/framework/src/test/resources/config-test-index.conf b/framework/src/test/resources/config-test-index.conf index b41fdfe8505..72e4a04f612 100644 --- a/framework/src/test/resources/config-test-index.conf +++ b/framework/src/test/resources/config-test-index.conf @@ -54,7 +54,7 @@ storage { node.discovery = { enable = true persist = true - external.ip = null + external.ip = "" } node { From 1323c54499ae85643895f817a7d72098c7e5f210 Mon Sep 17 00:00:00 2001 From: vividctrlalt Date: Wed, 1 Apr 2026 07:03:56 +0000 Subject: [PATCH 11/14] refactor(config): move LevelDB option reading from Storage to StorageConfig Move default/defaultM/defaultL LevelDB option reading into StorageConfig, so Storage no longer touches Config directly. - Add DbOptionOverride with nullable boxed types for partial overrides - Fix cacheSize type from int to long to match LevelDB Options API - Remove dead externalIp(Config) bridge method - Remove setIfNeeded and Config field from Storage --- .../org/tron/core/config/args/Storage.java | 96 +++++++++---------- .../tron/core/config/args/StorageConfig.java | 89 ++++++++++++++++- .../java/org/tron/core/config/args/Args.java | 7 +- .../org/tron/core/config/args/ArgsTest.java | 8 +- 4 files changed, 135 insertions(+), 65 deletions(-) diff --git a/common/src/main/java/org/tron/core/config/args/Storage.java b/common/src/main/java/org/tron/core/config/args/Storage.java index 4fd23fa2305..4ed89373754 100644 --- a/common/src/main/java/org/tron/core/config/args/Storage.java +++ b/common/src/main/java/org/tron/core/config/args/Storage.java @@ -18,7 +18,6 @@ import com.google.common.collect.Maps; import com.google.protobuf.ByteString; import com.typesafe.config.Config; -import com.typesafe.config.ConfigObject; import java.io.File; import java.util.List; import java.util.Map; @@ -47,7 +46,11 @@ public class Storage { private static final String DEFAULT_INDEX_SWITCH = "on"; - private Config storage; + + // Optional per-tier LevelDB option overrides, read from StorageConfig bean + private StorageConfig.DbOptionOverride defaultDbOption; + private StorageConfig.DbOptionOverride defaultMDbOption; + private StorageConfig.DbOptionOverride defaultLDbOption; /** * Database storage directory: /path/to/{dbDirectory} @@ -271,42 +274,6 @@ private static void applyPropertyOptions(StorageConfig.PropertyConfig pc, Option dbOptions.maxOpenFiles(pc.getMaxOpenFiles()); } - // Keep old createProperty and setIfNeeded for setDefaultDbOptions which still - // uses ConfigObject for dynamic default/defaultM/defaultL overrides - private static void setIfNeeded(ConfigObject conf, Options dbOptions) { - if (conf.containsKey("createIfMissing")) { - dbOptions.createIfMissing( - Boolean.parseBoolean(conf.get("createIfMissing").unwrapped().toString())); - } - if (conf.containsKey("paranoidChecks")) { - dbOptions.paranoidChecks( - Boolean.parseBoolean(conf.get("paranoidChecks").unwrapped().toString())); - } - if (conf.containsKey("verifyChecksums")) { - dbOptions.verifyChecksums( - Boolean.parseBoolean(conf.get("verifyChecksums").unwrapped().toString())); - } - if (conf.containsKey("compressionType")) { - dbOptions.compressionType(CompressionType.getCompressionTypeByPersistentId( - Integer.parseInt(conf.get("compressionType").unwrapped().toString()))); - } - if (conf.containsKey("blockSize")) { - dbOptions.blockSize( - Integer.parseInt(conf.get("blockSize").unwrapped().toString())); - } - if (conf.containsKey("writeBufferSize")) { - dbOptions.writeBufferSize( - Integer.parseInt(conf.get("writeBufferSize").unwrapped().toString())); - } - if (conf.containsKey("cacheSize")) { - dbOptions.cacheSize( - Long.parseLong(conf.get("cacheSize").unwrapped().toString())); - } - if (conf.containsKey("maxOpenFiles")) { - dbOptions.maxOpenFiles( - Integer.parseInt(conf.get("maxOpenFiles").unwrapped().toString())); - } - } /** * Set propertyMap of Storage object from Config via StorageConfig bean. @@ -339,28 +306,59 @@ public void deleteAllStoragePaths() { } /** - * Accepts raw storage Config sub-tree because default/defaultM/defaultL are - * optional nested objects with dynamic LevelDB Option fields that - * ConfigBeanFactory cannot bind to fixed bean fields. + * Initialize default LevelDB options and store optional per-tier overrides + * from StorageConfig bean (no raw Config needed). */ - public void setDefaultDbOptions(final Config storageSection) { + public void setDefaultDbOptions(StorageConfig sc) { this.defaultDbOptions = DbOptionalsUtils.createDefaultDbOptions(); - storage = storageSection; + this.defaultDbOption = sc.getDefaultDbOption(); + this.defaultMDbOption = sc.getDefaultMDbOption(); + this.defaultLDbOption = sc.getDefaultLDbOption(); } public Options newDefaultDbOptions(String name) { Options options = DbOptionalsUtils.newDefaultDbOptions(name, this.defaultDbOptions); - if (storage.hasPath("default")) { - setIfNeeded(storage.getObject("default"), options); + if (defaultDbOption != null) { + applyDbOptionOverride(defaultDbOption, options); } - if (storage.hasPath("defaultM") && DbOptionalsUtils.DB_M.contains(name)) { - setIfNeeded(storage.getObject("defaultM"), options); + if (defaultMDbOption != null && DbOptionalsUtils.DB_M.contains(name)) { + applyDbOptionOverride(defaultMDbOption, options); } - if (storage.hasPath("defaultL") && DbOptionalsUtils.DB_L.contains(name)) { - setIfNeeded(storage.getObject("defaultL"), options); + if (defaultLDbOption != null && DbOptionalsUtils.DB_L.contains(name)) { + applyDbOptionOverride(defaultLDbOption, options); } return options; } + + // Apply only user-specified overrides (non-null fields) to LevelDB Options. + private static void applyDbOptionOverride( + StorageConfig.DbOptionOverride o, Options dbOptions) { + if (o.getCreateIfMissing() != null) { + dbOptions.createIfMissing(o.getCreateIfMissing()); + } + if (o.getParanoidChecks() != null) { + dbOptions.paranoidChecks(o.getParanoidChecks()); + } + if (o.getVerifyChecksums() != null) { + dbOptions.verifyChecksums(o.getVerifyChecksums()); + } + if (o.getCompressionType() != null) { + dbOptions.compressionType( + CompressionType.getCompressionTypeByPersistentId(o.getCompressionType())); + } + if (o.getBlockSize() != null) { + dbOptions.blockSize(o.getBlockSize()); + } + if (o.getWriteBufferSize() != null) { + dbOptions.writeBufferSize(o.getWriteBufferSize()); + } + if (o.getCacheSize() != null) { + dbOptions.cacheSize(o.getCacheSize()); + } + if (o.getMaxOpenFiles() != null) { + dbOptions.maxOpenFiles(o.getMaxOpenFiles()); + } + } } 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 5aecebe1059..4a766af1e1b 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 com.typesafe.config.ConfigObject; import java.util.ArrayList; import java.util.List; import lombok.Getter; @@ -36,9 +37,8 @@ public class StorageConfig { @Setter(lombok.AccessLevel.NONE) private Object merkleRoot; - // LevelDB per-database option overrides (default, defaultM, defaultL) - // These are not bean-bound because they are optional nested objects with - // dynamic usage. Read manually in fromConfig(). + // 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; @@ -47,6 +47,22 @@ 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 { @@ -158,7 +174,7 @@ public static class PropertyConfig { private int compressionType = 1; private int blockSize = 4096; private int writeBufferSize = 10485760; - private int cacheSize = 10485760; + private long cacheSize = 10485760; private int maxOpenFiles = 100; } @@ -168,8 +184,71 @@ public static StorageConfig fromConfig(Config config) { Config section = config.getConfig("storage"); StorageConfig sc = ConfigBeanFactory.create(section, StorageConfig.class); - // Keep raw config for legacy LevelDB per-database option overrides (default, defaultM, defaultL) sc.rawStorageConfig = section; + + // Read optional LevelDB option overrides (default, defaultM, defaultL). + sc.defaultDbOption = readDbOption(section, "default"); + sc.defaultMDbOption = readDbOption(section, "defaultM"); + sc.defaultLDbOption = readDbOption(section, "defaultL"); return sc; } + + // Partial LevelDB option override for default/defaultM/defaultL. + // Uses boxed types so null means "not set by user, keep existing value". + @Getter + @Setter + public static class DbOptionOverride { + private Boolean createIfMissing; + private Boolean paranoidChecks; + private Boolean verifyChecksums; + private Integer compressionType; + private Integer blockSize; + private Integer writeBufferSize; + private Long cacheSize; + private Integer maxOpenFiles; + } + + // Read optional LevelDB option override (default/defaultM/defaultL). + // Not bean-bound: users may only set a subset of keys (e.g. just maxOpenFiles), + // ConfigBeanFactory requires all fields present so partial overrides would fail. + private static DbOptionOverride readDbOption(Config section, String key) { + if (!section.hasPath(key)) { + return null; + } + ConfigObject conf = section.getObject(key); + DbOptionOverride o = new DbOptionOverride(); + if (conf.containsKey("createIfMissing")) { + o.setCreateIfMissing( + Boolean.parseBoolean(conf.get("createIfMissing").unwrapped().toString())); + } + if (conf.containsKey("paranoidChecks")) { + o.setParanoidChecks( + Boolean.parseBoolean(conf.get("paranoidChecks").unwrapped().toString())); + } + if (conf.containsKey("verifyChecksums")) { + o.setVerifyChecksums( + Boolean.parseBoolean(conf.get("verifyChecksums").unwrapped().toString())); + } + if (conf.containsKey("compressionType")) { + o.setCompressionType( + Integer.parseInt(conf.get("compressionType").unwrapped().toString())); + } + if (conf.containsKey("blockSize")) { + o.setBlockSize( + Integer.parseInt(conf.get("blockSize").unwrapped().toString())); + } + if (conf.containsKey("writeBufferSize")) { + o.setWriteBufferSize( + Integer.parseInt(conf.get("writeBufferSize").unwrapped().toString())); + } + if (conf.containsKey("cacheSize")) { + o.setCacheSize( + Long.parseLong(conf.get("cacheSize").unwrapped().toString())); + } + if (conf.containsKey("maxOpenFiles")) { + o.setMaxOpenFiles( + Integer.parseInt(conf.get("maxOpenFiles").unwrapped().toString())); + } + return o; + } } 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 86d9475ce28..0dc5aae4342 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 @@ -274,7 +274,7 @@ private static void applyStorageConfig(StorageConfig sc) { // Dynamic nested objects use StorageConfig's raw storage sub-tree // setDefaultDbOptions must be called before setPropertyMapFromBean because // createPropertyFromBean calls newDefaultDbOptions which needs defaultDbOptions initialized - PARAMETER.storage.setDefaultDbOptions(sc.getRawStorageConfig()); + PARAMETER.storage.setDefaultDbOptions(sc); PARAMETER.storage.setPropertyMapFromBean(sc.getProperties()); PARAMETER.storage.setCacheStrategies(sc.getRawStorageConfig()); PARAMETER.storage.setDbRoots(sc.getRawStorageConfig()); @@ -1157,11 +1157,6 @@ private static void logEmptyError(String arg) { // createTriggerConfig removed — logic moved to applyEventConfig() // getEventFilter removed — logic moved to applyEventConfig() - // Kept for backward compatibility — test code calls via reflection with Config param - private static void externalIp(final com.typesafe.config.Config config) { - externalIp(NodeConfig.fromConfig(config)); - } - private static void externalIp(NodeConfig nodeConfig) { String externalIp = nodeConfig.getDiscoveryExternalIp(); if (StringUtils.isEmpty(externalIp)) { 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 a69a483cf3b..24e560eb16b 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 @@ -143,14 +143,12 @@ public void testIpFromLibP2p() String configuredExternalIp = parameter.getNodeExternalIp(); Assert.assertEquals("46.168.1.1", configuredExternalIp); - Config config = Configuration.getByFileName(TestConstants.TEST_CONF); - Config config3 = config.withoutPath("node.discovery.external.ip"); - CommonParameter.getInstance().setNodeExternalIp(null); - Method method2 = Args.class.getDeclaredMethod("externalIp", Config.class); + NodeConfig nc = new NodeConfig(); + Method method2 = Args.class.getDeclaredMethod("externalIp", NodeConfig.class); method2.setAccessible(true); - method2.invoke(Args.class, config3); + method2.invoke(Args.class, nc); Assert.assertNotEquals(configuredExternalIp, parameter.getNodeExternalIp()); } From 4d8d9b9afe10f89771221c5b30b203031fb82d16 Mon Sep 17 00:00:00 2001 From: vividctrlalt Date: Wed, 1 Apr 2026 08:06:12 +0000 Subject: [PATCH 12/14] fix(config): handle null config values and SonarCloud issues - Replace null values (discovery.external.ip, trustNode) with empty string before ConfigBeanFactory binding for external config compat (system-test uses "external.ip = null" which ConfigBeanFactory cannot bind to String fields; recommend updating system-test to use "" instead) - Fix floating point comparison with Double.compare (java:S1244) - Extract duplicated string literals into constants/variables (java:S1192) --- .../tron/core/config/args/CommitteeConfig.java | 16 ++++++++++------ .../org/tron/core/config/args/EventConfig.java | 12 +++++++----- .../org/tron/core/config/args/MiscConfig.java | 7 ++++--- .../org/tron/core/config/args/NodeConfig.java | 17 +++++++++++++++++ common/src/main/resources/reference.conf | 1 + .../java/org/tron/core/config/args/Args.java | 7 ++----- 6 files changed, 41 insertions(+), 19 deletions(-) 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 804f4f70fb8..5c591afbd44 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 @@ -69,6 +69,7 @@ public class CommitteeConfig { private long consensusLogicOptimization = 0; private long allowTvmCancun = 0; private long allowTvmBlob = 0; + private long allowTvmOsaka = 0; private long unfreezeDelayDays = 0; private long allowReceiptsMerkleRoot = 0; private long allowAccountAssetOptimization = 0; @@ -92,22 +93,25 @@ public class CommitteeConfig { * 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 section = config.getConfig("committee"); // ConfigBeanFactory derives key names from setter methods. For setPBFTExpireNum() // it expects "PBFTExpireNum" (capital P), but config.conf uses "pBFTExpireNum". - // Similarly, getAllowPBFT() maps to "allowPBFT" which may be missing in test configs. - // Add uppercase aliases so ConfigBeanFactory can find them. + // Add uppercase alias so ConfigBeanFactory can find it. Config aliased = section; - if (section.hasPath("pBFTExpireNum") && !section.hasPath("PBFTExpireNum")) { - aliased = aliased.withValue("PBFTExpireNum", section.getValue("pBFTExpireNum")); + if (section.hasPath(PBFT_EXPIRE_NUM_KEY) && !section.hasPath("PBFTExpireNum")) { + aliased = aliased.withValue("PBFTExpireNum", section.getValue(PBFT_EXPIRE_NUM_KEY)); } CommitteeConfig cc = ConfigBeanFactory.create(aliased, CommitteeConfig.class); // Ensure the manually-named fields get the right values from the original keys - cc.allowPBFT = section.hasPath("allowPBFT") ? section.getLong("allowPBFT") : 0; - cc.pBFTExpireNum = section.hasPath("pBFTExpireNum") ? section.getLong("pBFTExpireNum") : 20; + 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 a1f8a81772f..ac1731de2dc 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 @@ -81,13 +81,15 @@ public static EventConfig fromConfig(Config config) { // "native" is a Java reserved word, "topics" has optional fields per item — // strip both before binding, read manually - Config bindable = section.withoutPath("native").withoutPath("topics") + String nativeKey = "native"; + String topicsKey = "topics"; + Config bindable = section.withoutPath(nativeKey).withoutPath(topicsKey) .withoutPath("topicDefaults"); EventConfig ec = ConfigBeanFactory.create(bindable, EventConfig.class); // manually bind "native" sub-section - Config nativeSection = section.hasPath("native") - ? section.getConfig("native") : ConfigFactory.empty(); + Config nativeSection = section.hasPath(nativeKey) + ? section.getConfig(nativeKey) : ConfigFactory.empty(); ec.nativeQueue = new NativeConfig(); if (nativeSection.hasPath("useNativeQueue")) { ec.nativeQueue.useNativeQueue = nativeSection.getBoolean("useNativeQueue"); @@ -100,9 +102,9 @@ public static EventConfig fromConfig(Config config) { } // manually bind topics — each item may have optional fields - if (section.hasPath("topics")) { + if (section.hasPath(topicsKey)) { ec.topics = new ArrayList<>(); - for (com.typesafe.config.ConfigObject obj : section.getObjectList("topics")) { + for (com.typesafe.config.ConfigObject obj : section.getObjectList(topicsKey)) { Config tc = obj.toConfig(); TopicConfig topic = new TopicConfig(); if (tc.hasPath("triggerName")) { diff --git a/common/src/main/java/org/tron/core/config/args/MiscConfig.java b/common/src/main/java/org/tron/core/config/args/MiscConfig.java index fa8b4f5d90f..f6c3b200b80 100644 --- a/common/src/main/java/org/tron/core/config/args/MiscConfig.java +++ b/common/src/main/java/org/tron/core/config/args/MiscConfig.java @@ -43,9 +43,10 @@ public static MiscConfig fromConfig(Config config) { // trx mc.trxReferenceBlock = config.hasPath("trx.reference.block") ? config.getString("trx.reference.block") : "solid"; - if (config.hasPath("trx.expiration.timeInMilliseconds") - && config.getLong("trx.expiration.timeInMilliseconds") > 0) { - mc.trxExpirationTimeInMilliseconds = config.getLong("trx.expiration.timeInMilliseconds"); + String trxExpirationKey = "trx.expiration.timeInMilliseconds"; + if (config.hasPath(trxExpirationKey) + && config.getLong(trxExpirationKey) > 0) { + mc.trxExpirationTimeInMilliseconds = config.getLong(trxExpirationKey); } // energy (note: config key has typo "enery" — preserved for backward compat) 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 1088fc1fb2f..616886a9146 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 @@ -360,6 +360,13 @@ public static class DnsConfig { public static NodeConfig fromConfig(Config config) { Config section = config.getConfig("node"); + // Replace null values with empty strings before bean binding. + // Some external configs (e.g. system-test) set "discovery.external.ip = null" + // which ConfigBeanFactory cannot bind to a String field. + // Note: hasPath() returns false for null values, use hasPathOrNull() instead. + section = replaceNullWithEmpty(section, "discovery.external.ip"); + section = replaceNullWithEmpty(section, "trustNode"); + // Auto-bind all fields and sub-beans NodeConfig nc = ConfigBeanFactory.create(section, NodeConfig.class); @@ -474,4 +481,14 @@ private static String getString(Config config, String path, String defaultValue) return config.hasPath(path) ? config.getString(path) : defaultValue; } + // Replace a null config value with empty string so ConfigBeanFactory can bind it. + // hasPath() returns false for null values; hasPathOrNull() returns true. + private static Config replaceNullWithEmpty(Config config, String path) { + if (config.hasPathOrNull(path) && config.getIsNull(path)) { + return config.withValue(path, + com.typesafe.config.ConfigValueFactory.fromAnyRef("")); + } + return config; + } + } diff --git a/common/src/main/resources/reference.conf b/common/src/main/resources/reference.conf index fcd181c53b5..e657fd19dbb 100644 --- a/common/src/main/resources/reference.conf +++ b/common/src/main/resources/reference.conf @@ -747,6 +747,7 @@ committee = { consensusLogicOptimization = 0 allowTvmCancun = 0 allowTvmBlob = 0 + allowTvmOsaka = 0 allowAccountAssetOptimization = 0 allowAssetOptimization = 0 allowNewReward = 0 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 0dc5aae4342..07db07af5e2 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 @@ -523,6 +523,7 @@ private static void applyCommitteeConfig(CommitteeConfig cc) { PARAMETER.consensusLogicOptimization = cc.getConsensusLogicOptimization(); PARAMETER.allowTvmCancun = cc.getAllowTvmCancun(); PARAMETER.allowTvmBlob = cc.getAllowTvmBlob(); + PARAMETER.allowTvmOsaka = cc.getAllowTvmOsaka(); PARAMETER.unfreezeDelayDays = cc.getUnfreezeDelayDays(); // allowReceiptsMerkleRoot not in CommonParameter — skip for now PARAMETER.allowAccountAssetOptimization = cc.getAllowAccountAssetOptimization(); @@ -811,10 +812,6 @@ public static void applyConfigParams( eventConfig = EventConfig.fromConfig(config); applyEventConfig(eventConfig); - PARAMETER.allowTvmOsaka = - config.hasPath(ConfigKey.COMMITTEE_ALLOW_TVM_OSAKA) ? config - .getInt(ConfigKey.COMMITTEE_ALLOW_TVM_OSAKA) : 0; - logConfig(); } @@ -1079,7 +1076,7 @@ private static void loadDnsPublishParameters(NodeConfig.DnsConfig dns, if (dns.getChangeThreshold() > 0) { publishConfig.setChangeThreshold(dns.getChangeThreshold()); - } else if (dns.getChangeThreshold() != 0.0) { + } else if (Double.compare(dns.getChangeThreshold(), 0.0) != 0) { logger.error("Check node.dns.changeThreshold, should be bigger than 0, default 0.1"); } From 651e0193690efa0a7eefa338ac7641e755575a97 Mon Sep 17 00:00:00 2001 From: vividctrlalt Date: Mon, 6 Apr 2026 09:33:41 +0000 Subject: [PATCH 13/14] fix(config): address PR review feedback - DynamicArgs: parse NodeConfig once in reload(), pass to both methods - Storage: remove 12 unused getXxxFromConfig static methods - reference.conf: fix influxdb database default to "metrics" --- .../org/tron/core/config/args/Storage.java | 73 ------------------- common/src/main/resources/reference.conf | 2 +- .../tron/core/config/args/DynamicArgs.java | 11 ++- 3 files changed, 6 insertions(+), 80 deletions(-) diff --git a/common/src/main/java/org/tron/core/config/args/Storage.java b/common/src/main/java/org/tron/core/config/args/Storage.java index 4ed89373754..782a0ef07c8 100644 --- a/common/src/main/java/org/tron/core/config/args/Storage.java +++ b/common/src/main/java/org/tron/core/config/args/Storage.java @@ -25,7 +25,6 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.iq80.leveldb.CompressionType; import org.iq80.leveldb.Options; import org.tron.common.cache.CacheStrategies; @@ -124,78 +123,6 @@ public class Storage { // db root private final Map dbRoots = Maps.newConcurrentMap(); - /** - * All getXxxFromConfig methods now read from StorageConfig bean instead of - * manual string constants. Signatures preserved for backward compatibility. - */ - - public static String getDbEngineFromConfig(final Config config) { - return StorageConfig.fromConfig(config).getDb().getEngine(); - } - - public static Boolean getDbVersionSyncFromConfig(final Config config) { - return StorageConfig.fromConfig(config).getDb().isSync(); - } - - public static int getSnapshotMaxFlushCountFromConfig(final Config config) { - int maxFlushCountConfig = StorageConfig.fromConfig(config) - .getSnapshot().getMaxFlushCount(); - if (maxFlushCountConfig <= 0) { - throw new IllegalArgumentException("MaxFlushCount value can not be negative or zero!"); - } - if (maxFlushCountConfig > 500) { - throw new IllegalArgumentException("MaxFlushCount value must not exceed 500!"); - } - return maxFlushCountConfig; - } - - public static Boolean getContractParseSwitchFromConfig(final Config config) { - // contractParse is under event.subscribe, not storage — read from EventConfig - EventConfig ec = EventConfig.fromConfig(config); - return ec.isContractParse(); - } - - public static String getDbDirectoryFromConfig(final Config config) { - return StorageConfig.fromConfig(config).getDb().getDirectory(); - } - - public static String getIndexDirectoryFromConfig(final Config config) { - return StorageConfig.fromConfig(config).getIndex().getDirectory(); - } - - public static String getIndexSwitchFromConfig(final Config config) { - String val = StorageConfig.fromConfig(config).getIndex().getSwitch(); - return StringUtils.isNotEmpty(val) ? val : DEFAULT_INDEX_SWITCH; - } - - public static String getTransactionHistorySwitchFromConfig(final Config config) { - return StorageConfig.fromConfig(config).getTransHistory().getSwitch(); - } - - public static int getCheckpointVersionFromConfig(final Config config) { - return StorageConfig.fromConfig(config).getCheckpoint().getVersion(); - } - - public static boolean getCheckpointSyncFromConfig(final Config config) { - return StorageConfig.fromConfig(config).getCheckpoint().isSync(); - } - - public static int getEstimatedTransactionsFromConfig(final Config config) { - int estimatedTransactions = StorageConfig.fromConfig(config) - .getTxCache().getEstimatedTransactions(); - if (estimatedTransactions > 10000) { - estimatedTransactions = 10000; - } else if (estimatedTransactions < 100) { - estimatedTransactions = 100; - } - return estimatedTransactions; - } - - public static boolean getTxCacheInitOptimizationFromConfig(final Config config) { - return StorageConfig.fromConfig(config).getTxCache().isInitOptimization(); - } - - /** * Accepts raw storage Config sub-tree because cache.strategies has dynamic keys * (CacheType enum names) that ConfigBeanFactory cannot bind to fixed bean fields. diff --git a/common/src/main/resources/reference.conf b/common/src/main/resources/reference.conf index e657fd19dbb..3286ac73bbd 100644 --- a/common/src/main/resources/reference.conf +++ b/common/src/main/resources/reference.conf @@ -182,7 +182,7 @@ node.metrics = { influxdb { ip = "" port = 8086 - database = "" + database = "metrics" metricsReportInterval = 10 } } diff --git a/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java b/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java index 3c97b5ab3fa..5a9923b16c9 100644 --- a/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java +++ b/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java @@ -61,14 +61,14 @@ public void run() { public void reload() { logger.debug("Reloading ... "); Config config = Configuration.getByFileName(Args.getConfigFilePath()); + NodeConfig nodeConfig = NodeConfig.fromConfig(config); - updateActiveNodes(config); + updateActiveNodes(nodeConfig); - updateTrustNodes(config); + updateTrustNodes(nodeConfig); } - private void updateActiveNodes(Config config) { - NodeConfig nodeConfig = NodeConfig.fromConfig(config); + private void updateActiveNodes(NodeConfig nodeConfig) { List newActiveNodes = Args.filterInetSocketAddress(nodeConfig.getActive(), true); parameter.setActiveNodes(newActiveNodes); @@ -79,8 +79,7 @@ private void updateActiveNodes(Config config) { TronNetService.getP2pConfig().getActiveNodes().toString()); } - private void updateTrustNodes(Config config) { - NodeConfig nodeConfig = NodeConfig.fromConfig(config); + private void updateTrustNodes(NodeConfig nodeConfig) { List newPassiveNodes = new java.util.ArrayList<>(); for (InetSocketAddress sa : Args.filterInetSocketAddress(nodeConfig.getPassive(), false)) { newPassiveNodes.add(sa.getAddress()); From c38f3885d92b1759445178e63d65a4936b91cd4b Mon Sep 17 00:00:00 2001 From: vividctrlalt Date: Wed, 8 Apr 2026 06:01:26 +0000 Subject: [PATCH 14/14] fix(config): restore lost memoFee/allowNewReward clamps and add boundary tests Add missing clamps in CommitteeConfig.postProcess(): - memoFee clamped to [0, 1_000_000_000] (regression from manual parsing) - allowNewReward clamped to [0, 1] (must run before cross-field check) Add boundary test coverage for every clamp in CommitteeConfig, NodeConfig, VmConfig, and Args bridge code (fetchBlockTimeout). 31 new test cases pin each clamp's below/above/in-range behavior to prevent silent regression in future refactors. Reported by reviewer kuny0707. --- .../core/config/args/CommitteeConfig.java | 9 + .../core/config/args/CommitteeConfigTest.java | 162 ++++++++++++++++++ .../tron/core/config/args/NodeConfigTest.java | 84 +++++++++ .../tron/core/config/args/VmConfigTest.java | 18 ++ .../org/tron/core/config/args/ArgsTest.java | 45 +++++ 5 files changed, 318 insertions(+) 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 5c591afbd44..235c6855ea0 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 @@ -148,6 +148,15 @@ private void postProcess() { if (dynamicEnergyMaxFactor < 0) { dynamicEnergyMaxFactor = 0; } if (dynamicEnergyMaxFactor > 100_000L) { dynamicEnergyMaxFactor = 100_000L; } + // clamp allowNewReward to 0-1 (must run BEFORE the cross-field check below, + // which depends on allowNewReward != 1) + if (allowNewReward < 0) { allowNewReward = 0; } + if (allowNewReward > 1) { allowNewReward = 1; } + + // clamp memoFee to 0-1_000_000_000 + if (memoFee < 0) { memoFee = 0; } + if (memoFee > 1_000_000_000L) { memoFee = 1_000_000_000L; } + // cross-field: allowOldRewardOpt requires at least one reward/vote flag if (allowOldRewardOpt == 1 && allowNewRewardAlgorithm != 1 && allowNewReward != 1 && allowTvmVote != 1) { diff --git a/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java b/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java index 0f0270454b7..962b6a349ab 100644 --- a/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java +++ b/common/src/test/java/org/tron/core/config/args/CommitteeConfigTest.java @@ -68,4 +68,166 @@ public void testAllowOldRewardOptWithPrerequisite() { withRef("committee { allowOldRewardOpt = 1, allowTvmVote = 1 }")); assertEquals(1, cc.getAllowOldRewardOpt()); } + + // =========================================================================== + // Boundary tests for postProcess() clamps + // + // Background: PR #6615 (config bean refactor) silently dropped clamps for + // memoFee and allowNewReward because no test covered the boundary cases. + // These tests pin every clamp in CommitteeConfig.postProcess() so future + // refactors cannot drop them undetected. + // =========================================================================== + + // ----- memoFee: clamped to [0, 1_000_000_000] ----- + + @Test + public void testMemoFeeClampedBelowZero() { + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { memoFee = -100 }")).getMemoFee()); + } + + @Test + public void testMemoFeeClampedAboveMax() { + assertEquals(1_000_000_000L, CommitteeConfig.fromConfig( + withRef("committee { memoFee = 5000000000 }")).getMemoFee()); + } + + @Test + public void testMemoFeeInRangeUnchanged() { + assertEquals(500_000_000L, CommitteeConfig.fromConfig( + withRef("committee { memoFee = 500000000 }")).getMemoFee()); + } + + @Test + public void testMemoFeeBoundaryValues() { + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { memoFee = 0 }")).getMemoFee()); + assertEquals(1_000_000_000L, CommitteeConfig.fromConfig( + withRef("committee { memoFee = 1000000000 }")).getMemoFee()); + } + + // ----- allowNewReward: clamped to [0, 1] ----- + + @Test + public void testAllowNewRewardClampedBelowZero() { + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { allowNewReward = -5 }")).getAllowNewReward()); + } + + @Test + public void testAllowNewRewardClampedAboveOne() { + assertEquals(1, CommitteeConfig.fromConfig( + withRef("committee { allowNewReward = 99 }")).getAllowNewReward()); + } + + @Test + public void testAllowNewRewardBoundaryValues() { + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { allowNewReward = 0 }")).getAllowNewReward()); + assertEquals(1, CommitteeConfig.fromConfig( + withRef("committee { allowNewReward = 1 }")).getAllowNewReward()); + } + + // Critical: clamp must run BEFORE the cross-field check, otherwise + // `allowNewReward = 2` (intended as "enabled") would still satisfy + // `allowNewReward != 1` and the cross-field check would throw. + // This test pins the clamp ordering. + @Test + public void testAllowNewRewardClampRunsBeforeCrossFieldCheck() { + CommitteeConfig cc = CommitteeConfig.fromConfig(withRef( + "committee { allowOldRewardOpt = 1, allowNewReward = 2 }")); + assertEquals(1, cc.getAllowNewReward()); + assertEquals(1, cc.getAllowOldRewardOpt()); + } + + // ----- allowDelegateOptimization: clamped to [0, 1] ----- + + @Test + public void testAllowDelegateOptimizationClampedBelowZero() { + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { allowDelegateOptimization = -3 }")) + .getAllowDelegateOptimization()); + } + + @Test + public void testAllowDelegateOptimizationClampedAboveOne() { + assertEquals(1, CommitteeConfig.fromConfig( + withRef("committee { allowDelegateOptimization = 7 }")) + .getAllowDelegateOptimization()); + } + + // ----- allowDynamicEnergy: clamped to [0, 1] ----- + + @Test + public void testAllowDynamicEnergyClampedBelowZero() { + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { allowDynamicEnergy = -1 }")).getAllowDynamicEnergy()); + } + + // ----- unfreezeDelayDays: clamped to [0, 365] (boundary values) ----- + + @Test + public void testUnfreezeDelayDaysBoundaryValues() { + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { unfreezeDelayDays = 0 }")).getUnfreezeDelayDays()); + assertEquals(365, CommitteeConfig.fromConfig( + withRef("committee { unfreezeDelayDays = 365 }")).getUnfreezeDelayDays()); + assertEquals(100, CommitteeConfig.fromConfig( + withRef("committee { unfreezeDelayDays = 100 }")).getUnfreezeDelayDays()); + } + + // ----- dynamicEnergyThreshold: clamped to [0, 100_000_000_000_000_000] ----- + + @Test + public void testDynamicEnergyThresholdClampedBelowZero() { + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { dynamicEnergyThreshold = -1 }")) + .getDynamicEnergyThreshold()); + } + + // ----- dynamicEnergyIncreaseFactor: clamped to [0, 10_000] ----- + + @Test + public void testDynamicEnergyIncreaseFactorClamped() { + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { dynamicEnergyIncreaseFactor = -1 }")) + .getDynamicEnergyIncreaseFactor()); + assertEquals(10_000L, CommitteeConfig.fromConfig( + withRef("committee { dynamicEnergyIncreaseFactor = 10001 }")) + .getDynamicEnergyIncreaseFactor()); + assertEquals(5_000L, CommitteeConfig.fromConfig( + withRef("committee { dynamicEnergyIncreaseFactor = 5000 }")) + .getDynamicEnergyIncreaseFactor()); + } + + // ----- dynamicEnergyMaxFactor: clamped to [0, 100_000] ----- + + @Test + public void testDynamicEnergyMaxFactorClamped() { + assertEquals(0, CommitteeConfig.fromConfig( + withRef("committee { dynamicEnergyMaxFactor = -1 }")) + .getDynamicEnergyMaxFactor()); + assertEquals(100_000L, CommitteeConfig.fromConfig( + withRef("committee { dynamicEnergyMaxFactor = 100001 }")) + .getDynamicEnergyMaxFactor()); + assertEquals(50_000L, CommitteeConfig.fromConfig( + withRef("committee { dynamicEnergyMaxFactor = 50000 }")) + .getDynamicEnergyMaxFactor()); + } + + // ----- Cross-field validation for allowOldRewardOpt ----- + + @Test + public void testAllowOldRewardOptWithAllowNewReward() { + CommitteeConfig cc = CommitteeConfig.fromConfig( + withRef("committee { allowOldRewardOpt = 1, allowNewReward = 1 }")); + assertEquals(1, cc.getAllowOldRewardOpt()); + } + + @Test + public void testAllowOldRewardOptWithAllowNewRewardAlgorithm() { + CommitteeConfig cc = CommitteeConfig.fromConfig( + withRef("committee { allowOldRewardOpt = 1, allowNewRewardAlgorithm = 1 }")); + assertEquals(1, cc.getAllowOldRewardOpt()); + } } 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 9a633b7d63a..55012393463 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 @@ -141,4 +141,88 @@ public void testRpcUserOverrideExplicitValues() { assertEquals(8388608, rpc.getMaxMessageSize()); assertEquals(16384, rpc.getMaxHeaderListSize()); } + + // =========================================================================== + // Boundary tests for postProcess() clamps + // Pin every clamp in NodeConfig.postProcess() so future refactors cannot + // drop them undetected (regression seen in PR #6615 with CommitteeConfig). + // =========================================================================== + + // ----- blockProducedTimeOut: clamped to [30, 100] ----- + + @Test + public void testBlockProducedTimeOutClampedBelowMin() { + NodeConfig nc = NodeConfig.fromConfig( + withRef("node { blockProducedTimeOut = 10 }")); + assertEquals(30, nc.getBlockProducedTimeOut()); + } + + @Test + public void testBlockProducedTimeOutClampedAboveMax() { + NodeConfig nc = NodeConfig.fromConfig( + withRef("node { blockProducedTimeOut = 200 }")); + assertEquals(100, nc.getBlockProducedTimeOut()); + } + + @Test + public void testBlockProducedTimeOutBoundaryValues() { + assertEquals(30, NodeConfig.fromConfig( + withRef("node { blockProducedTimeOut = 30 }")).getBlockProducedTimeOut()); + assertEquals(100, NodeConfig.fromConfig( + withRef("node { blockProducedTimeOut = 100 }")).getBlockProducedTimeOut()); + assertEquals(75, NodeConfig.fromConfig( + withRef("node { blockProducedTimeOut = 75 }")).getBlockProducedTimeOut()); + } + + // ----- inactiveThreshold: minimum 1 ----- + + @Test + public void testInactiveThresholdClampedBelowMin() { + NodeConfig nc = NodeConfig.fromConfig( + withRef("node { inactiveThreshold = 0 }")); + assertEquals(1, nc.getInactiveThreshold()); + } + + @Test + public void testInactiveThresholdClampedNegative() { + NodeConfig nc = NodeConfig.fromConfig( + withRef("node { inactiveThreshold = -100 }")); + assertEquals(1, nc.getInactiveThreshold()); + } + + @Test + public void testInactiveThresholdInRangeUnchanged() { + assertEquals(1, NodeConfig.fromConfig( + withRef("node { inactiveThreshold = 1 }")).getInactiveThreshold()); + assertEquals(600, NodeConfig.fromConfig( + withRef("node { inactiveThreshold = 600 }")).getInactiveThreshold()); + assertEquals(1000, NodeConfig.fromConfig( + withRef("node { inactiveThreshold = 1000 }")).getInactiveThreshold()); + } + + // ----- maxFastForwardNum: clamped to [1, MAX_ACTIVE_WITNESS_NUM=27] ----- + + @Test + public void testMaxFastForwardNumClampedBelowMin() { + NodeConfig nc = NodeConfig.fromConfig( + withRef("node { maxFastForwardNum = 0 }")); + assertEquals(1, nc.getMaxFastForwardNum()); + } + + @Test + public void testMaxFastForwardNumClampedAboveMax() { + NodeConfig nc = NodeConfig.fromConfig( + withRef("node { maxFastForwardNum = 100 }")); + assertEquals(27, nc.getMaxFastForwardNum()); + } + + @Test + public void testMaxFastForwardNumBoundaryValues() { + assertEquals(1, NodeConfig.fromConfig( + withRef("node { maxFastForwardNum = 1 }")).getMaxFastForwardNum()); + assertEquals(27, NodeConfig.fromConfig( + withRef("node { maxFastForwardNum = 27 }")).getMaxFastForwardNum()); + assertEquals(4, NodeConfig.fromConfig( + withRef("node { maxFastForwardNum = 4 }")).getMaxFastForwardNum()); + } } diff --git a/common/src/test/java/org/tron/core/config/args/VmConfigTest.java b/common/src/test/java/org/tron/core/config/args/VmConfigTest.java index 0e7c5438580..b134fe00c2b 100644 --- a/common/src/test/java/org/tron/core/config/args/VmConfigTest.java +++ b/common/src/test/java/org/tron/core/config/args/VmConfigTest.java @@ -70,4 +70,22 @@ public void testPartialConfig() { assertFalse(vm.isSupportConstant()); // default assertEquals(500, vm.getLruCacheSize()); // default } + + // =========================================================================== + // Boundary tests for postProcess() clamps + // Pin every clamp in VmConfig.postProcess() so future refactors cannot + // drop them undetected (regression seen in PR #6615 with CommitteeConfig). + // =========================================================================== + + // ----- estimateEnergyMaxRetry: clamped to [0, 10] ----- + + @Test + public void testEstimateEnergyMaxRetryBoundaryValues() { + assertEquals(0, VmConfig.fromConfig( + withRef("vm { estimateEnergyMaxRetry = 0 }")).getEstimateEnergyMaxRetry()); + assertEquals(10, VmConfig.fromConfig( + withRef("vm { estimateEnergyMaxRetry = 10 }")).getEstimateEnergyMaxRetry()); + assertEquals(3, VmConfig.fromConfig( + withRef("vm { estimateEnergyMaxRetry = 3 }")).getEstimateEnergyMaxRetry()); + } } 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 24e560eb16b..e0d9d456e9a 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 @@ -370,5 +370,50 @@ public void testConfigStorageDefaults() { Args.clearParam(); } + + // =========================================================================== + // Boundary tests for clamps applied in Args.java bridge code (not in + // bean postProcess()). + // + // fetchBlockTimeout is read from NodeConfig but clamped in Args.applyNodeConfig + // to range [100, 1000]. Pin this clamp here so any future refactor that moves + // it (e.g. into NodeConfig.postProcess()) preserves the behavior. + // =========================================================================== + + @Test + public void testFetchBlockTimeoutClampedBelowMin() { + Map override = new HashMap<>(); + override.put("storage.db.directory", "database"); + override.put("node.fetchBlock.timeout", "50"); + Config config = ConfigFactory.parseMap(override) + .withFallback(ConfigFactory.defaultReference()); + Args.applyConfigParams(config); + Assert.assertEquals(100, Args.getInstance().getFetchBlockTimeout()); + Args.clearParam(); + } + + @Test + public void testFetchBlockTimeoutClampedAboveMax() { + Map override = new HashMap<>(); + override.put("storage.db.directory", "database"); + override.put("node.fetchBlock.timeout", "2000"); + Config config = ConfigFactory.parseMap(override) + .withFallback(ConfigFactory.defaultReference()); + Args.applyConfigParams(config); + Assert.assertEquals(1000, Args.getInstance().getFetchBlockTimeout()); + Args.clearParam(); + } + + @Test + public void testFetchBlockTimeoutInRangeUnchanged() { + Map override = new HashMap<>(); + override.put("storage.db.directory", "database"); + override.put("node.fetchBlock.timeout", "500"); + Config config = ConfigFactory.parseMap(override) + .withFallback(ConfigFactory.defaultReference()); + Args.applyConfigParams(config); + Assert.assertEquals(500, Args.getInstance().getFetchBlockTimeout()); + Args.clearParam(); + } }