Skip to content

Commit c414f1e

Browse files
committed
test(vm): add Wallet throttle test, tighten cap-engagement docs
Reflective test on Wallet.constantCallSemaphore covers permit count, lazy-init idempotency, and freeze-at-first-call. Sample docs now state the exact engagement condition.
1 parent e4adc30 commit c414f1e

4 files changed

Lines changed: 123 additions & 12 deletions

File tree

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ public class CommonParameter {
6868
@Setter
6969
public long constantCallTimeoutMs = 0L;
7070
/**
71-
* Max concurrent constant-call executions. Cap is engaged only when
72-
* constantCallTimeoutMs exceeds the baseline default; nodes leaving the
73-
* timeout at its default see no behaviour change.
71+
* Max concurrent constant-call executions. The cap engages strictly when
72+
* constantCallTimeoutMs exceeds the network's current maxCpuTimeOfOneTx;
73+
* nodes leaving constantCallTimeoutMs at its default see no throttling.
7474
*/
7575
@Getter
7676
@Setter

common/src/main/resources/reference.conf

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -717,8 +717,11 @@ vm = {
717717
# block-processing, which is unsafe; see issue #6266).
718718
constantCallTimeoutMs = 0
719719

720-
# Max concurrent constant-call executions. Engages only when
721-
# constantCallTimeoutMs is raised above 80.
720+
# Max concurrent constant-call executions. The cap engages strictly when
721+
# constantCallTimeoutMs exceeds the network's current maxCpuTimeOfOneTx;
722+
# nodes leaving constantCallTimeoutMs at 0 see no throttling. Note: a
723+
# committee proposal that lowers maxCpuTimeOfOneTx may newly engage the
724+
# cap on nodes that previously satisfied configured <= network.
722725
constantCallMaxConcurrency = 10
723726
}
724727

framework/src/main/resources/config.conf

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -713,15 +713,17 @@ vm = {
713713

714714
# -- Constant-call timeout (issue #6681) --
715715
# Max TVM execution time (ms) for constant calls (triggerconstantcontract /
716-
# eth_call). 0 = use the same deadline as block processing. Allowed values:
717-
# 0 or in [80, 500]. Raising this value engages a concurrency cap (see
718-
# constantCallMaxConcurrency). If you previously ran with --debug to extend
719-
# constant calls, switch to this option: --debug also extends block-processing
720-
# time which can desync the node (see issue #6266).
716+
# eth_call). 0 = use the same deadline as block processing. When set, must
717+
# be in [80, 500]. Migration note: if previously running --debug to extend
718+
# constant calls, switch to this option (--debug also extends block-processing,
719+
# which is unsafe; see issue #6266).
721720
# constantCallTimeoutMs = 0
722721

723-
# Max concurrent constant-call executions. Engages only when
724-
# constantCallTimeoutMs is raised above 80. Default: 10.
722+
# Max concurrent constant-call executions. The cap engages strictly when
723+
# constantCallTimeoutMs exceeds the network's current maxCpuTimeOfOneTx;
724+
# nodes leaving constantCallTimeoutMs at 0 see no throttling. Note: a
725+
# committee proposal that lowers maxCpuTimeOfOneTx may newly engage the
726+
# cap on nodes that previously satisfied configured <= network. Default: 10.
725727
# constantCallMaxConcurrency = 10
726728
}
727729

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package org.tron.core;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertFalse;
5+
import static org.junit.Assert.assertSame;
6+
import static org.junit.Assert.assertTrue;
7+
8+
import java.lang.reflect.Field;
9+
import java.lang.reflect.Method;
10+
import java.util.concurrent.Semaphore;
11+
12+
import org.junit.After;
13+
import org.junit.Before;
14+
import org.junit.Test;
15+
import org.tron.common.parameter.CommonParameter;
16+
17+
/**
18+
* Verifies the lazy-init contract for {@code Wallet.constantCallSemaphore}:
19+
* permit count is taken from {@code CommonParameter.constantCallMaxConcurrency}
20+
* at first call, and subsequent calls return the same instance. Together with
21+
* {@link org.tron.core.vm.VmTimeoutPolicyTest} (which covers
22+
* {@code isCapEngaged}), this is the integration-level coverage for the
23+
* constant-call throttle wired up in {@code Wallet.callConstantContract}.
24+
*/
25+
public class WalletConstantCallThrottleTest {
26+
27+
private static final Field SEMAPHORE_FIELD;
28+
private static final Method GET_SEMAPHORE_METHOD;
29+
30+
static {
31+
try {
32+
SEMAPHORE_FIELD = Wallet.class.getDeclaredField("constantCallSemaphore");
33+
SEMAPHORE_FIELD.setAccessible(true);
34+
GET_SEMAPHORE_METHOD = Wallet.class.getDeclaredMethod("getConstantCallSemaphore");
35+
GET_SEMAPHORE_METHOD.setAccessible(true);
36+
} catch (NoSuchFieldException | NoSuchMethodException e) {
37+
throw new ExceptionInInitializerError(e);
38+
}
39+
}
40+
41+
private int savedMaxConcurrency;
42+
43+
@Before
44+
public void setUp() throws Exception {
45+
savedMaxConcurrency = CommonParameter.getInstance().getConstantCallMaxConcurrency();
46+
SEMAPHORE_FIELD.set(null, null);
47+
}
48+
49+
@After
50+
public void tearDown() throws Exception {
51+
CommonParameter.getInstance().setConstantCallMaxConcurrency(savedMaxConcurrency);
52+
SEMAPHORE_FIELD.set(null, null);
53+
}
54+
55+
private static Semaphore invokeFactory() throws Exception {
56+
return (Semaphore) GET_SEMAPHORE_METHOD.invoke(null);
57+
}
58+
59+
@Test
60+
public void permitCountTracksConfiguredMaxConcurrency() throws Exception {
61+
CommonParameter.getInstance().setConstantCallMaxConcurrency(3);
62+
63+
Semaphore sem = invokeFactory();
64+
65+
assertEquals(3, sem.availablePermits());
66+
}
67+
68+
@Test
69+
public void factoryIsIdempotent() throws Exception {
70+
CommonParameter.getInstance().setConstantCallMaxConcurrency(2);
71+
72+
Semaphore first = invokeFactory();
73+
Semaphore second = invokeFactory();
74+
75+
assertSame(first, second);
76+
}
77+
78+
@Test
79+
public void permitCountFrozenAtFirstCall() throws Exception {
80+
CommonParameter.getInstance().setConstantCallMaxConcurrency(1);
81+
Semaphore sem = invokeFactory();
82+
83+
// Mutating the parameter after the semaphore has been created must not
84+
// resize it — confirms that operators cannot widen the cap on a live node.
85+
CommonParameter.getInstance().setConstantCallMaxConcurrency(99);
86+
87+
assertSame(sem, invokeFactory());
88+
assertEquals(1, sem.availablePermits());
89+
}
90+
91+
@Test
92+
public void semaphoreSaturatesAtConfiguredCap() throws Exception {
93+
CommonParameter.getInstance().setConstantCallMaxConcurrency(2);
94+
Semaphore sem = invokeFactory();
95+
96+
assertTrue(sem.tryAcquire());
97+
assertTrue(sem.tryAcquire());
98+
assertFalse("3rd acquire must fail when cap=2", sem.tryAcquire());
99+
100+
sem.release();
101+
assertTrue("permit must replenish after release", sem.tryAcquire());
102+
103+
sem.release();
104+
sem.release();
105+
}
106+
}

0 commit comments

Comments
 (0)