kopia lustrzana https://github.com/ryukoposting/Signal-Android
Enforce upper bound on MobileCoin/fiat entry.
rodzic
5daa027c10
commit
113393de8f
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
@ -51,11 +50,11 @@ public class CreatePaymentViewModel extends ViewModel {
|
|||
private final MutableLiveData<CharSequence> note;
|
||||
|
||||
private CreatePaymentViewModel(@NonNull PayeeParcelable payee, @Nullable CharSequence note) {
|
||||
this.payee = payee;
|
||||
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.payee = payee;
|
||||
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.requireMobileCoin(), s.getMoney().requireMobileCoin()));
|
||||
|
||||
if (payee.getPayee().hasRecipientId()) {
|
||||
isPaymentsSupportedByPayee = LiveDataUtil.mapAsync(new DefaultValueLiveData<>(payee.getPayee().requireRecipientId()), r -> {
|
||||
|
@ -146,10 +145,10 @@ public class CreatePaymentViewModel extends ViewModel {
|
|||
@NonNull AmountKeyboardGlyph glyph,
|
||||
@NonNull Currency currency)
|
||||
{
|
||||
String newFiatAmount = updateAmountString(context, inputState.getFiatAmount(), glyph, currency.getDefaultFractionDigits());
|
||||
FiatMoney newFiat = stringToFiatValueOrZero(newFiatAmount, currency);
|
||||
Money newMoney = OptionalUtil.flatMap(inputState.getExchangeRate(), e -> e.exchange(newFiat)).get();
|
||||
String newMoneyAmount;
|
||||
String newFiatAmount = updateAmountString(context, inputState.getFiatAmount(), glyph, currency.getDefaultFractionDigits());
|
||||
FiatMoney newFiat = stringToFiatValueOrZero(newFiatAmount, currency);
|
||||
Money newMoney = OptionalUtil.flatMap(inputState.getExchangeRate(), e -> e.exchange(newFiat)).get();
|
||||
String newMoneyAmount;
|
||||
|
||||
if (newFiatAmount.equals("0")) {
|
||||
newMoneyAmount = "0";
|
||||
|
@ -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,28 +168,37 @@ 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 {
|
||||
newFiatAmount = newFiat.transform(f -> FiatMoneyUtil.format(context.getResources(), f, FiatMoneyUtil.formatOptions().withDisplayTime(false).numberOnly())).or("0");
|
||||
newFiatAmount = newFiat.transform(f -> FiatMoneyUtil.format(context.getResources(), f, FiatMoneyUtil.formatOptions().withDisplayTime(false).numberOnly())).or("0");
|
||||
}
|
||||
|
||||
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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue