Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions actuator/src/main/java/org/tron/core/actuator/VMActuator.java
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,10 @@ private void create()
long maxCpuTimeOfOneTx = rootRepository.getDynamicPropertiesStore()
.getMaxCpuTimeOfOneTx() * VMConstant.ONE_THOUSAND;
long thisTxCPULimitInUs = (long) (maxCpuTimeOfOneTx * getCpuLimitInUsRatio());
long constantCallTimeoutMs = CommonParameter.getInstance().getConstantCallTimeoutMs();
if (isConstantCall && constantCallTimeoutMs > 0L) {
thisTxCPULimitInUs = constantCallTimeoutMs * VMConstant.ONE_THOUSAND;
}
long vmStartInUs = System.nanoTime() / VMConstant.ONE_THOUSAND;
long vmShouldEndInUs = vmStartInUs + thisTxCPULimitInUs;
ProgramInvoke programInvoke = ProgramInvokeFactory
Expand Down Expand Up @@ -514,8 +518,11 @@ private void call()

long maxCpuTimeOfOneTx = rootRepository.getDynamicPropertiesStore()
.getMaxCpuTimeOfOneTx() * VMConstant.ONE_THOUSAND;
long thisTxCPULimitInUs =
(long) (maxCpuTimeOfOneTx * getCpuLimitInUsRatio());
long thisTxCPULimitInUs = (long) (maxCpuTimeOfOneTx * getCpuLimitInUsRatio());
long constantCallTimeoutMs = CommonParameter.getInstance().getConstantCallTimeoutMs();
if (isConstantCall && constantCallTimeoutMs > 0L) {
thisTxCPULimitInUs = constantCallTimeoutMs * VMConstant.ONE_THOUSAND;
}
long vmStartInUs = System.nanoTime() / VMConstant.ONE_THOUSAND;
long vmShouldEndInUs = vmStartInUs + thisTxCPULimitInUs;
ProgramInvoke programInvoke = ProgramInvokeFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ public class CommonParameter {
@Getter
@Setter
public double maxTimeRatio = calcMaxTimeRatio();
/**
* Max TVM execution time (ms) for constant calls — covers
* triggerconstantcontract, triggersmartcontract dispatched to view/pure
* functions, estimateenergy, eth_call, eth_estimateGas, and any other
* RPC routed through Wallet#callConstantContract. 0 = use the same
* deadline as block processing (current behaviour). When operators set
* this in config the value must be positive; validated at config-load
* in VmConfig.
*/
@Getter
@Setter
public long constantCallTimeoutMs = 0L;
@Getter
@Setter
public boolean saveInternalTx;
Expand Down
24 changes: 22 additions & 2 deletions common/src/main/java/org/tron/core/config/args/VmConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.typesafe.config.Config;
import com.typesafe.config.ConfigBeanFactory;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -15,6 +16,8 @@
@Setter
public class VmConfig {

private static final String CONSTANT_CALL_TIMEOUT_MS_KEY = "constantCallTimeoutMs";

private boolean supportConstant = false;
private long maxEnergyLimitForConstant = 100_000_000L;
private int lruCacheSize = 500;
Expand All @@ -27,6 +30,11 @@ public class VmConfig {
private boolean saveInternalTx = false;
private boolean saveFeaturedInternalTx = false;
private boolean saveCancelAllUnfreezeV2Details = false;
// Excluded from ConfigBeanFactory binding (no setter): the property is
// intentionally absent from reference.conf so {@code Config#hasPath} alone
// signals operator opt-in. Bound manually in {@link #fromConfig}.
@Setter(AccessLevel.NONE)
private long constantCallTimeoutMs = 0L;

/**
* Create VmConfig from the "vm" section of the application config.
Expand All @@ -36,11 +44,11 @@ public class VmConfig {
public static VmConfig fromConfig(Config config) {
Config vmSection = config.getConfig("vm");
VmConfig vmConfig = ConfigBeanFactory.create(vmSection, VmConfig.class);
vmConfig.postProcess();
vmConfig.postProcess(vmSection);
return vmConfig;
}

private void postProcess() {
private void postProcess(Config vmSection) {
// clamp maxEnergyLimitForConstant
if (maxEnergyLimitForConstant < 3_000_000L) {
maxEnergyLimitForConstant = 3_000_000L;
Expand All @@ -60,5 +68,17 @@ private void postProcess() {
logger.warn("Configuring [vm.saveCancelAllUnfreezeV2Details] won't work as "
+ "vm.saveInternalTx or vm.saveFeaturedInternalTx is off.");
}

// constantCallTimeoutMs is excluded from ConfigBeanFactory binding (no
// setter) and intentionally absent from reference.conf, so hasPath alone
// tells us whether the operator opted in. Only positive values are valid.
if (vmSection.hasPath(CONSTANT_CALL_TIMEOUT_MS_KEY)) {
long value = vmSection.getLong(CONSTANT_CALL_TIMEOUT_MS_KEY);
if (value <= 0L) {
throw new IllegalArgumentException(
"vm.constantCallTimeoutMs must be > 0 when configured, got " + value);
}
constantCallTimeoutMs = value;
}
}
}
51 changes: 51 additions & 0 deletions common/src/test/java/org/tron/core/config/args/VmConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,55 @@ public void testEstimateEnergyMaxRetryBoundaryValues() {
assertEquals(3, VmConfig.fromConfig(
withRef("vm { estimateEnergyMaxRetry = 3 }")).getEstimateEnergyMaxRetry());
}

// ===========================================================================
// Constant-call timeout (issue #6681). The validation rule: any positive
// value is accepted, but zero/negative is rejected ONLY when the operator
// explicitly set the property in their config. Absence keeps the in-Java
// default (0L = "share the block-processing deadline").
// ===========================================================================

@Test
public void testConstantCallTimeoutDefaultWhenAbsent() {
// No path in the config, no entry in reference.conf -> default 0L kept,
// no validation triggered.
VmConfig vm = VmConfig.fromConfig(withRef());
assertEquals(0L, vm.getConstantCallTimeoutMs());
}

@Test
public void testConstantCallTimeoutAcceptsAnyPositiveValue() {
assertEquals(1L, VmConfig.fromConfig(
withRef("vm { constantCallTimeoutMs = 1 }")).getConstantCallTimeoutMs());
assertEquals(50L, VmConfig.fromConfig(
withRef("vm { constantCallTimeoutMs = 50 }")).getConstantCallTimeoutMs());
assertEquals(500L, VmConfig.fromConfig(
withRef("vm { constantCallTimeoutMs = 500 }")).getConstantCallTimeoutMs());
assertEquals(5_000L, VmConfig.fromConfig(
withRef("vm { constantCallTimeoutMs = 5000 }")).getConstantCallTimeoutMs());
}

@Test
public void testConstantCallTimeoutZeroRejectedWhenExplicitlyConfigured() {
// Operator wrote `= 0` in config -> treated as a misconfiguration even
// though it equals the in-Java default. Forces an explicit positive value.
try {
VmConfig.fromConfig(withRef("vm { constantCallTimeoutMs = 0 }"));
org.junit.Assert.fail("expected IllegalArgumentException for explicit 0");
} catch (IllegalArgumentException ex) {
org.junit.Assert.assertTrue(ex.getMessage(),
ex.getMessage().contains("constantCallTimeoutMs"));
}
}

@Test
public void testConstantCallTimeoutNegativeRejected() {
try {
VmConfig.fromConfig(withRef("vm { constantCallTimeoutMs = -1 }"));
org.junit.Assert.fail("expected IllegalArgumentException for negative ms");
} catch (IllegalArgumentException ex) {
org.junit.Assert.assertTrue(ex.getMessage(),
ex.getMessage().contains("constantCallTimeoutMs"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ private static void applyVmConfig(VmConfig vm) {
PARAMETER.saveInternalTx = vm.isSaveInternalTx();
PARAMETER.saveFeaturedInternalTx = vm.isSaveFeaturedInternalTx();
PARAMETER.saveCancelAllUnfreezeV2Details = vm.isSaveCancelAllUnfreezeV2Details();
PARAMETER.constantCallTimeoutMs = vm.getConstantCallTimeoutMs();
}

// Old applyStorageConfig removed — merged into applyStorageConfig()
Expand Down
12 changes: 12 additions & 0 deletions framework/src/main/resources/config.conf
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,18 @@ vm = {

# Indicates the max retry time for executing transaction in estimating energy. Default 3.
# estimateEnergyMaxRetry = 3

# Max TVM execution time (ms) for constant calls — applies to
# triggerconstantcontract, triggersmartcontract dispatched to view/pure
# functions, estimateenergy, eth_call, eth_estimateGas, and any other RPC
# routed through the constant-call path. When set, must be a positive
# integer and is used verbatim as the per-call deadline (no clamp against
# the network's maxCpuTimeOfOneTx). Omit the property entirely to keep the
# default behaviour of sharing the block-processing deadline. Migration
# note: if previously running --debug to extend constant calls, switch to
# this option (--debug also extends block-processing, which is unsafe; see
# issue #6266).
# constantCallTimeoutMs = 100
}

# These parameters are designed for private chain testing only and cannot be freely switched on or off in production systems.
Expand Down
Loading