Enforce upper bound on MobileCoin/fiat entry.

fork-5.53.8
Alan Evans 2021-04-09 11:15:47 -03:00
rodzic 5daa027c10
commit 113393de8f
5 zmienionych plików z 49 dodań i 30 usunięć

Wyświetl plik

@ -137,7 +137,6 @@ import org.whispersystems.signalservice.api.payments.Money;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
@ -335,8 +334,8 @@ public final class MessageContentProcessor {
recipient.getId(),
message.getTimestamp(),
paymentNotification.getNote(),
Money.mobileCoin(BigDecimal.ZERO),
Money.mobileCoin(BigDecimal.ZERO),
Money.MobileCoin.ZERO,
Money.MobileCoin.ZERO,
paymentNotification.getReceipt());
} catch (PaymentDatabase.PublicKeyConflictException e) {
Log.w(TAG, "Ignoring payment with public key already in database");

Wyświetl plik

@ -34,13 +34,12 @@ import java.math.BigDecimal;
import java.util.Currency;
import java.util.Objects;
import static org.thoughtcrime.securesms.payments.create.InputTarget.FIAT_MONEY;
public class CreatePaymentViewModel extends ViewModel {
private static final String TAG = Log.tag(CreatePaymentViewModel.class);
private static final Money.MobileCoin AMOUNT_LOWER_BOUND_EXCLUSIVE = Money.mobileCoin(BigDecimal.ZERO);
private static final Money.MobileCoin AMOUNT_LOWER_BOUND_EXCLUSIVE = Money.MobileCoin.ZERO;
private static final Money.MobileCoin AMOUNT_UPPER_BOUND_EXCLUSIVE = Money.MobileCoin.MAX_VALUE;
private final LiveData<Money> spendableBalance;
private final LiveData<Boolean> isValidAmount;
@ -55,7 +54,7 @@ public class CreatePaymentViewModel extends ViewModel {
this.spendableBalance = Transformations.map(SignalStore.paymentsValues().liveMobileCoinBalance(), Balance::getTransferableAmount);
this.note = new MutableLiveData<>(note);
this.inputState = new Store<>(new InputState());
this.isValidAmount = LiveDataUtil.combineLatest(spendableBalance, inputState.getStateLiveData(), (b, s) -> validateAmount(b, s.getMoney()));
this.isValidAmount = LiveDataUtil.combineLatest(spendableBalance, inputState.getStateLiveData(), (b, s) -> validateAmount(b.requireMobileCoin(), s.getMoney().requireMobileCoin()));
if (payee.getPayee().hasRecipientId()) {
isPaymentsSupportedByPayee = LiveDataUtil.mapAsync(new DefaultValueLiveData<>(payee.getPayee().requireRecipientId()), r -> {
@ -157,6 +156,10 @@ public class CreatePaymentViewModel extends ViewModel {
newMoneyAmount = newMoney.toString(FormatterOptions.builder().withoutUnit().build());
}
if (!withinMobileCoinBounds(newMoney.requireMobileCoin())) {
return inputState;
}
return inputState.updateAmount(newMoneyAmount, newFiatAmount, newMoney, Optional.of(newFiat));
}
@ -165,10 +168,14 @@ public class CreatePaymentViewModel extends ViewModel {
@NonNull AmountKeyboardGlyph glyph)
{
String newMoneyAmount = updateAmountString(context, inputState.getMoneyAmount(), glyph, inputState.getMoney().getCurrency().getDecimalPrecision());
Money newMoney = stringToMobileCoinValueOrZero(newMoneyAmount);
Money.MobileCoin newMoney = stringToMobileCoinValueOrZero(newMoneyAmount);
Optional<FiatMoney> newFiat = OptionalUtil.flatMap(inputState.getExchangeRate(), e -> e.exchange(newMoney));
String newFiatAmount;
if (!withinMobileCoinBounds(newMoney)) {
return inputState;
}
if (newMoneyAmount.equals("0")) {
newFiatAmount = "0";
} else {
@ -178,15 +185,20 @@ public class CreatePaymentViewModel extends ViewModel {
return inputState.updateAmount(newMoneyAmount, newFiatAmount, newMoney, newFiat);
}
private boolean validateAmount(@NonNull Money spendableBalance, @NonNull Money amount) {
private boolean validateAmount(@NonNull Money.MobileCoin spendableBalance, @NonNull Money.MobileCoin amount) {
try {
return amount.requireMobileCoin().greaterThan(AMOUNT_LOWER_BOUND_EXCLUSIVE) &&
!amount.requireMobileCoin().greaterThan(spendableBalance.requireMobileCoin());
return amount.greaterThan(AMOUNT_LOWER_BOUND_EXCLUSIVE) &&
!amount.greaterThan(spendableBalance);
} catch (NumberFormatException exception) {
return false;
}
}
private static boolean withinMobileCoinBounds(@NonNull Money.MobileCoin amount) {
return !amount.lessThan(AMOUNT_LOWER_BOUND_EXCLUSIVE) &&
!amount.greaterThan(AMOUNT_UPPER_BOUND_EXCLUSIVE);
}
private @NonNull FiatMoney stringToFiatValueOrZero(@Nullable String string, @NonNull Currency currency) {
try {
if (string != null) return new FiatMoney(new BigDecimal(string), currency);
@ -195,12 +207,12 @@ public class CreatePaymentViewModel extends ViewModel {
return new FiatMoney(BigDecimal.ZERO, currency);
}
private @NonNull Money stringToMobileCoinValueOrZero(@Nullable String string) {
private @NonNull Money.MobileCoin stringToMobileCoinValueOrZero(@Nullable String string) {
try {
if (string != null) return Money.mobileCoin(new BigDecimal(string));
} catch (NumberFormatException ignored) { }
return Money.mobileCoin(BigDecimal.ZERO);
return Money.MobileCoin.ZERO;
}
public @NonNull CreatePaymentDetails getCreatePaymentDetails() {

Wyświetl plik

@ -7,9 +7,7 @@ import org.thoughtcrime.securesms.payments.currency.FiatMoney;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.payments.Money;
import java.math.BigDecimal;
class InputState {
final class InputState {
private final InputTarget inputTarget;
private final String moneyAmount;
private final String fiatAmount;
@ -18,7 +16,7 @@ class InputState {
private final Optional<CurrencyExchange.ExchangeRate> exchangeRate;
InputState() {
this(InputTarget.MONEY, "0", "0", Money.mobileCoin(BigDecimal.ZERO), Optional.absent(), Optional.absent());
this(InputTarget.MONEY, "0", "0", Money.MobileCoin.ZERO, Optional.absent(), Optional.absent());
}
private InputState(@NonNull InputTarget inputTarget,
@ -64,10 +62,6 @@ class InputState {
return new InputState(inputTarget, moneyAmount, fiatAmount, money, fiatMoney, exchangeRate);
}
@NonNull InputState updateFiatAmount(@NonNull String fiatAmount) {
return new InputState(inputTarget, moneyAmount, fiatAmount, money, fiatMoney, exchangeRate);
}
@NonNull InputState updateAmount(@NonNull String moneyAmount, @NonNull String fiatAmount, @NonNull Money money, @NonNull Optional<FiatMoney> fiatMoney) {
return new InputState(inputTarget, moneyAmount, fiatAmount, money, fiatMoney, exchangeRate);
}

Wyświetl plik

@ -5,6 +5,7 @@ import org.whispersystems.signalservice.api.util.Uint64Util;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
@ -97,6 +98,14 @@ public abstract class Money {
public static final Comparator<MobileCoin> DESCENDING = (x, y) -> y.amount.compareTo(x.amount);
public static final MobileCoin ZERO = new MobileCoin(BigInteger.ZERO);
public static final MobileCoin MAX_VALUE;
static {
byte[] bytes = new byte[8];
Arrays.fill(bytes, (byte) 0xff);
BigInteger max64Bit = new BigInteger(1, bytes);
MAX_VALUE = Money.picoMobileCoin(max64Bit);
}
private static final int PRECISION = 12;

Wyświetl plik

@ -336,6 +336,11 @@ public final class MoneyTest_MobileCoin {
assertSame(Money.MobileCoin.ZERO, mobileCoin.toZero());
}
@Test
public void max_long_value() {
assertEquals("MOB:18446744073709551615", Money.MobileCoin.MAX_VALUE.serialize());
}
private static Money.MobileCoin mobileCoin2(double value) {
return Money.mobileCoin(BigDecimal.valueOf(value));
}