Fix long overflow in Block.distributeBlockRewardToQoraHolders()

Sadly no native 128bit integer support in Java 11 so resorting to using
BigInteger.

Added/improved unit tests to cover.
This commit is contained in:
catbref
2020-05-07 16:37:40 +01:00
parent c7c419a3cd
commit 6d8f41ab05
5 changed files with 120 additions and 11 deletions

View File

@@ -1693,6 +1693,8 @@ public class Block {
final boolean isProcessingNotOrphaning = totalAmount >= 0;
long qoraPerQortReward = BlockChain.getInstance().getQoraPerQortReward();
BigInteger qoraPerQortRewardBI = BigInteger.valueOf(qoraPerQortReward);
List<AccountBalanceData> qoraHolders = this.repository.getAccountRepository().getEligibleLegacyQoraHolders(isProcessingNotOrphaning ? null : this.blockData.getHeight());
long totalQoraHeld = 0;
@@ -1706,10 +1708,18 @@ public class Block {
if (totalQoraHeld <= 0)
return sharedAmount;
// Could do with a faster 128bit integer library, but until then...
BigInteger qoraHoldersAmountBI = BigInteger.valueOf(qoraHoldersAmount);
BigInteger totalQoraHeldBI = BigInteger.valueOf(totalQoraHeld);
for (int h = 0; h < qoraHolders.size(); ++h) {
AccountBalanceData qoraHolder = qoraHolders.get(h);
BigInteger qoraHolderBalanceBI = BigInteger.valueOf(qoraHolder.getBalance());
// This is where a 128bit integer library could help:
// long holderReward = (qoraHoldersAmount * qoraHolder.getBalance()) / totalQoraHeld;
long holderReward = qoraHoldersAmountBI.multiply(qoraHolderBalanceBI).divide(totalQoraHeldBI).longValue();
long holderReward = (qoraHoldersAmount * qoraHolder.getBalance()) / totalQoraHeld;
long finalHolderReward = holderReward;
LOGGER.trace(() -> String.format("QORA holder %s has %s / %s QORA so share: %s",
qoraHolder.getAddress(), Amounts.prettyAmount(qoraHolder.getBalance()), finalTotalQoraHeld, Amounts.prettyAmount(finalHolderReward)));
@@ -1724,7 +1734,7 @@ public class Block {
// If processing, make sure we don't overpay
if (isProcessingNotOrphaning) {
long maxQortFromQora = qoraHolder.getBalance() / qoraPerQortReward;
long maxQortFromQora = Amounts.scaledDivide(qoraHolderBalanceBI, qoraPerQortRewardBI);
if (newQortFromQoraBalance >= maxQortFromQora) {
// Reduce final QORT-from-QORA payment to match max

View File

@@ -64,8 +64,12 @@ public abstract class Amounts {
return roundDownScaledMultiply(BigInteger.valueOf(multiplicand), BigInteger.valueOf(multiplier));
}
public static long scaledDivide(BigInteger dividend, BigInteger divisor) {
return dividend.multiply(Amounts.MULTIPLIER_BI).divide(divisor).longValue();
}
public static long scaledDivide(long dividend, long divisor) {
return BigInteger.valueOf(dividend).multiply(Amounts.MULTIPLIER_BI).divide(BigInteger.valueOf(divisor)).longValue();
return scaledDivide(BigInteger.valueOf(dividend), BigInteger.valueOf(divisor));
}
}