Skip to content

Commit 2e97653

Browse files
committed
fix: harden resource window calculations
1 parent 82d1639 commit 2e97653

4 files changed

Lines changed: 203 additions & 25 deletions

File tree

actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -925,15 +925,18 @@ private long increase(long lastUsage, long usage, long lastTime, long now, long
925925

926926
if (lastTime != now) {
927927
assert now > lastTime;
928-
if (lastTime + windowSize > now) {
929-
long delta = now - lastTime;
930-
double decay = (windowSize - delta) / (double) windowSize;
931-
averageLastUsage = round(averageLastUsage * decay, VMConfig.disableJavaLangMath());
928+
if (windowNotExpired(lastTime, now, windowSize)) {
929+
long delta = elapsedTime(now, lastTime);
930+
averageLastUsage = decayAverageUsage(averageLastUsage, windowSize - delta, windowSize);
932931
} else {
933932
averageLastUsage = 0;
934933
}
935934
}
936-
averageLastUsage += averageUsage;
935+
if (hardenResourceCalculation()) {
936+
averageLastUsage = StrictMathWrapper.addExact(averageLastUsage, averageUsage);
937+
} else {
938+
averageLastUsage += averageUsage;
939+
}
937940
return getUsage(averageLastUsage, windowSize);
938941
}
939942

@@ -950,6 +953,36 @@ private long divideCeilExact(BigInteger numerator, BigInteger denominator) {
950953
return result;
951954
}
952955

956+
private long decayAverageUsage(long averageUsage, long remainWindowSize, long windowSize) {
957+
if (hardenResourceCalculation()) {
958+
BigInteger denominator = BigInteger.valueOf(windowSize);
959+
BigInteger[] divRem = BigInteger.valueOf(averageUsage)
960+
.multiply(BigInteger.valueOf(remainWindowSize))
961+
.divideAndRemainder(denominator);
962+
BigInteger result = divRem[0];
963+
if (divRem[1].shiftLeft(1).compareTo(denominator) >= 0) {
964+
result = result.add(BigInteger.ONE);
965+
}
966+
return result.longValueExact();
967+
}
968+
double decay = remainWindowSize / (double) windowSize;
969+
return round(averageUsage * decay, VMConfig.disableJavaLangMath());
970+
}
971+
972+
private boolean windowNotExpired(long lastTime, long now, long windowSize) {
973+
if (hardenResourceCalculation()) {
974+
return StrictMathWrapper.addExact(lastTime, windowSize) > now;
975+
}
976+
return lastTime + windowSize > now;
977+
}
978+
979+
private long elapsedTime(long now, long lastTime) {
980+
if (hardenResourceCalculation()) {
981+
return StrictMathWrapper.subtractExact(now, lastTime);
982+
}
983+
return now - lastTime;
984+
}
985+
953986
private long getUsage(long usage, long windowSize) {
954987
if (hardenResourceCalculation()) {
955988
return BigInteger.valueOf(usage)

chainbase/src/main/java/org/tron/core/db/ResourceProcessor.java

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,18 @@ protected long increase(long lastUsage, long usage, long lastTime, long now, lon
6464

6565
if (lastTime != now) {
6666
assert now > lastTime;
67-
if (lastTime + windowSize > now) {
68-
long delta = now - lastTime;
69-
double decay = (windowSize - delta) / (double) windowSize;
70-
averageLastUsage = round(averageLastUsage * decay,
71-
this.disableJavaLangMath());
67+
if (windowNotExpired(lastTime, now, windowSize)) {
68+
long delta = elapsedTime(now, lastTime);
69+
averageLastUsage = decayAverageUsage(averageLastUsage, windowSize - delta, windowSize);
7270
} else {
7371
averageLastUsage = 0;
7472
}
7573
}
76-
averageLastUsage += averageUsage;
74+
if (hardenCalculation()) {
75+
averageLastUsage = StrictMathWrapper.addExact(averageLastUsage, averageUsage);
76+
} else {
77+
averageLastUsage += averageUsage;
78+
}
7779
return getUsage(averageLastUsage, windowSize);
7880
}
7981

@@ -105,11 +107,10 @@ public long increase(AccountCapsule accountCapsule, ResourceCode resourceCode,
105107
}
106108

107109
if (lastTime != now) {
108-
if (lastTime + oldWindowSize > now) {
109-
long delta = now - lastTime;
110-
double decay = (oldWindowSize - delta) / (double) oldWindowSize;
111-
averageLastUsage = round(averageLastUsage * decay,
112-
this.disableJavaLangMath());
110+
if (windowNotExpired(lastTime, now, oldWindowSize)) {
111+
long delta = elapsedTime(now, lastTime);
112+
averageLastUsage = decayAverageUsage(averageLastUsage, oldWindowSize - delta,
113+
oldWindowSize);
113114
} else {
114115
averageLastUsage = 0;
115116
}
@@ -122,7 +123,7 @@ public long increase(AccountCapsule accountCapsule, ResourceCode resourceCode,
122123
accountCapsule.setNewWindowSize(resourceCode, this.windowSize);
123124
return newUsage;
124125
}
125-
long remainWindowSize = oldWindowSize - (now - lastTime);
126+
long remainWindowSize = remainWindowSize(oldWindowSize, now, lastTime);
126127
long newWindowSize = getNewWindowSize(remainUsage, remainWindowSize, usage,
127128
windowSize, newUsage);
128129
accountCapsule.setNewWindowSize(resourceCode, newWindowSize);
@@ -150,11 +151,10 @@ public long increaseV2(AccountCapsule accountCapsule, ResourceCode resourceCode,
150151
}
151152

152153
if (lastTime != now) {
153-
if (lastTime + oldWindowSize > now) {
154-
long delta = now - lastTime;
155-
double decay = (oldWindowSize - delta) / (double) oldWindowSize;
156-
averageLastUsage = round(averageLastUsage * decay,
157-
this.disableJavaLangMath());
154+
if (windowNotExpired(lastTime, now, oldWindowSize)) {
155+
long delta = elapsedTime(now, lastTime);
156+
averageLastUsage = decayAverageUsage(averageLastUsage, oldWindowSize - delta,
157+
oldWindowSize);
158158
} else {
159159
averageLastUsage = 0;
160160
}
@@ -167,7 +167,7 @@ public long increaseV2(AccountCapsule accountCapsule, ResourceCode resourceCode,
167167
return newUsage;
168168
}
169169

170-
long remainWindowSize = oldWindowSizeV2 - (now - lastTime) * WINDOW_SIZE_PRECISION;
170+
long remainWindowSize = remainWindowSizeV2(oldWindowSizeV2, now, lastTime);
171171
long newWindowSize;
172172
if (hardenCalculation()) {
173173
BigInteger biNewWindowSize = BigInteger.valueOf(remainUsage)
@@ -203,7 +203,9 @@ public void unDelegateIncrease(AccountCapsule owner, final AccountCapsule receiv
203203
remainOwnerWindowSize = remainOwnerWindowSize < 0 ? 0 : remainOwnerWindowSize;
204204
remainReceiverWindowSize = remainReceiverWindowSize < 0 ? 0 : remainReceiverWindowSize;
205205

206-
long newOwnerUsage = ownerUsage + transferUsage;
206+
long newOwnerUsage = hardenCalculation()
207+
? StrictMathWrapper.addExact(ownerUsage, transferUsage)
208+
: ownerUsage + transferUsage;
207209
// mean ownerUsage == 0 and transferUsage == 0
208210
if (newOwnerUsage == 0) {
209211
owner.setNewWindowSize(resourceCode, this.windowSize);
@@ -225,7 +227,9 @@ public void unDelegateIncreaseV2(AccountCapsule owner, final AccountCapsule rece
225227
long ownerUsage = owner.getUsage(resourceCode);
226228
// Update itself first
227229
ownerUsage = increase(owner, resourceCode, ownerUsage, 0, lastOwnerTime, now);
228-
long newOwnerUsage = ownerUsage + transferUsage;
230+
long newOwnerUsage = hardenCalculation()
231+
? StrictMathWrapper.addExact(ownerUsage, transferUsage)
232+
: ownerUsage + transferUsage;
229233
// mean ownerUsage == 0 and transferUsage == 0
230234
if (newOwnerUsage == 0) {
231235
owner.setNewWindowSizeV2(resourceCode, this.windowSize * WINDOW_SIZE_PRECISION);
@@ -282,6 +286,53 @@ private long divideCeilExact(BigInteger numerator, BigInteger denominator) {
282286
return result;
283287
}
284288

289+
private long decayAverageUsage(long averageUsage, long remainWindowSize, long windowSize) {
290+
if (hardenCalculation()) {
291+
BigInteger denominator = BigInteger.valueOf(windowSize);
292+
BigInteger[] divRem = BigInteger.valueOf(averageUsage)
293+
.multiply(BigInteger.valueOf(remainWindowSize))
294+
.divideAndRemainder(denominator);
295+
BigInteger result = divRem[0];
296+
if (divRem[1].shiftLeft(1).compareTo(denominator) >= 0) {
297+
result = result.add(BigInteger.ONE);
298+
}
299+
return result.longValueExact();
300+
}
301+
double decay = remainWindowSize / (double) windowSize;
302+
return round(averageUsage * decay, this.disableJavaLangMath());
303+
}
304+
305+
private boolean windowNotExpired(long lastTime, long now, long windowSize) {
306+
if (hardenCalculation()) {
307+
return StrictMathWrapper.addExact(lastTime, windowSize) > now;
308+
}
309+
return lastTime + windowSize > now;
310+
}
311+
312+
private long elapsedTime(long now, long lastTime) {
313+
if (hardenCalculation()) {
314+
return StrictMathWrapper.subtractExact(now, lastTime);
315+
}
316+
return now - lastTime;
317+
}
318+
319+
private long remainWindowSize(long windowSize, long now, long lastTime) {
320+
if (hardenCalculation()) {
321+
return StrictMathWrapper.subtractExact(windowSize, elapsedTime(now, lastTime));
322+
}
323+
return windowSize - (now - lastTime);
324+
}
325+
326+
private long remainWindowSizeV2(long windowSizeV2, long now, long lastTime) {
327+
if (hardenCalculation()) {
328+
BigInteger remain = BigInteger.valueOf(windowSizeV2)
329+
.subtract(BigInteger.valueOf(elapsedTime(now, lastTime))
330+
.multiply(BigInteger.valueOf(WINDOW_SIZE_PRECISION)));
331+
return remain.longValueExact();
332+
}
333+
return windowSizeV2 - (now - lastTime) * WINDOW_SIZE_PRECISION;
334+
}
335+
285336
private long getUsage(long usage, long windowSize) {
286337
if (hardenCalculation()) {
287338
return BigInteger.valueOf(usage).multiply(BigInteger.valueOf(windowSize))

framework/src/test/java/org/tron/core/db/ResourceProcessorHardenTest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,46 @@ public void testIncreaseOverflowDetectedWithHardening() {
117117
() -> processor.increase(lastUsage, usage, lastTime, now, windowSize));
118118
}
119119

120+
@Test
121+
public void testIncreaseAverageSumOverflowDetectedWithHardening() {
122+
long lastUsage = 150_000_000_000_000_000L;
123+
long usage = 150_000_000_000_000_000L;
124+
long now = 9995L;
125+
long windowSize = 28800L;
126+
127+
dbManager.getDynamicPropertiesStore().saveAllowHardenResourceCalculation(1);
128+
129+
Assert.assertThrows(ArithmeticException.class,
130+
() -> processor.increase(lastUsage, usage, now, now, windowSize));
131+
}
132+
133+
@Test
134+
public void testIncreaseDecayUsesExactRoundingWithHardening() {
135+
long lastUsage = 250_000_000_000_000_000L;
136+
long usage = 0L;
137+
long now = 9995L;
138+
long windowSize = 28800L;
139+
140+
dbManager.getDynamicPropertiesStore().saveAllowHardenResourceCalculation(1);
141+
142+
long result = processor.increase(lastUsage, usage, now - 1, now, windowSize);
143+
Assert.assertEquals(249_991_319_444_444_444L, result);
144+
}
145+
146+
@Test
147+
public void testIncreaseWindowBoundaryOverflowDetectedWithHardening() {
148+
long lastUsage = 1000L;
149+
long usage = 0L;
150+
long lastTime = Long.MAX_VALUE - 10;
151+
long now = Long.MAX_VALUE - 5;
152+
long windowSize = 28800L;
153+
154+
dbManager.getDynamicPropertiesStore().saveAllowHardenResourceCalculation(1);
155+
156+
Assert.assertThrows(ArithmeticException.class,
157+
() -> processor.increase(lastUsage, usage, lastTime, now, windowSize));
158+
}
159+
120160
@Test
121161
public void testIncreaseOverflowSilentWithoutHardening() {
122162
long lastUsage = Long.MAX_VALUE / 10;
@@ -223,6 +263,20 @@ public void testUnDelegateIncreaseV2ConsistentWithHardening() {
223263
Assert.assertEquals(windowOld, windowNew);
224264
}
225265

266+
@Test
267+
public void testUnDelegateIncreaseV2UsageOverflowDetected() {
268+
dbManager.getDynamicPropertiesStore().saveUnfreezeDelayDays(14);
269+
dbManager.getDynamicPropertiesStore().saveAllowCancelAllUnfreezeV2(1);
270+
dbManager.getDynamicPropertiesStore().saveAllowHardenResourceCalculation(1);
271+
272+
long now = 10000L;
273+
setupForUnDelegateWithUsage(now, 1L, 1L);
274+
275+
Assert.assertThrows(ArithmeticException.class,
276+
() -> processor.unDelegateIncreaseV2(ownerCapsule, receiverCapsule,
277+
Long.MAX_VALUE, ResourceCode.ENERGY, now));
278+
}
279+
226280
@Test
227281
public void testIncreaseV2OverflowDetected() {
228282
dbManager.getDynamicPropertiesStore().saveUnfreezeDelayDays(14);

framework/src/test/java/org/tron/core/vm/repository/RepositoryImplHardenTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,46 @@ public void testIncreaseOverflowDetectedWithHardening() {
133133
() -> invokeIncrease(lastUsage, usage, lastTime, now, windowSize));
134134
}
135135

136+
@Test
137+
public void testIncreaseAverageSumOverflowDetectedWithHardening() {
138+
long lastUsage = 150_000_000_000_000_000L;
139+
long usage = 150_000_000_000_000_000L;
140+
long now = 9995L;
141+
long windowSize = 28800L;
142+
143+
VMConfig.initAllowHardenResourceCalculation(1);
144+
145+
Assert.assertThrows(ArithmeticException.class,
146+
() -> invokeIncrease(lastUsage, usage, now, now, windowSize));
147+
}
148+
149+
@Test
150+
public void testIncreaseDecayUsesExactRoundingWithHardening() throws Exception {
151+
long lastUsage = 250_000_000_000_000_000L;
152+
long usage = 0L;
153+
long now = 9995L;
154+
long windowSize = 28800L;
155+
156+
VMConfig.initAllowHardenResourceCalculation(1);
157+
158+
long result = invokeIncrease(lastUsage, usage, now - 1, now, windowSize);
159+
Assert.assertEquals(249_991_319_444_444_444L, result);
160+
}
161+
162+
@Test
163+
public void testIncreaseWindowBoundaryOverflowDetectedWithHardening() {
164+
long lastUsage = 1000L;
165+
long usage = 0L;
166+
long lastTime = Long.MAX_VALUE - 10;
167+
long now = Long.MAX_VALUE - 5;
168+
long windowSize = 28800L;
169+
170+
VMConfig.initAllowHardenResourceCalculation(1);
171+
172+
Assert.assertThrows(ArithmeticException.class,
173+
() -> invokeIncrease(lastUsage, usage, lastTime, now, windowSize));
174+
}
175+
136176
@Test
137177
public void testIncreaseOverflowSilentWithoutHardening() throws Exception {
138178
long lastUsage = Long.MAX_VALUE / 10;

0 commit comments

Comments
 (0)