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 org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -335,8 +334,8 @@ public final class MessageContentProcessor {
recipient.getId(), recipient.getId(),
message.getTimestamp(), message.getTimestamp(),
paymentNotification.getNote(), paymentNotification.getNote(),
Money.mobileCoin(BigDecimal.ZERO), Money.MobileCoin.ZERO,
Money.mobileCoin(BigDecimal.ZERO), Money.MobileCoin.ZERO,
paymentNotification.getReceipt()); paymentNotification.getReceipt());
} catch (PaymentDatabase.PublicKeyConflictException e) { } catch (PaymentDatabase.PublicKeyConflictException e) {
Log.w(TAG, "Ignoring payment with public key already in database"); 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.Currency;
import java.util.Objects; import java.util.Objects;
import static org.thoughtcrime.securesms.payments.create.InputTarget.FIAT_MONEY;
public class CreatePaymentViewModel extends ViewModel { public class CreatePaymentViewModel extends ViewModel {
private static final String TAG = Log.tag(CreatePaymentViewModel.class); 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<Money> spendableBalance;
private final LiveData<Boolean> isValidAmount; private final LiveData<Boolean> isValidAmount;
@ -55,7 +54,7 @@ public class CreatePaymentViewModel extends ViewModel {
this.spendableBalance = Transformations.map(SignalStore.paymentsValues().liveMobileCoinBalance(), Balance::getTransferableAmount); this.spendableBalance = Transformations.map(SignalStore.paymentsValues().liveMobileCoinBalance(), Balance::getTransferableAmount);
this.note = new MutableLiveData<>(note); this.note = new MutableLiveData<>(note);
this.inputState = new Store<>(new InputState()); 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()) { if (payee.getPayee().hasRecipientId()) {
isPaymentsSupportedByPayee = LiveDataUtil.mapAsync(new DefaultValueLiveData<>(payee.getPayee().requireRecipientId()), r -> { 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()); newMoneyAmount = newMoney.toString(FormatterOptions.builder().withoutUnit().build());
} }
if (!withinMobileCoinBounds(newMoney.requireMobileCoin())) {
return inputState;
}
return inputState.updateAmount(newMoneyAmount, newFiatAmount, newMoney, Optional.of(newFiat)); return inputState.updateAmount(newMoneyAmount, newFiatAmount, newMoney, Optional.of(newFiat));
} }
@ -165,10 +168,14 @@ public class CreatePaymentViewModel extends ViewModel {
@NonNull AmountKeyboardGlyph glyph) @NonNull AmountKeyboardGlyph glyph)
{ {
String newMoneyAmount = updateAmountString(context, inputState.getMoneyAmount(), glyph, inputState.getMoney().getCurrency().getDecimalPrecision()); 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)); Optional<FiatMoney> newFiat = OptionalUtil.flatMap(inputState.getExchangeRate(), e -> e.exchange(newMoney));
String newFiatAmount; String newFiatAmount;
if (!withinMobileCoinBounds(newMoney)) {
return inputState;
}
if (newMoneyAmount.equals("0")) { if (newMoneyAmount.equals("0")) {
newFiatAmount = "0"; newFiatAmount = "0";
} else { } else {
@ -178,15 +185,20 @@ public class CreatePaymentViewModel extends ViewModel {
return inputState.updateAmount(newMoneyAmount, newFiatAmount, newMoney, newFiat); 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 { try {
return amount.requireMobileCoin().greaterThan(AMOUNT_LOWER_BOUND_EXCLUSIVE) && return amount.greaterThan(AMOUNT_LOWER_BOUND_EXCLUSIVE) &&
!amount.requireMobileCoin().greaterThan(spendableBalance.requireMobileCoin()); !amount.greaterThan(spendableBalance);
} catch (NumberFormatException exception) { } catch (NumberFormatException exception) {
return false; 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) { private @NonNull FiatMoney stringToFiatValueOrZero(@Nullable String string, @NonNull Currency currency) {
try { try {
if (string != null) return new FiatMoney(new BigDecimal(string), currency); 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); return new FiatMoney(BigDecimal.ZERO, currency);
} }
private @NonNull Money stringToMobileCoinValueOrZero(@Nullable String string) { private @NonNull Money.MobileCoin stringToMobileCoinValueOrZero(@Nullable String string) {
try { try {
if (string != null) return Money.mobileCoin(new BigDecimal(string)); if (string != null) return Money.mobileCoin(new BigDecimal(string));
} catch (NumberFormatException ignored) { } } catch (NumberFormatException ignored) { }
return Money.mobileCoin(BigDecimal.ZERO); return Money.MobileCoin.ZERO;
} }
public @NonNull CreatePaymentDetails getCreatePaymentDetails() { 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.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.payments.Money; import org.whispersystems.signalservice.api.payments.Money;
import java.math.BigDecimal; final class InputState {
class InputState {
private final InputTarget inputTarget; private final InputTarget inputTarget;
private final String moneyAmount; private final String moneyAmount;
private final String fiatAmount; private final String fiatAmount;
@ -18,7 +16,7 @@ class InputState {
private final Optional<CurrencyExchange.ExchangeRate> exchangeRate; private final Optional<CurrencyExchange.ExchangeRate> exchangeRate;
InputState() { 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, private InputState(@NonNull InputTarget inputTarget,
@ -64,10 +62,6 @@ class InputState {
return new InputState(inputTarget, moneyAmount, fiatAmount, money, fiatMoney, exchangeRate); 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) { @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); 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.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; 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 Comparator<MobileCoin> DESCENDING = (x, y) -> y.amount.compareTo(x.amount);
public static final MobileCoin ZERO = new MobileCoin(BigInteger.ZERO); 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; private static final int PRECISION = 12;

Wyświetl plik

@ -336,6 +336,11 @@ public final class MoneyTest_MobileCoin {
assertSame(Money.MobileCoin.ZERO, mobileCoin.toZero()); 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) { private static Money.MobileCoin mobileCoin2(double value) {
return Money.mobileCoin(BigDecimal.valueOf(value)); return Money.mobileCoin(BigDecimal.valueOf(value));
} }