Skip to content

Commit 057bf4e

Browse files
committed
fix(vm): harden constant call timeout deadline
1 parent 8b7901f commit 057bf4e

6 files changed

Lines changed: 71 additions & 27 deletions

File tree

actuator/src/main/java/org/tron/core/actuator/VMActuator.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -398,13 +398,9 @@ private void create()
398398
byte[] ops = newSmartContract.getBytecode().toByteArray();
399399
rootInternalTx = new InternalTransaction(trx, trxType);
400400

401-
long maxCpuTimeOfOneTx = rootRepository.getDynamicPropertiesStore()
402-
.getMaxCpuTimeOfOneTx() * VMConstant.ONE_THOUSAND;
403-
long thisTxCPULimitInUs = (long) (maxCpuTimeOfOneTx * getCpuLimitInUsRatio());
404-
long constantCallTimeoutMs = CommonParameter.getInstance().getConstantCallTimeoutMs();
405-
if (isConstantCall && constantCallTimeoutMs > 0L) {
406-
thisTxCPULimitInUs = constantCallTimeoutMs * VMConstant.ONE_THOUSAND;
407-
}
401+
long thisTxCPULimitInUs = calculateCpuLimitInUs(isConstantCall,
402+
rootRepository.getDynamicPropertiesStore().getMaxCpuTimeOfOneTx(),
403+
getCpuLimitInUsRatio(), CommonParameter.getInstance().getConstantCallTimeoutMs());
408404
long vmStartInUs = System.nanoTime() / VMConstant.ONE_THOUSAND;
409405
long vmShouldEndInUs = vmStartInUs + thisTxCPULimitInUs;
410406
ProgramInvoke programInvoke = ProgramInvokeFactory
@@ -516,13 +512,9 @@ private void call()
516512
energyLimit = getTotalEnergyLimit(creator, caller, contract, feeLimit, callValue);
517513
}
518514

519-
long maxCpuTimeOfOneTx = rootRepository.getDynamicPropertiesStore()
520-
.getMaxCpuTimeOfOneTx() * VMConstant.ONE_THOUSAND;
521-
long thisTxCPULimitInUs = (long) (maxCpuTimeOfOneTx * getCpuLimitInUsRatio());
522-
long constantCallTimeoutMs = CommonParameter.getInstance().getConstantCallTimeoutMs();
523-
if (isConstantCall && constantCallTimeoutMs > 0L) {
524-
thisTxCPULimitInUs = constantCallTimeoutMs * VMConstant.ONE_THOUSAND;
525-
}
515+
long thisTxCPULimitInUs = calculateCpuLimitInUs(isConstantCall,
516+
rootRepository.getDynamicPropertiesStore().getMaxCpuTimeOfOneTx(),
517+
getCpuLimitInUsRatio(), CommonParameter.getInstance().getConstantCallTimeoutMs());
526518
long vmStartInUs = System.nanoTime() / VMConstant.ONE_THOUSAND;
527519
long vmShouldEndInUs = vmStartInUs + thisTxCPULimitInUs;
528520
ProgramInvoke programInvoke = ProgramInvokeFactory
@@ -699,6 +691,14 @@ private double getCpuLimitInUsRatio() {
699691
return cpuLimitRatio;
700692
}
701693

694+
static long calculateCpuLimitInUs(boolean isConstantCall, long maxCpuTimeOfOneTxMs,
695+
double cpuLimitInUsRatio, long constantCallTimeoutMs) {
696+
if (isConstantCall && constantCallTimeoutMs > 0L) {
697+
return constantCallTimeoutMs * VMConstant.ONE_THOUSAND;
698+
}
699+
return (long) (maxCpuTimeOfOneTxMs * VMConstant.ONE_THOUSAND * cpuLimitInUsRatio);
700+
}
701+
702702
public long getTotalEnergyLimitWithFixRatio(AccountCapsule creator, AccountCapsule caller,
703703
TriggerSmartContract contract, long feeLimit, long callValue)
704704
throws ContractValidateException {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.tron.core.actuator;
2+
3+
import static org.junit.Assert.assertEquals;
4+
5+
import org.junit.Test;
6+
7+
public class VMActuatorTest {
8+
9+
@Test
10+
public void testConstantCallUsesConfiguredTimeoutVerbatim() {
11+
assertEquals(123_000L, VMActuator.calculateCpuLimitInUs(true, 80L, 5.0, 123L));
12+
}
13+
14+
@Test
15+
public void testConstantCallWithoutConfiguredTimeoutUsesNetworkDeadline() {
16+
assertEquals(400_000L, VMActuator.calculateCpuLimitInUs(true, 80L, 5.0, 0L));
17+
}
18+
19+
@Test
20+
public void testNonConstantCallIgnoresConfiguredTimeout() {
21+
assertEquals(400_000L, VMActuator.calculateCpuLimitInUs(false, 80L, 5.0, 123L));
22+
}
23+
}

common/src/main/java/org/tron/common/parameter/CommonParameter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ public class CommonParameter {
6464
* functions, estimateenergy, eth_call, eth_estimateGas, and any other
6565
* RPC routed through Wallet#callConstantContract. 0 = use the same
6666
* deadline as block processing (current behaviour). When operators set
67-
* this in config the value must be positive; validated at config-load
68-
* in VmConfig.
67+
* this in config the value must be positive and fit VM deadline conversion;
68+
* validated at config-load in VmConfig.
6969
*/
7070
@Getter
7171
@Setter

common/src/main/java/org/tron/core/config/args/VmConfig.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99

1010
/**
1111
* VM configuration bean. Field names match config.conf keys under the "vm" section.
12-
* Bound automatically via ConfigBeanFactory — no manual key constants needed.
12+
* Most fields are bound automatically via ConfigBeanFactory; opt-in fields that
13+
* must stay absent from reference.conf are bound manually after hasPath checks.
1314
*/
1415
@Slf4j
1516
@Getter
1617
@Setter
1718
public class VmConfig {
1819

1920
private static final String CONSTANT_CALL_TIMEOUT_MS_KEY = "constantCallTimeoutMs";
21+
static final long MAX_CONSTANT_CALL_TIMEOUT_MS = Long.MAX_VALUE / 1_000L;
2022

2123
private boolean supportConstant = false;
2224
private long maxEnergyLimitForConstant = 100_000_000L;
@@ -71,13 +73,19 @@ private void postProcess(Config vmSection) {
7173

7274
// constantCallTimeoutMs is excluded from ConfigBeanFactory binding (no
7375
// setter) and intentionally absent from reference.conf, so hasPath alone
74-
// tells us whether the operator opted in. Only positive values are valid.
76+
// tells us whether the operator opted in. Only positive values that can be
77+
// safely converted to microseconds are valid.
7578
if (vmSection.hasPath(CONSTANT_CALL_TIMEOUT_MS_KEY)) {
7679
long value = vmSection.getLong(CONSTANT_CALL_TIMEOUT_MS_KEY);
7780
if (value <= 0L) {
7881
throw new IllegalArgumentException(
7982
"vm.constantCallTimeoutMs must be > 0 when configured, got " + value);
8083
}
84+
if (value > MAX_CONSTANT_CALL_TIMEOUT_MS) {
85+
throw new IllegalArgumentException(
86+
"vm.constantCallTimeoutMs must be <= " + MAX_CONSTANT_CALL_TIMEOUT_MS
87+
+ " to fit VM deadline conversion, got " + value);
88+
}
8189
constantCallTimeoutMs = value;
8290
}
8391
}

common/src/test/java/org/tron/core/config/args/VmConfigTest.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,10 @@ public void testEstimateEnergyMaxRetryBoundaryValues() {
9191

9292
// ===========================================================================
9393
// Constant-call timeout (issue #6681). The validation rule: any positive
94-
// value is accepted, but zero/negative is rejected ONLY when the operator
95-
// explicitly set the property in their config. Absence keeps the in-Java
96-
// default (0L = "share the block-processing deadline").
94+
// value that fits VM deadline conversion is accepted, but zero/negative is
95+
// rejected ONLY when the operator explicitly set the property in their
96+
// config. Absence keeps the in-Java default (0L = "share the
97+
// block-processing deadline").
9798
// ===========================================================================
9899

99100
@Test
@@ -139,4 +140,16 @@ public void testConstantCallTimeoutNegativeRejected() {
139140
ex.getMessage().contains("constantCallTimeoutMs"));
140141
}
141142
}
143+
144+
@Test
145+
public void testConstantCallTimeoutOverflowRejected() {
146+
long value = VmConfig.MAX_CONSTANT_CALL_TIMEOUT_MS + 1L;
147+
try {
148+
VmConfig.fromConfig(withRef("vm { constantCallTimeoutMs = " + value + " }"));
149+
org.junit.Assert.fail("expected IllegalArgumentException for overflowing ms");
150+
} catch (IllegalArgumentException ex) {
151+
org.junit.Assert.assertTrue(ex.getMessage(),
152+
ex.getMessage().contains("deadline conversion"));
153+
}
154+
}
142155
}

framework/src/main/resources/config.conf

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -715,12 +715,12 @@ vm = {
715715
# triggerconstantcontract, triggersmartcontract dispatched to view/pure
716716
# functions, estimateenergy, eth_call, eth_estimateGas, and any other RPC
717717
# routed through the constant-call path. When set, must be a positive
718-
# integer and is used verbatim as the per-call deadline (no clamp against
719-
# the network's maxCpuTimeOfOneTx). Omit the property entirely to keep the
720-
# default behaviour of sharing the block-processing deadline. Migration
721-
# note: if previously running --debug to extend constant calls, switch to
722-
# this option (--debug also extends block-processing, which is unsafe; see
723-
# issue #6266).
718+
# integer that fits VM deadline conversion and is used verbatim as the
719+
# per-call deadline (no clamp against the network's maxCpuTimeOfOneTx).
720+
# Omit the property entirely to keep the default behaviour of sharing the
721+
# block-processing deadline. Migration note: if previously running --debug
722+
# to extend constant calls, switch to this option (--debug also extends
723+
# block-processing, which is unsafe; see issue #6266).
724724
# constantCallTimeoutMs = 100
725725
}
726726

0 commit comments

Comments
 (0)