|
18 | 18 | import com.google.protobuf.Any; |
19 | 19 | import com.google.protobuf.ByteString; |
20 | 20 | import com.google.protobuf.InvalidProtocolBufferException; |
| 21 | +import java.lang.reflect.Method; |
21 | 22 | import org.junit.Assert; |
22 | 23 | import org.junit.Before; |
23 | 24 | import org.junit.Test; |
@@ -300,6 +301,95 @@ public void testTriggerUseUsage() |
300 | 301 |
|
301 | 302 | } |
302 | 303 |
|
| 304 | + /** |
| 305 | + * Regression test for the {@code newSize == 0} guard in {@code resetAccountUsage}. |
| 306 | + * |
| 307 | + * <p>When {@code mergedSize == currentSize} and {@code size == 0}, {@code newSize} becomes 0. |
| 308 | + * Without the guard this would cause an {@link ArithmeticException} (divide by zero). |
| 309 | + * The expected outcome is {@code energyUsage = 0} with no exception thrown. |
| 310 | + */ |
| 311 | + @Test |
| 312 | + public void testResetAccountUsageZeroWindowSize() throws Exception { |
| 313 | + TriggerSmartContract contract = TriggerSmartContract.newBuilder() |
| 314 | + .setContractAddress(contractAddress) |
| 315 | + .setOwnerAddress(ownerAddress) |
| 316 | + .build(); |
| 317 | + Transaction transaction = Transaction.newBuilder() |
| 318 | + .setRawData(raw.newBuilder() |
| 319 | + .addContract(Contract.newBuilder() |
| 320 | + .setParameter(Any.pack(contract)) |
| 321 | + .setType(ContractType.TriggerSmartContract)) |
| 322 | + .build()) |
| 323 | + .build(); |
| 324 | + TransactionTrace trace = new TransactionTrace( |
| 325 | + new TransactionCapsule(transaction), StoreFactory.getInstance(), new RuntimeImpl()); |
| 326 | + |
| 327 | + AccountCapsule accountCap = new AccountCapsule(Account.newBuilder() |
| 328 | + .setAddress(ownerAddress) |
| 329 | + .setAccountResource(AccountResource.newBuilder() |
| 330 | + .setEnergyUsage(1000L) |
| 331 | + .build()) |
| 332 | + .build()); |
| 333 | + |
| 334 | + // A fresh account has EnergyWindowSize=0, so getWindowSize(ENERGY) returns the default |
| 335 | + // WINDOW_SIZE_MS / BLOCK_PRODUCED_INTERVAL = 28800. |
| 336 | + // Passing mergedSize == currentSize and size == 0 triggers newSize = 0. |
| 337 | + final long currentSize = 28800L; |
| 338 | + |
| 339 | + dbManager.getDynamicPropertiesStore().saveAllowCancelAllUnfreezeV2(0); |
| 340 | + Method method = TransactionTrace.class.getDeclaredMethod( |
| 341 | + "resetAccountUsage", |
| 342 | + AccountCapsule.class, long.class, long.class, long.class, long.class, long.class); |
| 343 | + method.setAccessible(true); |
| 344 | + method.invoke(trace, accountCap, 500L, 0L, 500L, currentSize, 0L); |
| 345 | + |
| 346 | + // newSize == 0 → guard returns 0 instead of dividing → energyUsage reset to 0 |
| 347 | + Assert.assertEquals(0L, accountCap.getEnergyUsage()); |
| 348 | + } |
| 349 | + |
| 350 | + /** |
| 351 | + * Same regression as above but exercises the {@code resetAccountUsageV2} code path, |
| 352 | + * which is activated when {@code supportAllowCancelAllUnfreezeV2} is enabled. |
| 353 | + */ |
| 354 | + @Test |
| 355 | + public void testResetAccountUsageV2ZeroWindowSize() throws Exception { |
| 356 | + TriggerSmartContract contract = TriggerSmartContract.newBuilder() |
| 357 | + .setContractAddress(contractAddress) |
| 358 | + .setOwnerAddress(ownerAddress) |
| 359 | + .build(); |
| 360 | + Transaction transaction = Transaction.newBuilder() |
| 361 | + .setRawData(raw.newBuilder() |
| 362 | + .addContract(Contract.newBuilder() |
| 363 | + .setParameter(Any.pack(contract)) |
| 364 | + .setType(ContractType.TriggerSmartContract)) |
| 365 | + .build()) |
| 366 | + .build(); |
| 367 | + TransactionTrace trace = new TransactionTrace( |
| 368 | + new TransactionCapsule(transaction), StoreFactory.getInstance(), new RuntimeImpl()); |
| 369 | + |
| 370 | + AccountCapsule accountCap = new AccountCapsule(Account.newBuilder() |
| 371 | + .setAddress(ownerAddress) |
| 372 | + .setAccountResource(AccountResource.newBuilder() |
| 373 | + .setEnergyUsage(1000L) |
| 374 | + .build()) |
| 375 | + .build()); |
| 376 | + |
| 377 | + final long currentSize = 28800L; |
| 378 | + |
| 379 | + dbManager.getDynamicPropertiesStore().saveAllowCancelAllUnfreezeV2(1); |
| 380 | + try { |
| 381 | + Method method = TransactionTrace.class.getDeclaredMethod( |
| 382 | + "resetAccountUsage", |
| 383 | + AccountCapsule.class, long.class, long.class, long.class, long.class, long.class); |
| 384 | + method.setAccessible(true); |
| 385 | + method.invoke(trace, accountCap, 500L, 0L, 500L, currentSize, 0L); |
| 386 | + |
| 387 | + Assert.assertEquals(0L, accountCap.getEnergyUsage()); |
| 388 | + } finally { |
| 389 | + dbManager.getDynamicPropertiesStore().saveAllowCancelAllUnfreezeV2(0); |
| 390 | + } |
| 391 | + } |
| 392 | + |
303 | 393 | private byte[] deployInit(Transaction transaction) |
304 | 394 | throws VMIllegalException, ContractExeException, |
305 | 395 | ContractValidateException, BalanceInsufficientException { |
|
0 commit comments