This commit is contained in:
Andrey Litvitski 2025-03-11 12:21:09 -05:00 committed by GitHub
commit 92f5246b8f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -49,15 +49,13 @@ public class ExchangeRate {
/** /**
* Convert a coin amount to a fiat amount using this exchange rate. * Convert a coin amount to a fiat amount using this exchange rate.
* @throws ArithmeticException if the converted fiat amount is too high or too low.
*/ */
public Fiat coinToFiat(Coin convertCoin) { public Fiat coinToFiat(Coin convertCoin) {
// Use BigInteger because it's much easier to maintain full precision without overflowing. // Use BigInteger because it's much easier to maintain full precision without overflowing.
final BigInteger converted = BigInteger.valueOf(convertCoin.value).multiply(BigInteger.valueOf(fiat.value)) final BigInteger converted = convert(convertCoin.value, fiat.value, coin.value);
.divide(BigInteger.valueOf(coin.value));
if (converted.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0 checkOverflow(converted);
|| converted.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0)
throw new ArithmeticException("Overflow");
return Fiat.valueOf(fiat.currencyCode, converted.longValue()); return Fiat.valueOf(fiat.currencyCode, converted.longValue());
} }
@ -68,12 +66,12 @@ public class ExchangeRate {
public Coin fiatToCoin(Fiat convertFiat) { public Coin fiatToCoin(Fiat convertFiat) {
checkArgument(convertFiat.currencyCode.equals(fiat.currencyCode), () -> checkArgument(convertFiat.currencyCode.equals(fiat.currencyCode), () ->
"currency mismatch: " + convertFiat.currencyCode + " vs " + fiat.currencyCode); "currency mismatch: " + convertFiat.currencyCode + " vs " + fiat.currencyCode);
// Use BigInteger because it's much easier to maintain full precision without overflowing. // Use BigInteger because it's much easier to maintain full precision without overflowing.
final BigInteger converted = BigInteger.valueOf(convertFiat.value).multiply(BigInteger.valueOf(coin.value)) final BigInteger converted = convert(convertFiat.value, coin.value, fiat.value);
.divide(BigInteger.valueOf(fiat.value));
if (converted.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0 checkOverflow(converted);
|| converted.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0)
throw new ArithmeticException("Overflow");
try { try {
return Coin.valueOf(converted.longValue()); return Coin.valueOf(converted.longValue());
} catch (IllegalArgumentException x) { } catch (IllegalArgumentException x) {
@ -81,6 +79,25 @@ public class ExchangeRate {
} }
} }
/**
* Checks for overflow in the BigInteger value.
* @throws ArithmeticException if the value is outside the range of Long.MIN_VALUE to Long.MAX_VALUE.
*/
private void checkOverflow(BigInteger value) throws ArithmeticException {
if (value.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0
|| value.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0) {
throw new ArithmeticException("Overflow");
}
}
/**
* Common conversion logic.
*/
private BigInteger convert(long fromValue, long toValue, long targetValue) {
return BigInteger.valueOf(fromValue).multiply(BigInteger.valueOf(toValue))
.divide(BigInteger.valueOf(targetValue));
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;