diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/PaymentDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/PaymentDatabase.java index 9f295d40bb..1335a10448 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/PaymentDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/PaymentDatabase.java @@ -12,6 +12,7 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import com.google.protobuf.InvalidProtocolBufferException; +import com.mobilecoin.lib.exceptions.SerializationException; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; @@ -30,6 +31,7 @@ import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.CursorUtil; import org.thoughtcrime.securesms.util.SqlUtil; import org.thoughtcrime.securesms.util.livedata.LiveDataUtil; +import org.whispersystems.signalservice.api.InvalidMessageStructureException; import org.whispersystems.signalservice.api.payments.Money; import java.util.Arrays; @@ -105,7 +107,7 @@ public final class PaymentDatabase extends Database { @NonNull Money amount, @NonNull Money fee, @NonNull byte[] receipt) - throws PublicKeyConflictException + throws PublicKeyConflictException, SerializationException { create(uuid, fromRecipient, null, timestamp, 0, note, Direction.RECEIVED, State.SUBMITTED, amount, fee, null, receipt, null, false); } @@ -122,7 +124,9 @@ public final class PaymentDatabase extends Database { create(uuid, toRecipient, publicAddress, timestamp, 0, note, Direction.SENT, State.INITIAL, amount, amount.toZero(), null, null, null, true); } catch (PublicKeyConflictException e) { Log.w(TAG, "Tried to create payment but the public key appears already in the database", e); - throw new AssertionError(e); + throw new IllegalArgumentException(e); + } catch (SerializationException e) { + throw new IllegalArgumentException(e); } } @@ -142,6 +146,7 @@ public final class PaymentDatabase extends Database { @NonNull Money fee, @NonNull byte[] receipt, @NonNull PaymentMetaData metaData) + throws SerializationException { try { create(uuid, toRecipient, publicAddress, timestamp, blockIndex, note, Direction.SENT, State.SUCCESSFUL, amount, fee, null, receipt, metaData, true); @@ -165,6 +170,8 @@ public final class PaymentDatabase extends Database { } catch (PublicKeyConflictException e) { Log.w(TAG, "Tried to create payment but the public key appears already in the database", e); throw new AssertionError(e); + } catch (SerializationException e) { + throw new IllegalArgumentException(e); } } @@ -183,7 +190,7 @@ public final class PaymentDatabase extends Database { @Nullable byte[] receipt, @Nullable PaymentMetaData metaData, boolean seen) - throws PublicKeyConflictException + throws PublicKeyConflictException, SerializationException { if (recipientId == null && publicAddress == null) { throw new AssertionError(); @@ -403,8 +410,12 @@ public final class PaymentDatabase extends Database { values.put(STATE, State.SUBMITTED.serialize()); values.put(TRANSACTION, transaction); values.put(RECEIPT, receipt); - values.put(PUBLIC_KEY, Base64.encodeBytes(PaymentMetaDataUtil.receiptPublic(PaymentMetaDataUtil.fromReceipt(receipt)))); - values.put(META_DATA, PaymentMetaDataUtil.fromReceiptAndTransaction(receipt, transaction).toByteArray()); + try { + values.put(PUBLIC_KEY, Base64.encodeBytes(PaymentMetaDataUtil.receiptPublic(PaymentMetaDataUtil.fromReceipt(receipt)))); + values.put(META_DATA, PaymentMetaDataUtil.fromReceiptAndTransaction(receipt, transaction).toByteArray()); + } catch (SerializationException e) { + throw new IllegalArgumentException(e); + } values.put(FEE, CryptoValueUtil.moneyToCryptoValue(fee).toByteArray()); database.beginTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/PaymentMetaDataUtil.java b/app/src/main/java/org/thoughtcrime/securesms/database/PaymentMetaDataUtil.java index c73fd2887d..5cf93cab71 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/PaymentMetaDataUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/PaymentMetaDataUtil.java @@ -23,11 +23,11 @@ public final class PaymentMetaDataUtil { try { return PaymentMetaData.parseFrom(requireBlob); } catch (InvalidProtocolBufferException e) { - throw new AssertionError(e); + throw new IllegalStateException(e); } } - public static @NonNull PaymentMetaData fromReceipt(@Nullable byte[] receipt) { + public static @NonNull PaymentMetaData fromReceipt(@Nullable byte[] receipt) throws SerializationException { PaymentMetaData.MobileCoinTxoIdentification.Builder builder = PaymentMetaData.MobileCoinTxoIdentification.newBuilder(); if (receipt != null) { @@ -46,7 +46,7 @@ public final class PaymentMetaDataUtil { return PaymentMetaData.newBuilder().setMobileCoinTxoIdentification(builder).build(); } - public static @NonNull PaymentMetaData fromReceiptAndTransaction(@Nullable byte[] receipt, @Nullable byte[] transaction) { + public static @NonNull PaymentMetaData fromReceiptAndTransaction(@Nullable byte[] receipt, @Nullable byte[] transaction) throws SerializationException { PaymentMetaData.MobileCoinTxoIdentification.Builder builder = PaymentMetaData.MobileCoinTxoIdentification.newBuilder(); if (transaction != null) { @@ -58,27 +58,19 @@ public final class PaymentMetaDataUtil { return PaymentMetaData.newBuilder().setMobileCoinTxoIdentification(builder).build(); } - private static void addReceiptData(@NonNull byte[] receipt, PaymentMetaData.MobileCoinTxoIdentification.Builder builder) { - try { - RistrettoPublic publicKey = Receipt.fromBytes(receipt).getPublicKey(); - addPublicKey(builder, publicKey); - } catch (SerializationException e) { - throw new AssertionError(e); - } + private static void addReceiptData(@NonNull byte[] receipt, PaymentMetaData.MobileCoinTxoIdentification.Builder builder) throws SerializationException { + RistrettoPublic publicKey = Receipt.fromBytes(receipt).getPublicKey(); + addPublicKey(builder, publicKey); } - private static void addTransactionData(@NonNull byte[] transactionBytes, PaymentMetaData.MobileCoinTxoIdentification.Builder builder) { - try { - Transaction transaction = Transaction.fromBytes(transactionBytes); - Set keyImages = transaction.getKeyImages(); - for (KeyImage keyImage : keyImages) { - builder.addKeyImages(ByteString.copyFrom(keyImage.getData())); - } - for (RistrettoPublic publicKey : transaction.getOutputPublicKeys()) { - addPublicKey(builder, publicKey); - } - } catch (SerializationException e) { - throw new AssertionError(e); + private static void addTransactionData(@NonNull byte[] transactionBytes, PaymentMetaData.MobileCoinTxoIdentification.Builder builder) throws SerializationException { + Transaction transaction = Transaction.fromBytes(transactionBytes); + Set keyImages = transaction.getKeyImages(); + for (KeyImage keyImage : keyImages) { + builder.addKeyImages(ByteString.copyFrom(keyImage.getData())); + } + for (RistrettoPublic publicKey : transaction.getOutputPublicKeys()) { + addPublicKey(builder, publicKey); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java index a9e80da06d..d68128dc2d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -10,6 +10,7 @@ import androidx.annotation.Nullable; import com.annimon.stream.Collectors; import com.annimon.stream.Stream; +import com.mobilecoin.lib.exceptions.SerializationException; import org.signal.core.util.logging.Log; import org.signal.ringrtc.CallId; @@ -309,7 +310,7 @@ public final class MessageContentProcessor { else if (syncMessage.getBlockedList().isPresent()) handleSynchronizeBlockedListMessage(syncMessage.getBlockedList().get()); else if (syncMessage.getFetchType().isPresent()) handleSynchronizeFetchMessage(syncMessage.getFetchType().get()); else if (syncMessage.getMessageRequestResponse().isPresent()) handleSynchronizeMessageRequestResponse(syncMessage.getMessageRequestResponse().get()); - else if (syncMessage.getOutgoingPaymentMessage().isPresent()) handleSynchronizeOutgoingPayment(syncMessage.getOutgoingPaymentMessage().get()); + else if (syncMessage.getOutgoingPaymentMessage().isPresent()) handleSynchronizeOutgoingPayment(content, syncMessage.getOutgoingPaymentMessage().get()); else warn(String.valueOf(content.getTimestamp()), "Contains no known sync types..."); } else if (content.getCallMessage().isPresent()) { log(String.valueOf(content.getTimestamp()), "Got call message..."); @@ -410,6 +411,8 @@ public final class MessageContentProcessor { } catch (PaymentDatabase.PublicKeyConflictException e) { warn(content.getTimestamp(), "Ignoring payment with public key already in database"); return; + } catch (SerializationException e) { + warn(content.getTimestamp(), "Ignoring payment with bad data.", e); } ApplicationDependencies.getJobManager() @@ -1016,7 +1019,7 @@ public final class MessageContentProcessor { } } - private void handleSynchronizeOutgoingPayment(@NonNull OutgoingPaymentMessage outgoingPaymentMessage) { + private void handleSynchronizeOutgoingPayment(@NonNull SignalServiceContent content, @NonNull OutgoingPaymentMessage outgoingPaymentMessage) { RecipientId recipientId = outgoingPaymentMessage.getRecipient() .transform(uuid -> RecipientId.from(uuid, null)) .orNull(); @@ -1027,23 +1030,27 @@ public final class MessageContentProcessor { Optional address = outgoingPaymentMessage.getAddress().transform(MobileCoinPublicAddress::fromBytes); if (!address.isPresent() && recipientId == null) { - log("Inserting defrag"); + log(content.getTimestamp(), "Inserting defrag"); address = Optional.of(ApplicationDependencies.getPayments().getWallet().getMobileCoinPublicAddress()); recipientId = Recipient.self().getId(); } UUID uuid = UUID.randomUUID(); - DatabaseFactory.getPaymentDatabase(context) - .createSuccessfulPayment(uuid, - recipientId, - address.get(), - timestamp, - outgoingPaymentMessage.getBlockIndex(), - outgoingPaymentMessage.getNote().or(""), - outgoingPaymentMessage.getAmount(), - outgoingPaymentMessage.getFee(), - outgoingPaymentMessage.getReceipt().toByteArray(), - PaymentMetaDataUtil.fromKeysAndImages(outgoingPaymentMessage.getPublicKeys(), outgoingPaymentMessage.getKeyImages())); + try { + DatabaseFactory.getPaymentDatabase(context) + .createSuccessfulPayment(uuid, + recipientId, + address.get(), + timestamp, + outgoingPaymentMessage.getBlockIndex(), + outgoingPaymentMessage.getNote().or(""), + outgoingPaymentMessage.getAmount(), + outgoingPaymentMessage.getFee(), + outgoingPaymentMessage.getReceipt().toByteArray(), + PaymentMetaDataUtil.fromKeysAndImages(outgoingPaymentMessage.getPublicKeys(), outgoingPaymentMessage.getKeyImages())); + } catch (SerializationException e) { + warn(content.getTimestamp(), "Ignoring synchronized outgoing payment with bad data.", e); + } log("Inserted synchronized payment " + uuid); } @@ -2254,6 +2261,10 @@ public final class MessageContentProcessor { warn(String.valueOf(timestamp), message); } + protected void warn(long timestamp, @NonNull String message, @Nullable Throwable t) { + warn(String.valueOf(timestamp), message, t); + } + protected void warn(@NonNull String message, @Nullable Throwable t) { warn("", message, t); }