Background
In the TRON network, the triggerconstantcontract API (and the Ethereum-compatible eth_call) is widely used for constant calls — including state queries via view/pure functions and transaction simulation (dry-runs). However, both are constrained by the TVM execution time limit (mainnet default: 80ms). When executing complex contract logic or intensive state access, this can lead to out_of_time exceptions, reducing the reliability of development tools, data services, and analytics platforms.
Running the FullNode with the --debug flag can remove the TVM timeout restriction for constant calls, but it also removes the timeout limit for transactions during normal block processing. As discussed in #6266, this can cause the local execution result to diverge from the result carried in the block (the block may report OUT_OF_TIME while the local node does not), which in turn causes the node to stop syncing. --debug is therefore not suitable for production nodes.
This issue supersedes #6288. After extensive discussion in that thread, the community reached consensus that a node-level configuration is preferable to the originally proposed request-body parameter approach. This new issue reflects the revised direction.
Related issues: #6266, #6288
Rationale
Why should this feature exist?
Node operators should be able to extend the TVM execution time window for constant calls only, without affecting block validation behavior. This would:
- Improve the stability of off-chain analysis tools and data services
- Prevent false negatives when querying or simulating complex contracts
- Reduce misleading error codes that confuse developers
- Avoid the sync-halt side effect of
--debug
What are the use-cases?
- Data services performing bulk state queries via contract calls
- Deep cross-contract call simulation
- Simulating complex logic (e.g., loops or large mappings) that exceed the default execution window
Why a node-level config instead of a request parameter?
As discussed in #6288, we decided against adding a timeout_ms field to the request body, for the following reasons:
- API standard compatibility. Adding a non-standard field to
eth_call would diverge from the Ethereum JSON-RPC specification and complicate integration for Ethereum tooling (ref).
- Separation of responsibility. Execution policy belongs to the node operator, not the caller. Operators that need extended simulation windows opt in explicitly; public-facing nodes keep the default (ref).
- No consensus risk. Constant calls are not broadcast on-chain, so a node-level knob does not risk SR consensus divergence (ref).
- Backward compatible. No API surface change, no SDK updates required.
Specification
Introduce a new node configuration option vm.constantCallTimeoutMs in config.conf:
vm {
# Max TVM execution time (ms) for constant calls. Omit to keep the current
# behaviour (constant calls share the block-processing deadline derived
# from the network's maxCpuTimeOfOneTx). When set, must be a positive
# integer; the value is used verbatim as the per-call deadline.
# constantCallTimeoutMs = 100
}
Behavior:
- Applies only to constant calls —
triggerconstantcontract, triggersmartcontract dispatched to view/pure functions, estimateenergy, eth_call, eth_estimateGas, and any other RPC routed through Wallet.callConstantContract.
- Does not affect TVM execution during block processing or transaction broadcasting.
- The configured value is used as the per-call deadline as-is. No clamp against the network's
maxCpuTimeOfOneTx, no cpuLimitInUsRatio scaling.
- Operator opt-in is detected via
Config#hasPath: the property is intentionally absent from reference.conf, so a node that does not write the property in its own config.conf is unaffected.
- Validation runs only when the operator opts in: explicit
0 and negative values are rejected at config-load time.
Safeguards
- Validation at config-load: when the operator sets
vm.constantCallTimeoutMs, the value must be a positive integer; 0 and negative values are rejected with a clear error.
- Block-processing path is structurally unaffected: the new branch in
VMActuator.create() / call() is gated by isConstantCall, so it is unreachable from block validation by structure, not by convention.
- Throttling concurrent constant calls is left to the operator's infrastructure (reverse proxy / API gateway / per-IP limits). Earlier drafts proposed an in-process
vm.constantCallMaxConcurrency cap, but the in-PR review concluded that hard-gating at the node would false-positive on deployments that already throttle externally and that the additional surface area was not justified for the threat model.
Test Specification
- Unit tests covering: default behavior unchanged; custom timeout respected for constant calls; block-processing timeout unchanged regardless of the new config.
- Integration tests verifying that a node with an extended
constantCallTimeoutMs still syncs correctly against the main network (no OUT_OF_TIME divergence like --debug).
- Stress tests under concurrent constant-call load.
Scope Of Impact
- Non-breaking. No API signature change; existing clients and SDKs unaffected.
- TVM executor must branch on call context (block processing vs. constant call) when selecting the timeout.
- Operator documentation and release notes must describe the new option and its risks.
Implementation
Tentative outline:
- Add the config field and wire it through
Args / VM config.
- Plumb the constant-call timeout through the invoke / program context — not a new global flag, and not by mutating
maxCpuTimeOfOneTx * ratio — so the block-processing deadline remains untouched by design, not by convention.
- Validate at config-load: reject
0 or negative values when the operator opts in.
Migration from --debug
Operators currently running with --debug purely to extend the constant-call window should switch to constantCallTimeoutMs=<ms> once this lands. --debug additionally extends the block-processing timeout, which is unsafe for any node serving traffic (see #6266). The release notes and the constantCallTimeoutMs config reference will cross-link this migration note so operators on the upgrade path see it in both places.
Are you willing to implement this feature?
Yes.
Background
In the TRON network, the
triggerconstantcontractAPI (and the Ethereum-compatibleeth_call) is widely used for constant calls — including state queries viaview/purefunctions and transaction simulation (dry-runs). However, both are constrained by the TVM execution time limit (mainnet default: 80ms). When executing complex contract logic or intensive state access, this can lead toout_of_timeexceptions, reducing the reliability of development tools, data services, and analytics platforms.Running the FullNode with the
--debugflag can remove the TVM timeout restriction for constant calls, but it also removes the timeout limit for transactions during normal block processing. As discussed in #6266, this can cause the local execution result to diverge from the result carried in the block (the block may reportOUT_OF_TIMEwhile the local node does not), which in turn causes the node to stop syncing.--debugis therefore not suitable for production nodes.This issue supersedes #6288. After extensive discussion in that thread, the community reached consensus that a node-level configuration is preferable to the originally proposed request-body parameter approach. This new issue reflects the revised direction.
Related issues: #6266, #6288
Rationale
Why should this feature exist?
Node operators should be able to extend the TVM execution time window for constant calls only, without affecting block validation behavior. This would:
--debugWhat are the use-cases?
Why a node-level config instead of a request parameter?
As discussed in #6288, we decided against adding a
timeout_msfield to the request body, for the following reasons:eth_callwould diverge from the Ethereum JSON-RPC specification and complicate integration for Ethereum tooling (ref).Specification
Introduce a new node configuration option
vm.constantCallTimeoutMsinconfig.conf:Behavior:
triggerconstantcontract,triggersmartcontractdispatched to view/pure functions,estimateenergy,eth_call,eth_estimateGas, and any other RPC routed throughWallet.callConstantContract.maxCpuTimeOfOneTx, nocpuLimitInUsRatioscaling.Config#hasPath: the property is intentionally absent fromreference.conf, so a node that does not write the property in its ownconfig.confis unaffected.0and negative values are rejected at config-load time.Safeguards
vm.constantCallTimeoutMs, the value must be a positive integer;0and negative values are rejected with a clear error.VMActuator.create()/call()is gated byisConstantCall, so it is unreachable from block validation by structure, not by convention.vm.constantCallMaxConcurrencycap, but the in-PR review concluded that hard-gating at the node would false-positive on deployments that already throttle externally and that the additional surface area was not justified for the threat model.Test Specification
constantCallTimeoutMsstill syncs correctly against the main network (noOUT_OF_TIMEdivergence like--debug).Scope Of Impact
Implementation
Tentative outline:
Args/ VM config.maxCpuTimeOfOneTx * ratio— so the block-processing deadline remains untouched by design, not by convention.0or negative values when the operator opts in.Migration from
--debugOperators currently running with
--debugpurely to extend the constant-call window should switch toconstantCallTimeoutMs=<ms>once this lands.--debugadditionally extends the block-processing timeout, which is unsafe for any node serving traffic (see #6266). The release notes and theconstantCallTimeoutMsconfig reference will cross-link this migration note so operators on the upgrade path see it in both places.Are you willing to implement this feature?
Yes.