diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md
index df1e33c2..3a355bf3 100644
--- a/android/CHANGELOG.md
+++ b/android/CHANGELOG.md
@@ -6,10 +6,21 @@ follow [https://changelog.md/](https://changelog.md/) guidelines.
## [Unreleased]
+## [49.4] - 2022-05-23
+
+### CHANGED
+- Receiving Node cell behavior in Operation Detail. Instead of opening 1ML site (private nodes
+return a 404 not found), we copy node public key to clipboard.
+
+### FIXED
+- A bug that triggered LN invoice regeneration multiple times when a payment was received
+- Added a workaround for a strange BadPaddingException coming from Android's Keystore
+
## [49.3] - 2022-04-26
### ADDED
- Better error reporting and extra metadata for MoneyDecoration (MuunAmountInput) crash
+- Show LN alias for outgoing payments in payment history and payment Detail
### FIXED
- Added missing error metadata to some crashlytics errors (e.g for background task of anon users)
diff --git a/android/Dockerfile b/android/Dockerfile
index 994618a7..9660ece9 100644
--- a/android/Dockerfile
+++ b/android/Dockerfile
@@ -3,7 +3,7 @@ FROM openjdk:17-jdk-buster@sha256:9217da81dcff19e60861791511ce57c019e47eaf5ca40d
ENV NDK_VERSION 22.0.7026061
ENV ANDROID_PLATFORM_VERSION 28
ENV ANDROID_BUILD_TOOLS_VERSION 28.0.3
-ENV GO_VERSION 1.16.8
+ENV GO_VERSION 1.18.1
RUN apt-get update \
&& apt-get install --yes --no-install-recommends \
diff --git a/android/apollo/build.gradle b/android/apollo/build.gradle
index 2bab1fbb..58204408 100644
--- a/android/apollo/build.gradle
+++ b/android/apollo/build.gradle
@@ -20,20 +20,6 @@ apply from: "${project.rootDir}/linters/pmd/check-android.gradle"
// https://github.com/spotbugs/spotbugs-gradle-plugin/issues/90
//apply from: "${project.rootDir}/linters/findbugs/check-android.gradle"
-/**
- * Returns the ip of the local network specified by the TESTING_LOCAL_NETWORK env var.
- */
-def testingLocalIp() {
- def interfaceName = System.getenv("TESTING_LOCAL_NETWORK")?.trim()
-
- return NetworkInterface.getNetworkInterfaces().
- findAll { interfaceName && interfaceName.equalsIgnoreCase(it.displayName) }.
- collect { Collections.list it.getInetAddresses() }.
- flatten().
- find()?. // first or null
- getHostAddress()
-}
-
android {
compileSdkVersion 30
@@ -43,9 +29,6 @@ android {
versionCode 1
versionName "1.0"
- // circle
- buildConfigField("String", "TESTING_LOCAL_IP", "\"${testingLocalIp()}\"")
-
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
diff --git a/android/apollo/src/main/AndroidManifest.xml b/android/apollo/src/main/AndroidManifest.xml
index 55d86c80..ca99cd26 100644
--- a/android/apollo/src/main/AndroidManifest.xml
+++ b/android/apollo/src/main/AndroidManifest.xml
@@ -2,4 +2,6 @@
xmlns:android="http://schemas.android.com/apk/res/android">
+
+
diff --git a/android/apollo/src/main/java/io/muun/apollo/data/logging/Crashlytics.kt b/android/apollo/src/main/java/io/muun/apollo/data/logging/Crashlytics.kt
new file mode 100644
index 00000000..90427c3e
--- /dev/null
+++ b/android/apollo/src/main/java/io/muun/apollo/data/logging/Crashlytics.kt
@@ -0,0 +1,120 @@
+package io.muun.apollo.data.logging
+
+import com.google.firebase.crashlytics.FirebaseCrashlytics
+import io.muun.apollo.domain.action.debug.ForceCrashReportAction
+import io.muun.apollo.domain.errors.CyclicalSwapError
+import io.muun.apollo.domain.errors.FcmTokenNotAvailableError
+import io.muun.apollo.domain.errors.InvoiceAlreadyUsedException
+import io.muun.apollo.domain.errors.InvoiceExpiredException
+import io.muun.apollo.domain.errors.InvoiceExpiresTooSoonException
+import io.muun.apollo.domain.errors.InvoiceMissingAmountException
+import io.muun.apollo.domain.errors.NoPaymentRouteException
+import io.muun.apollo.domain.errors.UnreachableNodeException
+import io.muun.apollo.domain.model.report.CrashReport
+import io.muun.apollo.domain.utils.isInstanceOrIsCausedByError
+
+object Crashlytics {
+
+ private val crashlytics = FirebaseCrashlytics.getInstance()
+
+ /**
+ * Set up Crashlytics metadata.
+ */
+ @JvmStatic
+ fun configure(email: String?, userId: String) {
+ crashlytics.setUserId(userId)
+ crashlytics.setCustomKey("email", email ?: "unknown")
+
+ // TODO: use setUserEmail, and grab Houston session UUID to attach it
+ }
+
+ /**
+ * Add custom log event, to be displayed under Logs tab. Also queryable via Bigquery. See:
+ * https://firebase.google.com/docs/crashlytics/customize-crash-reports?platform=android#add-logs
+ */
+ @JvmStatic
+ fun logBreadcrumb(breadcrumb: String) {
+ crashlytics.log(breadcrumb);
+ }
+
+ /**
+ * Send the error to Crashlytics, attaching metadata as key-values with their SDK.
+ */
+ fun reportError(report: CrashReport) {
+
+ // Silence some common "nothing to worry about" errors
+ if (isOnCrashlyticsBlacklist(report.originalError)) {
+ return
+ }
+
+ crashlytics.setCustomKey("tag", report.tag)
+ crashlytics.setCustomKey("message", report.message)
+ crashlytics.setCustomKey("locale", LoggingContext.locale)
+
+ for (entry in report.metadata.entries) {
+ crashlytics.setCustomKey(entry.key, entry.value.toString())
+ }
+
+ crashlytics.recordException(report.error)
+ }
+
+ /**
+ * Send a "fallback" reporting error to Crashlytics. This means that there was an error while
+ * doing our usual error report processing. Hence we try to report the original error data (tag,
+ * message, error) and the error that happened while reporting.
+ */
+ fun reportReportingError(
+ tag: String?,
+ message: String?,
+ originalError: Throwable?,
+ crashReportingError: Throwable,
+ ) {
+
+ tag?.let { crashlytics.setCustomKey("tag", it) }
+ message?.let { crashlytics.setCustomKey("message", it) }
+
+ if (originalError != null) {
+ crashlytics.recordException(originalError)
+ }
+
+ crashlytics.recordException(crashReportingError)
+ }
+
+ /**
+ * Forcefully report an exception, bypassing our usual error processing. Meant to only be used
+ * by ForceCrashReportAction.
+ *
+ * @see ForceCrashReportAction
+ */
+ fun forceReport(error: ForceCrashReportAction.ForcedCrashlyticsCall) {
+ crashlytics.recordException(error)
+ }
+
+ /**
+ * There are certain errors that are expected and/or there's nothing we can do about it (besides
+ * properly informing the user about the situation), so let's try to reduce crashlytics noise by
+ * silencing some common "nothing to worry about" errors.
+ */
+ private fun isOnCrashlyticsBlacklist(error: Throwable?): Boolean {
+
+ // If root error has no throwable cause then there's nothing to blacklist
+ // This is an ugly signature and behaviour to have but makes life easier for caller
+ if (error == null) {
+ return false
+ }
+
+ return when {
+ error.isInstanceOrIsCausedByError() -> true
+ error.isInstanceOrIsCausedByError() -> true
+ error.isInstanceOrIsCausedByError() -> true
+ error.isInstanceOrIsCausedByError() -> true
+ error.isInstanceOrIsCausedByError() -> true
+ error.isInstanceOrIsCausedByError() -> true
+ error.isInstanceOrIsCausedByError() -> true
+
+ error.isInstanceOrIsCausedByError() -> true
+
+ else -> false
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/apollo/src/main/java/io/muun/apollo/data/logging/LoggingContext.kt b/android/apollo/src/main/java/io/muun/apollo/data/logging/LoggingContext.kt
index eb7ef831..d40cf12c 100644
--- a/android/apollo/src/main/java/io/muun/apollo/data/logging/LoggingContext.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/data/logging/LoggingContext.kt
@@ -1,25 +1,19 @@
package io.muun.apollo.data.logging
-import com.google.firebase.crashlytics.FirebaseCrashlytics
-
-
object LoggingContext {
+ /**
+ * Log errors to the Crashlytics.
+ */
@JvmStatic
var sendToCrashlytics = true // default for production
+ /**
+ * Log errors to the system logs.
+ */
@JvmStatic
var sendToLogcat = false // default for production
@JvmStatic
var locale: String = "UNSET" // easily track and attach user's locale to muun errors and reports
-
- @JvmStatic
- fun configure(email: String?, userId: String) {
- val crashlytics = FirebaseCrashlytics.getInstance()
- crashlytics.setUserId(userId)
- crashlytics.setCustomKey("email", email ?: "unknown")
-
- // TODO: use setUserEmail, and grab Houston session UUID to attach it
- }
}
\ No newline at end of file
diff --git a/android/apollo/src/main/java/io/muun/apollo/data/logging/MuunTree.kt b/android/apollo/src/main/java/io/muun/apollo/data/logging/MuunTree.kt
index 74f8c095..87b74eb3 100644
--- a/android/apollo/src/main/java/io/muun/apollo/data/logging/MuunTree.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/data/logging/MuunTree.kt
@@ -1,14 +1,10 @@
package io.muun.apollo.data.logging
import android.util.Log
-import com.google.firebase.crashlytics.FirebaseCrashlytics
-import io.muun.apollo.domain.errors.*
-import io.muun.apollo.domain.model.report.CrashReport
import io.muun.apollo.domain.model.report.CrashReportBuilder
-import io.muun.apollo.domain.utils.isInstanceOrIsCausedByError
import timber.log.Timber
-class MuunTree: Timber.DebugTree() {
+class MuunTree : Timber.DebugTree() {
/**
* Log a message, taking steps to enrich and report errors.
@@ -35,19 +31,21 @@ class MuunTree: Timber.DebugTree() {
private fun sendPreparedCrashReport(tag: String?, message: String?, error: Throwable?) {
val report = CrashReportBuilder.build(tag, message, error)
- if (LoggingContext.sendToCrashlytics && !isOnCrashlyticsBlacklist(error)) {
- sendToCrashlytics(report)
+ if (LoggingContext.sendToCrashlytics) {
+ Crashlytics.reportError(report)
}
if (LoggingContext.sendToLogcat) {
- sendToLogcat(report)
+ Log.e(report.tag, "${report.message} ${report.metadata}", report.error)
}
}
- private fun sendFallbackCrashReport(tag: String?,
- message: String?,
- error: Throwable?,
- crashReportingError: Throwable) {
+ private fun sendFallbackCrashReport(
+ tag: String?,
+ message: String?,
+ error: Throwable?,
+ crashReportingError: Throwable,
+ ) {
if (LoggingContext.sendToLogcat) {
Log.e("CrashReport:$tag", "During error processing", crashReportingError)
@@ -55,65 +53,7 @@ class MuunTree: Timber.DebugTree() {
}
if (LoggingContext.sendToCrashlytics) {
-
- val crashlytics = FirebaseCrashlytics.getInstance()
-
- if (error != null) {
- crashlytics.recordException(error)
- }
- crashlytics.recordException(crashReportingError)
- }
- }
-
- /**
- * Send the error to the system logs.
- */
- private fun sendToLogcat(report: CrashReport) {
- Log.e(report.tag, "${report.message} ${report.metadata}", report.error)
- }
-
- /**
- * Send the error to Crashlytics, attaching metadata as key-values with their SDK.
- */
- private fun sendToCrashlytics(report: CrashReport) {
- val crashlytics = FirebaseCrashlytics.getInstance()
-
- crashlytics.setCustomKey("tag", report.tag)
- crashlytics.setCustomKey("message", report.message)
- crashlytics.setCustomKey("locale", LoggingContext.locale)
-
- for (entry in report.metadata.entries) {
- crashlytics.setCustomKey(entry.key, entry.value.toString())
- }
-
- crashlytics.recordException(report.error)
- }
-
- /**
- * There are certain errors that are expected and/or there's nothing we can do about it (besides
- * properly informing the user about the situation), so let's try to reduce crashlytics noise by
- * silencing some common "nothing to worry about" errors.
- */
- private fun isOnCrashlyticsBlacklist(error: Throwable?): Boolean {
-
- // If root error has no throwable cause then there's nothing to blacklist
- // This is an ugly signature and behaviour to have but makes life easier for caller
- if (error == null) {
- return false
- }
-
- return when {
- error.isInstanceOrIsCausedByError() -> true
- error.isInstanceOrIsCausedByError() -> true
- error.isInstanceOrIsCausedByError() -> true
- error.isInstanceOrIsCausedByError() -> true
- error.isInstanceOrIsCausedByError() -> true
- error.isInstanceOrIsCausedByError() -> true
- error.isInstanceOrIsCausedByError() -> true
-
- error.isInstanceOrIsCausedByError() -> true
-
- else -> false
+ Crashlytics.reportReportingError(tag, message, error, crashReportingError)
}
}
}
\ No newline at end of file
diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/SecureStorageProvider.java b/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/SecureStorageProvider.java
index 5be891c2..52880889 100644
--- a/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/SecureStorageProvider.java
+++ b/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/SecureStorageProvider.java
@@ -1,6 +1,7 @@
package io.muun.apollo.data.os.secure_storage;
import io.muun.apollo.domain.errors.SecureStorageError;
+import io.muun.common.utils.Encodings;
import io.muun.common.utils.Preconditions;
import rx.Observable;
@@ -39,7 +40,9 @@ public byte[] get(String key) {
return retrieveDecrypted(key);
} catch (Throwable e) {
- throw new SecureStorageError(e, debugSnapshot());
+ final SecureStorageError ssError = new SecureStorageError(e, debugSnapshot());
+ enhanceError(ssError, key);
+ throw ssError;
}
}
@@ -60,7 +63,9 @@ public void put(String key, byte[] value) {
storeEncrypted(key, value);
} catch (Throwable e) {
- throw new SecureStorageError(e, debugSnapshot());
+ final SecureStorageError ssError = new SecureStorageError(e, debugSnapshot());
+ enhanceError(ssError, key);
+ throw ssError;
}
preferences.recordAuditTrail("PUT", key);
@@ -147,11 +152,17 @@ private void storeEncrypted(String key, byte[] input) {
preferences.saveBytes(keyStore.encryptData(input, key, preferences.getAesIv(key)), key);
}
+ private void enhanceError(SecureStorageError ssError, String key) {
+ ssError.addMetadata("key", key);
+ ssError.addMetadata("cypherText", Encodings.bytesToHex(preferences.getBytes(key)));
+ ssError.addMetadata("aesIV", Encodings.bytesToHex(preferences.getAesIv(key)));
+ }
+
/**
* Take a debug snapshot of the current state of the secure storage. This is safe to
* report without compromising any user data.
*/
- public DebugSnapshot debugSnapshot() {
+ private DebugSnapshot debugSnapshot() {
// NEVER ever return any values from the keystore itself, only labels should get out.
Set keystoreLabels = null;
diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/KeysRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/KeysRepository.java
index 62f4e366..4bfbf5f0 100644
--- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/KeysRepository.java
+++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/KeysRepository.java
@@ -391,7 +391,7 @@ public void storePublicChallengeKey(ChallengePublicKey publicKey, ChallengeType
}
secureStorageProvider.put(
- KEY_CHALLENGE_PUBLIC_KEY + type.toString(),
+ KEY_CHALLENGE_PUBLIC_KEY + type,
publicKey.serialize()
);
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/ApplicationLockManager.java b/android/apollo/src/main/java/io/muun/apollo/domain/ApplicationLockManager.java
index 31aa7ac7..bdfcfc3b 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/ApplicationLockManager.java
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/ApplicationLockManager.java
@@ -1,15 +1,20 @@
package io.muun.apollo.domain;
+import io.muun.apollo.data.logging.Crashlytics;
import io.muun.apollo.data.os.authentication.PinManager;
import io.muun.apollo.data.os.secure_storage.SecureStorageProvider;
+import io.muun.apollo.domain.errors.SecureStorageError;
import io.muun.apollo.domain.selector.ChallengePublicKeySelector;
+import io.muun.apollo.domain.utils.ExtensionsKt;
import io.muun.common.utils.Encodings;
import io.muun.common.utils.Preconditions;
+import android.content.Context;
import androidx.annotation.VisibleForTesting;
import rx.Observable;
import rx.Subscription;
+import timber.log.Timber;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
@@ -20,6 +25,9 @@
public class ApplicationLockManager {
public interface UnlockListener {
+ /**
+ * Called when lock screen has successfully been unlocked.
+ */
void onUnlock();
}
@@ -34,6 +42,7 @@ public interface UnlockListener {
private final PinManager pinManager;
private final SecureStorageProvider secureStorageProvider;
private final ChallengePublicKeySelector challengePublicKeySel;
+ private final Context context;
/**
* Constructor.
@@ -41,11 +50,13 @@ public interface UnlockListener {
@Inject
public ApplicationLockManager(PinManager pinManager,
SecureStorageProvider secureStorageProvider,
- ChallengePublicKeySelector challengePublicKeySel1) {
+ ChallengePublicKeySelector challengePublicKeySel1,
+ Context context) {
this.pinManager = pinManager;
this.secureStorageProvider = secureStorageProvider;
this.challengePublicKeySel = challengePublicKeySel1;
+ this.context = context;
}
public synchronized boolean isLockConfigured() {
@@ -176,6 +187,18 @@ private synchronized int fetchIncorrectAttempts() {
} catch (NoSuchElementException error) {
storeIncorrectAttempts(0);
return 0;
+
+ } catch (SecureStorageError error) {
+ if (ExtensionsKt.isCauseByBadPaddingException(error)) {
+ // If this error is caused by a BadPadding Exception coming from the Android
+ // Keystore, we try continue execution hoping this is the only piece of data
+ // affected by this data corruption.
+ error.addMetadata("hasBackup", challengePublicKeySel.existsAnyType());
+ Crashlytics.logBreadcrumb("bad_padding_exception_workaround");
+ Timber.e(error, "WORKAROUND for BadPaddingException in fetchIncorrectAttempts");
+ return 0;
+ }
+ throw error;
}
}
}
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/LoggingContextManager.kt b/android/apollo/src/main/java/io/muun/apollo/domain/LoggingContextManager.kt
index a32fbc0e..0ef3894c 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/LoggingContextManager.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/LoggingContextManager.kt
@@ -1,6 +1,7 @@
package io.muun.apollo.domain
import android.content.Context
+import io.muun.apollo.data.logging.Crashlytics
import io.muun.apollo.data.logging.LoggingContext
import io.muun.apollo.data.preferences.UserRepository
import io.muun.apollo.domain.model.user.User
@@ -10,13 +11,13 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
-class LoggingContextManager @Inject constructor(
+class LoggingContextManager @Inject constructor(
private val userRepository: UserRepository,
- private val context: Context
+ private val context: Context,
) {
/**
- * Setups Crashlytics metadata.
+ * Set up Crashlytics metadata.
*/
fun setupCrashlytics() {
val maybeUser: Optional = userRepository.fetchOneOptional()
@@ -26,7 +27,7 @@ class LoggingContextManager @Inject constructor(
}
val user = maybeUser.get()
- LoggingContext.configure(user.email.orElse(null), user.hid.toString())
+ Crashlytics.configure(user.email.orElse(null), user.hid.toString())
LoggingContext.locale = context.locale().toString()
}
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/challenge_keys/password_setup/StartEmailSetupAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/challenge_keys/password_setup/StartEmailSetupAction.kt
index fc824d70..a67825f9 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/action/challenge_keys/password_setup/StartEmailSetupAction.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/challenge_keys/password_setup/StartEmailSetupAction.kt
@@ -1,5 +1,6 @@
package io.muun.apollo.domain.action.challenge_keys.password_setup
+import io.muun.apollo.data.logging.Crashlytics
import io.muun.apollo.data.logging.LoggingContext
import io.muun.apollo.data.net.HoustonClient
import io.muun.apollo.data.preferences.UserRepository
@@ -36,7 +37,7 @@ class StartEmailSetupAction @Inject constructor(
user.email = Optional.of(email)
user.isEmailVerified = false
- LoggingContext.configure(email, user.hid.toString())
+ Crashlytics.configure(email, user.hid.toString())
userRepository.store(user)
}
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/debug/ForceCrashReportAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/debug/ForceCrashReportAction.kt
index a98d3fc1..d492dcdd 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/action/debug/ForceCrashReportAction.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/debug/ForceCrashReportAction.kt
@@ -1,7 +1,7 @@
package io.muun.apollo.domain.action.debug
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import io.muun.apollo.data.external.Globals
+import io.muun.apollo.data.logging.Crashlytics
import io.muun.apollo.domain.action.base.BaseAsyncAction1
import rx.Observable
import timber.log.Timber
@@ -9,15 +9,15 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
-class ForceCrashReportAction @Inject constructor(): BaseAsyncAction1() {
+class ForceCrashReportAction @Inject constructor() : BaseAsyncAction1() {
- class ForcedCrashlyticsCall(origin: String):
+ class ForcedCrashlyticsCall(origin: String) :
RuntimeException("Forced logException $origin v${Globals.INSTANCE.versionCode}")
- class ForcedTimberErrorCall(origin: String):
+ class ForcedTimberErrorCall(origin: String) :
RuntimeException("Forced Timber.e $origin v${Globals.INSTANCE.versionCode}")
- class ForcedBackgroundException(origin: String):
+ class ForcedBackgroundException(origin: String) :
RuntimeException("Forced throw $origin v${Globals.INSTANCE.versionCode}")
/**
@@ -26,10 +26,8 @@ class ForceCrashReportAction @Inject constructor(): BaseAsyncAction1 =
Observable.defer {
Timber.e(ForcedTimberErrorCall(origin))
- FirebaseCrashlytics.getInstance().recordException(ForcedCrashlyticsCall(origin))
+ Crashlytics.forceReport(ForcedCrashlyticsCall(origin))
throw ForcedBackgroundException(origin)
-
- Observable.just(null)
}
}
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/session/CreateFirstSessionAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/CreateFirstSessionAction.kt
index 0241f923..582261eb 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/action/session/CreateFirstSessionAction.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/CreateFirstSessionAction.kt
@@ -1,5 +1,6 @@
package io.muun.apollo.domain.action.session
+import io.muun.apollo.data.logging.Crashlytics
import io.muun.apollo.data.logging.LoggingContext
import io.muun.apollo.data.net.HoustonClient
import io.muun.apollo.data.preferences.FirebaseInstalationIdRepository
@@ -58,7 +59,7 @@ class CreateFirstSessionAction @Inject constructor(
keysRepository.storeBaseMuunPublicKey(it.cosigningPublicKey)
keysRepository.storeSwapServerPublicKey(it.swapServerPublicKey)
- LoggingContext.configure(null, it.user.hid.toString())
+ Crashlytics.configure(null, it.user.hid.toString())
}
}
}
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/session/CreateLoginSessionAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/CreateLoginSessionAction.kt
index a5c0bfe9..930edca6 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/action/session/CreateLoginSessionAction.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/CreateLoginSessionAction.kt
@@ -1,6 +1,6 @@
package io.muun.apollo.domain.action.session
-import io.muun.apollo.data.logging.LoggingContext
+import io.muun.apollo.data.logging.Crashlytics
import io.muun.apollo.data.net.HoustonClient
import io.muun.apollo.data.preferences.FirebaseInstalationIdRepository
import io.muun.apollo.domain.action.LogoutActions
@@ -40,6 +40,6 @@ class CreateLoginSessionAction @Inject constructor(
isRootedDeviceAction.actionNow()
)
}
- .doOnNext { LoggingContext.configure(email, "NotLoggedYet") }
+ .doOnNext { Crashlytics.configure(email, "NotLoggedYet") }
}
}
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/errors/MuunError.kt b/android/apollo/src/main/java/io/muun/apollo/domain/errors/MuunError.kt
index 14aea73f..e9f8b6b5 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/errors/MuunError.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/errors/MuunError.kt
@@ -1,6 +1,5 @@
package io.muun.apollo.domain.errors
-import io.muun.apollo.data.os.secure_storage.SecureStorageProvider
import java.io.Serializable
open class MuunError: RuntimeException {
@@ -23,4 +22,18 @@ open class MuunError: RuntimeException {
val mapKeys = metadata.mapKeys { entry -> "${javaClass.canonicalName}.${entry.key}" }
return mapKeys as MutableMap
}
+
+ /**
+ * Add extra metadata to this error.
+ */
+ fun addMetadata(key: String, value: Serializable) {
+ metadata[key] = value
+ }
+
+ /**
+ * Add extra metadata to this error.
+ */
+ fun addMetadata(newMetadata: Map) {
+ metadata.putAll(newMetadata)
+ }
}
\ No newline at end of file
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/errors/SecureStorageError.kt b/android/apollo/src/main/java/io/muun/apollo/domain/errors/SecureStorageError.kt
index ba36a3b3..742c4553 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/errors/SecureStorageError.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/errors/SecureStorageError.kt
@@ -2,13 +2,13 @@ package io.muun.apollo.domain.errors
import io.muun.apollo.data.os.secure_storage.SecureStorageProvider
-open class SecureStorageError: MuunError {
+open class SecureStorageError : MuunError {
constructor(debugSnapshot: SecureStorageProvider.DebugSnapshot) {
attachDebugSnapshotMetadata(debugSnapshot)
}
- constructor(t: Throwable, debugSnapshot: SecureStorageProvider.DebugSnapshot): super(t) {
+ constructor(t: Throwable, debugSnapshot: SecureStorageProvider.DebugSnapshot) : super(t) {
attachDebugSnapshotMetadata(debugSnapshot)
}
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReport.kt b/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReport.kt
index 64e38535..e250f3a8 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReport.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReport.kt
@@ -7,6 +7,7 @@ data class CrashReport(
val tag: String,
val message: String,
val error: Throwable,
+ val originalError: Throwable?,
val metadata: MutableMap
) {
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReportBuilder.kt b/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReportBuilder.kt
index 3c54def6..4c9676ac 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReportBuilder.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReportBuilder.kt
@@ -75,7 +75,7 @@ object CrashReportBuilder {
error = summarize(error)
// Done!
- return CrashReport(tag ?: "Apollo", message, error, metadata)
+ return CrashReport(tag ?: "Apollo", message, error, origError, metadata)
}
/** Craft a summarized Throwable */
@@ -105,5 +105,5 @@ object CrashReportBuilder {
/** Remove the Stack trace from the message, if present */
private fun removeRedundantStackTrace(timberMessage: String?, error: Throwable) =
- (timberMessage ?: "").split(error.javaClass.canonicalName!!, limit=2)[0]
+ (timberMessage ?: "").split(error.javaClass.canonicalName!!, limit = 2)[0]
}
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/selector/LatestOperationSelector.kt b/android/apollo/src/main/java/io/muun/apollo/domain/selector/LatestOperationSelector.kt
index 77212d0e..35d3f76b 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/selector/LatestOperationSelector.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/selector/LatestOperationSelector.kt
@@ -10,6 +10,7 @@ class LatestOperationSelector @Inject constructor(private val operationDao: Oper
fun watch(): Observable> {
return operationDao.fetchMaybeLatest()
+ .distinct { maybeOp -> maybeOp.map { it.hid }.orElse(null) }
}
fun get(): Optional {
diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/utils/Extensions.kt b/android/apollo/src/main/java/io/muun/apollo/domain/utils/Extensions.kt
index a6059439..c1b916d1 100644
--- a/android/apollo/src/main/java/io/muun/apollo/domain/utils/Extensions.kt
+++ b/android/apollo/src/main/java/io/muun/apollo/domain/utils/Extensions.kt
@@ -19,6 +19,7 @@ import libwallet.StringList
import rx.Observable
import java.io.Serializable
import java.util.*
+import javax.crypto.BadPaddingException
import javax.money.Monetary
import javax.money.MonetaryException
@@ -60,6 +61,13 @@ fun Throwable.isInstanceOrIsCausedByNetworkError() =
fun Throwable.isInstanceOrIsCausedBySecureStorageError() =
isInstanceOrIsCausedByError()
+/**
+ * Needed as inline reified functions can't be called from Java.
+ */
+fun Throwable.isCauseByBadPaddingException() =
+ isInstanceOrIsCausedByError()
+
+
inline fun Throwable.isInstanceOrIsCausedByError() =
this is T || isCausedByError()
diff --git a/android/apollo/src/test/java/io/muun/apollo/application_lock/ApplicationLockTest.java b/android/apollo/src/test/java/io/muun/apollo/application_lock/ApplicationLockTest.java
index f2668294..38f16083 100644
--- a/android/apollo/src/test/java/io/muun/apollo/application_lock/ApplicationLockTest.java
+++ b/android/apollo/src/test/java/io/muun/apollo/application_lock/ApplicationLockTest.java
@@ -9,6 +9,7 @@
import io.muun.apollo.domain.ApplicationLockManager;
import io.muun.apollo.domain.selector.ChallengePublicKeySelector;
+import android.content.Context;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -30,6 +31,9 @@ public class ApplicationLockTest extends BaseTest {
@Mock
private ChallengePublicKeySelector challengePublicKeySel;
+ @Mock
+ private Context context; // Not really used in this tests
+
private ApplicationLockManager lockManager;
@Before
@@ -42,7 +46,8 @@ public void setUp() {
lockManager = new ApplicationLockManager(
pinManager,
secureStorageProvider,
- challengePublicKeySel
+ challengePublicKeySel,
+ context
);
when(pinManager.verifyPin(CORRECT_PIN)).thenReturn(true);
diff --git a/android/apolloui/build.gradle b/android/apolloui/build.gradle
index 45b68a90..52cb2346 100644
--- a/android/apolloui/build.gradle
+++ b/android/apolloui/build.gradle
@@ -36,14 +36,14 @@ check.dependsOn 'lint'
/**
* Insert quotes around string fields that are injected literally into code, like a C macro.
*/
-def quote(string) {
+static def quote(string) {
return "\"" + string + "\""
}
/**
* Returns a prefix of the current commit hash.
*/
-def commitTag() {
+static def commitTag() {
return 'git rev-parse --short HEAD'.execute().text.trim().substring(0, 7)
}
@@ -51,7 +51,7 @@ def commitTag() {
/**
* Configure external links for a given product flavor.
*/
-def configExternalLinks(productFlavor, String host) {
+static def configExternalLinks(productFlavor, String host) {
String verifyPath = "/link/verify-v2/index.html"
String authorizePath = "/link/authorize/index.html"
String changePasswdPath = "/link/confirm/index.html"
@@ -80,8 +80,8 @@ android {
applicationId "io.muun.apollo"
minSdkVersion 19
targetSdkVersion 30
- versionCode 903
- versionName "49.3"
+ versionCode 904
+ versionName "49.4"
// Needed to make sure these classes are available in the main DEX file for API 19
// See: https://spin.atomicobject.com/2018/07/16/support-kitkat-multidex/
diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/model/UiOperation.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/model/UiOperation.kt
index c6419d58..093c667d 100644
--- a/android/apolloui/src/main/java/io/muun/apollo/presentation/model/UiOperation.kt
+++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/model/UiOperation.kt
@@ -187,13 +187,13 @@ abstract class UiOperation(
get() = operation.description
/**
- * Get the receiving LN node data, in a clickable link to an explorer.
+ * Get the receiving LN node data.
*/
- val swapReceiverLink: CharSequence
+ val swapReceiverNodeData: String
get() = if (operation.swap == null) {
""
} else {
- linkBuilder.lightningNodeLink(operation.swap!!.receiver)
+ operation.swap!!.receiver.formattedDestination
}
/**
diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationActivity.kt
index bb2d32a6..9f00fd89 100644
--- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationActivity.kt
+++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationActivity.kt
@@ -221,7 +221,11 @@ class NewOperationActivity : SingleFragmentActivity(), Ne
val newOpOrigin = getOrigin(intent)
val uri = getValidIntentUri(intent)
- val operationUri = try { OperationUri.fromString(uri) } catch (e: Exception) { null }
+ val operationUri = try {
+ OperationUri.fromString(uri)
+ } catch (e: Exception) {
+ null
+ }
if (operationUri != null) {
when {
@@ -639,7 +643,7 @@ class NewOperationActivity : SingleFragmentActivity(), Ne
}
- private fun getFormattedDestinationData(receiver: SubmarineSwapReceiver): CharSequence {
+ private fun getFormattedDestinationData(receiver: SubmarineSwapReceiver): CharSequence {
val publicKeyText: CharSequence = Html.fromHtml(
getString(
R.string.new_operation_receiving_node_public_key,
@@ -747,7 +751,7 @@ class NewOperationActivity : SingleFragmentActivity(), Ne
override fun onTickSeconds(remainingSeconds: Long) {
val context = this@NewOperationActivity
- val timeText= NewOperationInvoiceFormatter(context).formatSeconds(remainingSeconds)
+ val timeText = NewOperationInvoiceFormatter(context).formatSeconds(remainingSeconds)
val prefixText = getString(R.string.new_operation_invoice_exp_prefix)
val text = TextUtils.concat(prefixText, " ", timeText)
diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationPresenter.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationPresenter.kt
index 16c133d8..c963de3f 100644
--- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationPresenter.kt
+++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationPresenter.kt
@@ -26,7 +26,7 @@ import io.muun.apollo.presentation.ui.base.BasePresenter
import io.muun.apollo.presentation.ui.base.di.PerActivity
import io.muun.apollo.presentation.ui.fragments.manual_fee.ManualFeeParentPresenter
import io.muun.apollo.presentation.ui.fragments.new_op_error.NewOperationErrorParentPresenter
-import io.muun.apollo.presentation.ui.fragments.new_op_error.NewOperationErrorParentPresenter.*
+import io.muun.apollo.presentation.ui.fragments.new_op_error.NewOperationErrorParentPresenter.ErrorMetadata
import io.muun.apollo.presentation.ui.fragments.recommended_fee.RecommendedFeeParentPresenter
import io.muun.common.Rules
import io.muun.common.utils.Preconditions
diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailActivity.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailActivity.java
index 059bb4a4..3c27af95 100644
--- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailActivity.java
+++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailActivity.java
@@ -115,7 +115,7 @@ protected void initializeUi() {
}
@Override
- public void setOperation(UiOperation operation) {
+ public void setOperation(final UiOperation operation) {
final Context context = getViewContext();
// Header:
@@ -198,7 +198,7 @@ public void setOperation(UiOperation operation) {
}
}
- private void setSwapOperation(UiOperation operation) {
+ private void setSwapOperation(final UiOperation operation) {
// Sections involved:
swapSection.setVisibility(View.VISIBLE);
feeItem.setVisibility(View.VISIBLE);
@@ -216,7 +216,11 @@ private void setSwapOperation(UiOperation operation) {
swapInvoiceItem.setVisibility(View.VISIBLE);
swapReceiverPubkeyItem.setVisibility(View.VISIBLE);
- swapReceiverPubkeyItem.setDescription(operation.getSwapReceiverLink());
+ swapReceiverPubkeyItem.setDescription(operation.getSwapReceiverNodeData());
+ final String swapReceiverNodeData = operation.getSwapReceiverNodeData();
+ swapReceiverPubkeyItem.setOnIconClickListener(view ->
+ onCopyReceivingNodeToClipboard(swapReceiverNodeData)
+ );
// We no longer show fundingTxId to hide the submarine swap impl detail of our ln payments
@@ -226,7 +230,7 @@ private void setSwapOperation(UiOperation operation) {
swapPreimageItem.setOnIconClickListener(view -> onCopyPreimageToClipboard(preimage));
}
- private void setNormalOperation(Context context, UiOperation operation) {
+ private void setNormalOperation(final Context context, final UiOperation operation) {
// Sections involved:
normalSection.setVisibility(View.VISIBLE);
@@ -298,7 +302,7 @@ private void setIncomingSwapOperation(final UiOperation operation) {
}
}
- private CharSequence getStatusDescription(UiOperation operation) {
+ private CharSequence getStatusDescription(final UiOperation operation) {
switch (operation.getOperationStatus()) {
@@ -318,7 +322,7 @@ private CharSequence getStatusDescription(UiOperation operation) {
}
@NonNull
- private CharSequence getPendingStatusDescription(UiOperation operation) {
+ private CharSequence getPendingStatusDescription(final UiOperation operation) {
if (operation.is0ConfSwap()) {
return getString(R.string.operation_swap_pending_0conf_desc);
@@ -331,7 +335,7 @@ private CharSequence getPendingStatusDescription(UiOperation operation) {
);
}
- private Unit onWhyThisClick(String linkId) {
+ private Unit onWhyThisClick(final String linkId) {
final TitleAndDescriptionDrawer dialog = new TitleAndDescriptionDrawer();
dialog.setTitle(R.string.operation_swap_pending_confirmations_explanation_title);
dialog.setDescription(getString(R.string.operation_swap_pending_explanation_desc));
@@ -341,42 +345,47 @@ private Unit onWhyThisClick(String linkId) {
return null;
}
- private void onCopyInvoiceToClipboard(String invoice) {
+ private void onCopyInvoiceToClipboard(final String invoice) {
presenter.copyLnInvoiceToClipboard(invoice);
showTextToast(getString(R.string.operation_detail_invoice_copied));
}
- private void onCopyPreimageToClipboard(String preimage) {
+ private void onCopyPreimageToClipboard(final String preimage) {
presenter.copySwapPreimageToClipboard(preimage);
showTextToast(getString(R.string.operation_detail_preimage_copied));
}
- private void onCopyPaymentHashToClipboard(String paymentHash) {
+ private void onCopyPaymentHashToClipboard(final String paymentHash) {
presenter.copySwapPreimageToClipboard(paymentHash);
showTextToast(getString(R.string.operation_detail_preimage_copied));
}
- private void onCopyTransactionIdToClipboard(String transactionId) {
+ private void onCopyTransactionIdToClipboard(final String transactionId) {
presenter.copyTransactionIdToClipboard(transactionId);
showTextToast(getString(R.string.operation_detail_txid_copied));
}
- private void onShareTransactionId(String transactionId) {
+ private void onShareTransactionId(final String transactionId) {
presenter.shareTransactionId(transactionId);
}
- private void onCopyAmountToClipboard(String amount) {
+ private void onCopyAmountToClipboard(final String amount) {
presenter.copyAmountToClipboard(amount);
showTextToast(getString(R.string.operation_detail_amount_copied));
}
- private void onCopyNetworkFeeToClipboard(String fee) {
+ private void onCopyNetworkFeeToClipboard(final String fee) {
presenter.copyNetworkFeeToClipboard(fee);
showTextToast(getString(R.string.operation_detail_fee_copied));
}
- private void onCopyAddressToClipboard(String address) {
+ private void onCopyAddressToClipboard(final String address) {
presenter.copyNetworkFeeToClipboard(address);
showTextToast(getString(R.string.operation_detail_address_copied));
}
+
+ private void onCopyReceivingNodeToClipboard(final String receivingNode) {
+ presenter.copyReceivingNodeToClipboard(receivingNode);
+ showTextToast(getString(R.string.operation_detail_node_copied));
+ }
}
diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailPresenter.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailPresenter.java
index cc19a9aa..eaff5908 100644
--- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailPresenter.java
+++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailPresenter.java
@@ -104,6 +104,13 @@ public void copyTransactionIdToClipboard(String transactionId) {
clipboardManager.copy("Transaction ID", transactionId);
}
+ /**
+ * Copy receiving node to clipboard.
+ */
+ public void copyReceivingNodeToClipboard(String node) {
+ clipboardManager.copy("Receiving Node", node);
+ }
+
/**
* Copy fee amount to the clipboard.
*/
diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/utils/LinkBuilder.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/utils/LinkBuilder.java
index 3f096765..8878df29 100644
--- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/utils/LinkBuilder.java
+++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/utils/LinkBuilder.java
@@ -57,13 +57,6 @@ public RichText addressLink(String address) {
return createLink(address, urlRoot + address, "block_explorer_address");
}
- /**
- * Create a RichText containing a clickable link to a lightning node in an explorer.
- */
- public RichText lightningNodeLink(SubmarineSwapReceiver receiver) {
- return lightningNodeLink(receiver, getLnLinkText(receiver));
- }
-
/**
* Create a RichText containing a clickable link to a lightning node in an explorer.
*/
@@ -100,7 +93,6 @@ private RichText createLink(String text, String url, String trackingName) {
return new RichText(text).setLink(() -> {
ExtensionsKt.openInBrowser(context, url);
analytics.report(new AnalyticsEvent.E_OPEN_WEB(trackingName, url));
-
});
}
diff --git a/android/apolloui/src/main/res/layout/operation_detail_activity.xml b/android/apolloui/src/main/res/layout/operation_detail_activity.xml
index bf3996bf..0d30a9ae 100644
--- a/android/apolloui/src/main/res/layout/operation_detail_activity.xml
+++ b/android/apolloui/src/main/res/layout/operation_detail_activity.xml
@@ -172,7 +172,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
- muun:title="@string/operation_detail_receiving_pubkey" />
+ muun:title="@string/operation_detail_receiving_pubkey"
+ muun:icon="@drawable/ic_copy" />
diff --git a/android/apolloui/src/main/res/values-es/strings.xml b/android/apolloui/src/main/res/values-es/strings.xml
index d99d91e2..3b468c81 100644
--- a/android/apolloui/src/main/res/values-es/strings.xml
+++ b/android/apolloui/src/main/res/values-es/strings.xml
@@ -305,6 +305,7 @@
Monto copiado al portapapeles
Comisión de la red copiada al portapapeles
Dirección copiada al portapapeles
+ Nodo destinatario copiado al portapapeles
Pagos
diff --git a/android/apolloui/src/main/res/values/strings.xml b/android/apolloui/src/main/res/values/strings.xml
index 82246966..9f5d4229 100644
--- a/android/apolloui/src/main/res/values/strings.xml
+++ b/android/apolloui/src/main/res/values/strings.xml
@@ -296,6 +296,7 @@
Amount copied to clipboard
Network fee copied to clipboard
Address copied to clipboard
+ Receiving node copied to clipboard
Payments
diff --git a/libwallet/go.mod b/libwallet/go.mod
index 16ba4d4e..79239808 100644
--- a/libwallet/go.mod
+++ b/libwallet/go.mod
@@ -16,12 +16,14 @@ require (
github.com/pdfcpu/pdfcpu v0.3.11
github.com/pkg/errors v0.9.1
github.com/shopspring/decimal v1.2.0
- golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
- golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 // indirect
+ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
+ golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13 // indirect
+ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
+ golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.25.0
gopkg.in/gormigrate.v1 v1.6.0
)
// Fork that includes the -cache flag for quicker builds
-replace golang.org/x/mobile => github.com/champo/mobile v0.0.0-20210412201235-a784c99e2a62
+replace golang.org/x/mobile => github.com/champo/mobile v0.0.0-20220505154254-6a5f99bae305
diff --git a/libwallet/go.sum b/libwallet/go.sum
index be50686f..45b42254 100644
--- a/libwallet/go.sum
+++ b/libwallet/go.sum
@@ -82,6 +82,10 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/champo/mobile v0.0.0-20210412201235-a784c99e2a62 h1:6CturfaAc1IXi5udu7IMLekMFx6uB81XE7w9AGOqpyc=
github.com/champo/mobile v0.0.0-20210412201235-a784c99e2a62/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
+github.com/champo/mobile v0.0.0-20220503145505-51a7737dc434 h1:7KHSIWZ0Lc70SI4dTuF/8ZDBZWUKJfewCi3FpYHaqwk=
+github.com/champo/mobile v0.0.0-20220503145505-51a7737dc434/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
+github.com/champo/mobile v0.0.0-20220505154254-6a5f99bae305 h1:YgqwiwLKFqs1/d9BNXUduBA7YZ+uzfrjsKPzXBYAHsw=
+github.com/champo/mobile v0.0.0-20220505154254-6a5f99bae305/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
@@ -322,6 +326,8 @@ github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S0
github.com/urfave/cli v1.18.0 h1:m9MfmZWX7bwr9kUcs/Asr95j0IVXzGNNc+/5ku2m26Q=
github.com/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50 h1:ASw9n1EHMftwnP3Az4XW6e308+gNsrHzmdhd0Olz9Hs=
go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
@@ -340,6 +346,8 @@ golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
@@ -359,6 +367,8 @@ golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd h1:ePuNC7PZ6O5BzgPn9bZayER
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -378,8 +388,13 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -408,14 +423,25 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750 h1:ZBu6861dZq7xBnG1bn5SRU0vA8nx42at4+kP07FMTog=
golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -430,6 +456,9 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
+golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
+golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/libwallet/lnurl/lnurl.go b/libwallet/lnurl/lnurl.go
index 5144ba73..e935141a 100644
--- a/libwallet/lnurl/lnurl.go
+++ b/libwallet/lnurl/lnurl.go
@@ -135,7 +135,10 @@ func Withdraw(qr string, createInvoiceFunc CreateInvoiceFunction, allowUnsafe bo
// add request id to enhance error reports and troubleshooting with LNURL service providers
requestId := uuid.New().String()
- qrUrl.Query().Add("requestId", requestId)
+ // Mutate the query params so we keep those the original URL had
+ query := qrUrl.Query()
+ query.Add("requestId", requestId)
+ qrUrl.RawQuery = query.Encode()
notifier.SetRequestId(requestId)
// start withdraw with service
@@ -188,11 +191,12 @@ func Withdraw(qr string, createInvoiceFunc CreateInvoiceFunction, allowUnsafe bo
notifier.Status(StatusInvoiceCreated)
// Mutate the query params so we keep those the original URL had
- query := callbackURL.Query()
- query.Add("k1", wr.K1)
- query.Add("pr", invoice)
+ callbackQuery := callbackURL.Query()
+ callbackQuery.Add("requestId", requestId)
+ callbackQuery.Add("k1", wr.K1)
+ callbackQuery.Add("pr", invoice)
+ callbackURL.RawQuery = callbackQuery.Encode()
- callbackURL.RawQuery = query.Encode()
// Confirm withdraw with service
// Use an httpClient with a higher timeout for reliability with slow LNURL services
withdrawClient := http.Client{Timeout: 3 * time.Minute}
@@ -328,10 +332,10 @@ func decode(qr string) (*url.URL, error) {
}
if len(uri.Opaque) > 0 {
- // This catches scheme:LNURL
+ // This catches lightning:LNURL
toParse = uri.Opaque
} else {
- // And this catches scheme://LNURL which is needed for iOS
+ // And this catches lightning://LNURL which is needed for iOS
toParse = uri.Host
}
}
diff --git a/libwallet/lnurl/lnurl_test.go b/libwallet/lnurl/lnurl_test.go
index 0b490708..ddd082f6 100644
--- a/libwallet/lnurl/lnurl_test.go
+++ b/libwallet/lnurl/lnurl_test.go
@@ -388,6 +388,9 @@ func TestNoRouteCheck(t *testing.T) {
func TestExtraQueryParams(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Query().Get("requestId") == "" {
+ t.Fatalf("Expected non-empty requestId in query params. Got URL: %v", r.URL.String())
+ }
json.NewEncoder(w).Encode(&WithdrawResponse{
K1: "foobar",
Callback: "http://" + r.Host + "/withdraw/complete?foo=bar",
@@ -397,6 +400,9 @@ func TestExtraQueryParams(t *testing.T) {
})
})
mux.HandleFunc("/withdraw/complete", func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Query().Get("requestId") == "" {
+ t.Fatalf("Expected non-empty requestId in query params. Got URL: %v", r.URL.String())
+ }
if r.URL.Query().Get("foo") != "bar" {
t.Fatalf("Expected foo=bar in query params. Got URL: %v", r.URL.String())
}
diff --git a/linters/checkstyle/linter.py b/linters/checkstyle/linter.py
index ac529f8b..d30df14c 100755
--- a/linters/checkstyle/linter.py
+++ b/linters/checkstyle/linter.py
@@ -1,11 +1,11 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
import subprocess
import sys
def get_git_root():
- return subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).\
+ return subprocess.check_output(["git", "rev-parse", "--show-toplevel"], text = True).\
strip()
@@ -23,7 +23,7 @@ def run_checkstyle(jar_path, checks_path, suppressions_path, xpath_suppressions_
command.extend(files)
process = subprocess.Popen(command, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ stderr=subprocess.PIPE, text=True)
status = process.wait()
if status == 0:
@@ -50,7 +50,7 @@ def lint(files):
)
for result in results:
- print result
+ print(result)
return len(results) == 0