diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinTestNetConfig.java b/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinTestNetConfig.java index 1fb3c3d48..10ee8366f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinTestNetConfig.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinTestNetConfig.java @@ -56,12 +56,12 @@ final class MobileCoinTestNetConfig extends MobileCoinConfig { ClientConfig config = new ClientConfig(); String[] hardeningAdvisories = { "INTEL-SA-00334", "INTEL-SA-00615" }; VerifierFactory verifierFactory = new VerifierFactory(hardeningAdvisories, - // ~July 15, 2022 + // ~August 15, 2022 new ServiceConfig( - "4f134dcfd9c0885956f2f9af0f05c2050d8bdee2dc63b468a640670d7adeb7f8", - "8f2f3bf81f24bf493fa6d76e29e0f081815022592b1e854f95bda750aece7452", - "685481b33f2846585f33506ab65649c98a4a6d1244989651fd0fcde904ebd82f", - "719ca43abbe02f507bb91ea11ff8bc900aa86363a7d7e77b8130426fc53d8684" + "01746f4dd25f8623d603534425ed45833687eca2b3ba25bdd87180b9471dac28", + "3e9bf61f3191add7b054f0e591b62f832854606f6594fd63faef1e2aedec4021", + "92fb35d0f603ceb5eaf2988b24a41d4a4a83f8fb9cd72e67c3bc37960d864ad6", + "3d6e528ee0574ae3299915ea608b71ddd17cbe855d4f5e1c46df9b0d22b04cdb" )); config.logAdapter = new MobileCoinLogAdapter(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java b/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java index 9c966f057..bd956198e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java @@ -54,13 +54,17 @@ import java.util.concurrent.TimeoutException; public final class Wallet { - private static final String TAG = Log.tag(Wallet.class); + private static final String TAG = Log.tag(Wallet.class); + private static final Object LEDGER_LOCK = new Object(); private final MobileCoinConfig mobileCoinConfig; private final MobileCoinClient mobileCoinClient; private final AccountKey account; private final MobileCoinPublicAddress publicAddress; + private AccountSnapshot cachedAccountSnapshot; + private Amount cachedMinimumTxFee; + public Wallet(@NonNull MobileCoinConfig mobileCoinConfig, @NonNull Entropy paymentsEntropy) { this.mobileCoinConfig = mobileCoinConfig; try { @@ -122,6 +126,12 @@ public final class Wallet { return getCachedLedger(); } + /** + * Retrieve a user owned ledger + * @param minimumBlockIndex require the returned ledger to include all TxOuts to at least minimumBlockIndex + * @return a wrapped MobileCoin ledger that contains only TxOuts owned by the AccountKey + * or null if the requested minimumBlockIndex cannot be retrieved + */ @WorkerThread public @Nullable MobileCoinLedgerWrapper tryGetFullLedger(@Nullable Long minimumBlockIndex) throws IOException, FogSyncException { try { @@ -130,8 +140,16 @@ public final class Wallet { long highestBlockTimeStamp = 0; UnsignedLong highestBlockIndex = UnsignedLong.ZERO; final long asOfTimestamp = System.currentTimeMillis(); - AccountSnapshot accountSnapshot = mobileCoinClient.getAccountSnapshot(); - final Amount minimumTxFee = mobileCoinClient.getOrFetchMinimumTxFee(TokenId.MOB); + Amount minimumTxFee; + AccountSnapshot accountSnapshot; + + synchronized (LEDGER_LOCK) { + minimumTxFee = mobileCoinClient.getOrFetchMinimumTxFee(TokenId.MOB); + accountSnapshot = mobileCoinClient.getAccountSnapshot(); + + cachedMinimumTxFee = minimumTxFee; + cachedAccountSnapshot = accountSnapshot; + } if (minimumBlockIndex != null) { long snapshotBlockIndex = accountSnapshot.getBlockIndex().longValue(); @@ -212,8 +230,14 @@ public final class Wallet { @WorkerThread public @NonNull Money.MobileCoin getFee(@NonNull Money.MobileCoin amount) throws IOException { try { - BigInteger picoMob = amount.requireMobileCoin().toPicoMobBigInteger(); - return Money.picoMobileCoin(mobileCoinClient.estimateTotalFee(Amount.ofMOB(picoMob)).getValue()); + BigInteger picoMob = amount.requireMobileCoin().toPicoMobBigInteger(); + AccountSnapshot accountSnapshot = getCachedAccountSnapshot(); + Amount minimumFee = getCachedMinimumTxFee(); + if (accountSnapshot != null && minimumFee != null) { + return Money.picoMobileCoin(accountSnapshot.estimateTotalFee(Amount.ofMOB(picoMob), minimumFee).getValue()); + } else { + return Money.picoMobileCoin(mobileCoinClient.estimateTotalFee(Amount.ofMOB(picoMob)).getValue()); + } } catch (InvalidFogResponse | AttestationException | InsufficientFundsException e) { Log.w(TAG, "Failed to get fee", e); return Money.MobileCoin.ZERO; @@ -238,9 +262,7 @@ public final class Wallet { try { PaymentTransactionId.MobileCoin mobcoinTransaction = (PaymentTransactionId.MobileCoin) transactionId; Transaction transaction = Transaction.fromBytes(mobcoinTransaction.getTransaction()); - Transaction.Status status = mobileCoinClient.getAccountSnapshot() - .getTransactionStatus(transaction); - + Transaction.Status status = mobileCoinClient.getTransactionStatusQuick(transaction); switch (status) { case UNKNOWN: Log.w(TAG, "Unknown sent Transaction Status"); @@ -252,10 +274,10 @@ public final class Wallet { default: throw new IllegalStateException("Unknown Transaction Status: " + status); } - } catch (SerializationException | InvalidFogResponse e) { + } catch (SerializationException e) { Log.w(TAG, e); return TransactionStatusResult.failed(); - } catch (NetworkException | AttestationException e) { + } catch (NetworkException e) { Log.w(TAG, e); throw new IOException(e); } @@ -324,10 +346,18 @@ public final class Wallet { } try { - pendingTransaction = mobileCoinClient.prepareTransaction(to.getAddress(), - Amount.ofMOB(picoMob), - Amount.ofMOB(feeMobileCoin.toPicoMobBigInteger()), - TxOutMemoBuilder.createSenderAndDestinationRTHMemoBuilder(account)); + AccountSnapshot accountSnapshot = getCachedAccountSnapshot(); + if (accountSnapshot != null) { + pendingTransaction = accountSnapshot.prepareTransaction(to.getAddress(), + Amount.ofMOB(picoMob), + Amount.ofMOB(feeMobileCoin.toPicoMobBigInteger()), + TxOutMemoBuilder.createSenderAndDestinationRTHMemoBuilder(account)); + } else { + pendingTransaction = mobileCoinClient.prepareTransaction(to.getAddress(), + Amount.ofMOB(picoMob), + Amount.ofMOB(feeMobileCoin.toPicoMobBigInteger()), + TxOutMemoBuilder.createSenderAndDestinationRTHMemoBuilder(account)); + } } catch (InsufficientFundsException e) { Log.w(TAG, "Insufficient funds", e); results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.INSUFFICIENT_FUNDS, false)); @@ -408,6 +438,30 @@ public final class Wallet { getFullLedger(); } + /** + * @return cached account snapshot or null if it's not available + * @apiNote This method is synchronized with {@link #tryGetFullLedger} + * to wait for an updated value if ledger update is in progress. + */ + @WorkerThread + private @Nullable AccountSnapshot getCachedAccountSnapshot() { + synchronized (LEDGER_LOCK) { + return cachedAccountSnapshot; + } + } + + /** + * @return cached minimum transaction fee or null if it's not available + * @apiNote This method is synchronized with {@link #tryGetFullLedger} + * to wait for an updated value if ledger update is in progress. + */ + @WorkerThread + private @Nullable Amount getCachedMinimumTxFee() { + synchronized (LEDGER_LOCK) { + return cachedMinimumTxFee; + } + } + public enum TransactionStatus { COMPLETE, IN_PROGRESS, diff --git a/dependencies.gradle b/dependencies.gradle index e962ac4c7..3ecb00619 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -93,7 +93,7 @@ dependencyResolutionManagement { alias('rxjava3-rxkotlin').to('io.reactivex.rxjava3:rxkotlin:3.0.1') alias('rxdogtag').to('com.uber.rxdogtag2:rxdogtag:2.0.1') alias('conscrypt-android').to('org.conscrypt:conscrypt-android:2.0.0') - alias('mobilecoin').to('com.mobilecoin:android-sdk:1.2.2.2') + alias('mobilecoin').to('com.mobilecoin:android-sdk:1.2.2.4') alias('leolin-shortcutbadger').to('me.leolin:ShortcutBadger:1.1.22') alias('emilsjolander-stickylistheaders').to('se.emilsjolander:stickylistheaders:2.7.0') alias('jpardogo-materialtabstrip').to('com.jpardogo.materialtabstrip:library:1.0.9') @@ -153,4 +153,4 @@ dependencyResolutionManagement { alias('lint-tests').to('com.android.tools.lint', 'lint-tests').versionRef('lint') } } -} \ No newline at end of file +} diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 799f28e96..4a9d20bc1 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1983,9 +1983,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + +