diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 228e10d7..775975ca 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -6,6 +6,27 @@ follow [https://changelog.md/](https://changelog.md/) guidelines. ## [Unreleased] +## [52.1] - 2024-08-02 + +### ADDED + +- Background notification processing reliability improvements +- Wallet delete checks client-side (e.g prevent it wallet not fully empty) + +### FIXED + +- Handling of missing or deprecated currencies +- Tiny text copy when updating emergency kit + +### CHANGED + +- Upgraded compiledSdkVersion and targetSdkVersion to 34 +- Upgraded go version to 1.21.11 +- Enhanced password input for change password flow (consistency with rest of the app) +- Enhanced error metadata for strange secure storage errors +- Removed never used "max fee" button and calculations +- Revamped to UI test suite. Enhancing reliability and coverage. + ## [52] - 2024-06-14 ### ADDED diff --git a/android/Dockerfile b/android/Dockerfile index 97a2826d..0781f849 100644 --- a/android/Dockerfile +++ b/android/Dockerfile @@ -3,7 +3,7 @@ FROM --platform=linux/amd64 openjdk:17-jdk-buster@sha256:9217da81dcff19e60861791 ENV NDK_VERSION 22.0.7026061 ENV ANDROID_PLATFORM_VERSION 28 ENV ANDROID_BUILD_TOOLS_VERSION 28.0.3 -ENV GO_VERSION 1.18.1 +ENV GO_VERSION 1.21.11 RUN apt-get update \ && apt-get install --yes --no-install-recommends \ diff --git a/android/apollo/build.gradle b/android/apollo/build.gradle index 1340d203..a95528c2 100644 --- a/android/apollo/build.gradle +++ b/android/apollo/build.gradle @@ -21,20 +21,17 @@ apply from: "${project.rootDir}/linters/pmd/check-android.gradle" //apply from: "${project.rootDir}/linters/findbugs/check-android.gradle" android { - compileSdkVersion 31 + compileSdk 34 defaultConfig { - minSdkVersion 19 - targetSdkVersion 31 - versionCode 1 - versionName "1.0" + minSdk 19 + targetSdk 34 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { minified { - debuggable true minifyEnabled true } } @@ -48,12 +45,12 @@ android { jvmTarget = JavaVersion.VERSION_1_8.toString() } - lintOptions { + lint { abortOnError true htmlReport true textReport true - lintConfig file("${project.rootDir}/linters/android-lint/config.xml") - baseline file("lint-baseline.xml") + lintConfig file("$rootDir/linters/android-lint/config.xml") + baseline file('lint-baseline.xml') } testOptions { diff --git a/android/apollo/src/main/java/io/muun/apollo/data/analytics/AnalyticsProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/analytics/AnalyticsProvider.kt index b2c5f5df..e453d3c5 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/analytics/AnalyticsProvider.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/analytics/AnalyticsProvider.kt @@ -55,7 +55,7 @@ class AnalyticsProvider @Inject constructor(context: Context) { // Avoid recursion (Timber.i reports a breadcrumb). TODO proper design and fix this if (event !is AnalyticsEvent.E_BREADCRUMB) { - Timber.i("AnalyticsProvider", event.toString()) + Timber.i("AnalyticsProvider: $event") } } catch (t: Throwable) { diff --git a/android/apollo/src/main/java/io/muun/apollo/data/external/Gen.kt b/android/apollo/src/main/java/io/muun/apollo/data/external/Gen.kt index 75f22cf5..010cc851 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/external/Gen.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/external/Gen.kt @@ -292,23 +292,6 @@ object Gen { private fun muunAddress() = MuunAddress(1, "m/1/2/3", address()) - /** - * Get a PaymentRequest - */ - fun payReq( - amount: MonetaryAmount = Money.of(0, "USD"), - feeRate: Double = 10.0, - takeFeeFromAmount: Boolean = false, - - ) = PaymentRequest( - type = PaymentRequest.Type.TO_ADDRESS, - amount = amount, - description = "foo", - address = address(), - feeInSatoshisPerByte = feeRate, - takeFeeFromAmount = takeFeeFromAmount - ) - fun submarineSwap( outputAmountInSatoshis: Long, sweepFeeInSatoshis: Long = 0, diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/BackgroundExecutionMetricsProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/BackgroundExecutionMetricsProvider.kt index 6241e0f1..16b8e008 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/os/BackgroundExecutionMetricsProvider.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/BackgroundExecutionMetricsProvider.kt @@ -11,7 +11,6 @@ import io.muun.apollo.data.net.ConnectivityInfoProvider import io.muun.apollo.data.net.NetworkInfoProvider import kotlinx.serialization.Serializable import java.util.Locale -import java.util.TimeZone import javax.inject.Inject private const val UNSUPPORTED = -1 @@ -27,6 +26,7 @@ class BackgroundExecutionMetricsProvider @Inject constructor( private val activityManagerInfoProvider: ActivityManagerInfoProvider, private val resourcesInfoProvider: ResourcesInfoProvider, private val systemCapabilitiesProvider: SystemCapabilitiesProvider, + private val dateTimeZoneProvider: DateTimeZoneProvider, ) { private val powerManager: PowerManager by lazy { @@ -54,7 +54,7 @@ class BackgroundExecutionMetricsProvider @Inject constructor( SystemClock.elapsedRealtime(), hardwareCapabilitiesProvider.bootCount, Locale.getDefault().toString(), - TimeZone.getDefault().rawOffset / 1000L, + dateTimeZoneProvider.timeZoneOffsetSeconds, telephonyInfoProvider.region.orElse(""), telephonyInfoProvider.simRegion, appInfoProvider.appDatadir, @@ -68,7 +68,10 @@ class BackgroundExecutionMetricsProvider @Inject constructor( systemCapabilitiesProvider.developerEnabled, connectivityInfoProvider.proxyHttp, connectivityInfoProvider.proxyHttps, - connectivityInfoProvider.proxySocks + connectivityInfoProvider.proxySocks, + dateTimeZoneProvider.autoDateTime, + dateTimeZoneProvider.autoTimeZone, + dateTimeZoneProvider.timeZoneId ) @Suppress("ArrayInDataClass") @@ -108,6 +111,9 @@ class BackgroundExecutionMetricsProvider @Inject constructor( private val proxyHttp: String, private val proxyHttps: String, private val proxySocks: String, + private val autoDateTime: Int, + private val autoTimeZone: Int, + private val timeZoneId: String, ) /** diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/DateTimeZoneProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/DateTimeZoneProvider.kt new file mode 100644 index 00000000..281b3696 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/DateTimeZoneProvider.kt @@ -0,0 +1,37 @@ +package io.muun.apollo.data.os + +import android.content.Context +import android.provider.Settings +import java.util.TimeZone +import javax.inject.Inject + +class DateTimeZoneProvider @Inject constructor(private val context: Context) { + + val autoTimeZone: Int + get() { + return Settings.Global.getInt( + context.contentResolver, + Settings.Global.AUTO_TIME_ZONE, + -1 + ) + } + + val autoDateTime: Int + get() { + return Settings.Global.getInt( + context.contentResolver, + Settings.Global.AUTO_TIME, + -1 + ) + } + + val timeZoneId: String + get() { + return TimeZone.getDefault().id.take(100) + } + + val timeZoneOffsetSeconds: Long + get() { + return TimeZone.getDefault().rawOffset / 1000L + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/SecureStoragePreferences.java b/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/SecureStoragePreferences.java index 103d5fe8..6f9bf591 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/SecureStoragePreferences.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/SecureStoragePreferences.java @@ -77,6 +77,7 @@ public byte[] getAesIv(String key) { */ public synchronized byte[] getPersistentSecureRandomBytes(String key, int size) { if (sharedPreferences.contains(key)) { + Timber.i("getPersistentSecureRandomBytes for " + key + ". Cached."); final byte[] iv = getBytes(key); // We've had a few InvalidKeyExceptions that might come from invalid IVs @@ -90,6 +91,7 @@ public synchronized byte[] getPersistentSecureRandomBytes(String key, int size) return iv; } else { + Timber.i("getPersistentSecureRandomBytes for " + key + ". Generate new"); final byte[] bytes = RandomGenerator.getBytes(size); saveBytes(bytes, key); return bytes; @@ -102,7 +104,14 @@ public synchronized byte[] getPersistentSecureRandomBytes(String key, int size) public void saveBytes(byte[] bytes, String key) { initSecureStorage(); - sharedPreferences.edit().putString(key, SerializationUtils.serializeBytes(bytes)).commit(); + final boolean writeSuccess = sharedPreferences.edit() + .putString(key, SerializationUtils.serializeBytes(bytes)) + .commit(); + + Timber.i("SaveBytes for " + key + " success:" + writeSuccess ); + if (!writeSuccess) { + Timber.e("Error while committing write to secure storage preferences"); + } } /** 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 3d0ce9cc..65fee134 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 @@ -123,6 +123,9 @@ public boolean has(String key) { // our error report infra offers more metadata/insights on this issue if (hasKeyInPreferences != hasKeyInKeystore) { final SecureStorageError error = new SecureStorageError(debugSnapshot()); + error.addMetadata("key", key); + error.addMetadata("hasKeyInPreferences", hasKeyInPreferences); + error.addMetadata("hasKeyInKeystore", hasKeyInKeystore); Timber.e(error); throw error; } @@ -196,6 +199,7 @@ private void storeEncrypted(String key, byte[] input) { preferences.saveBytes(keyStore.encryptData(input, key, preferences.getAesIv(key)), key); } catch (Throwable e) { Timber.i("SecureStorageError on WRITE for key: " + key); + Timber.e(e); final SecureStorageError ssError = new SecureStorageError(e, debugSnapshot()); enhanceError(ssError, key); throw ssError; diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserRepository.java index e47e3907..14807b8c 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserRepository.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserRepository.java @@ -12,6 +12,7 @@ import io.muun.apollo.domain.model.user.UserPhoneNumber; import io.muun.apollo.domain.model.user.UserProfile; import io.muun.common.Optional; +import io.muun.common.model.Currency; import io.muun.common.model.PhoneNumber; import io.muun.common.utils.Preconditions; @@ -32,6 +33,7 @@ import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; +import javax.money.CurrencyUnit; @Singleton public class UserRepository extends BaseRepository { @@ -487,26 +489,37 @@ public static class StoredUserJson { // and migrate the preference to a non-minified JSON this class is APPEND-ONLY. public long hid; + public String email; + public String createdAt; public String phoneNumber; + public boolean isPhoneNumberVerified; public String firstName; + public String lastName; + public String profilePictureUrl; public boolean isEmailVerified; + public boolean hasRecoveryCode; + public boolean hasPassword; + public boolean hasP2PEnabled; + public boolean hasExportedKeys; public String currency; public String emergencyKitLastExportedAt; + public Integer emergencyKitVersion; + public String emergencyKitExportMethod; @NonNull // Not backed by Houston, cached locally @@ -551,25 +564,27 @@ public StoredUserJson() { /** * Manual constructor. */ - public StoredUserJson(long hid, - String email, - String createdAt, - String phoneNumber, - boolean isPhoneNumberVerified, - String firstName, - String lastName, - String profilePictureUrl, - boolean isEmailVerified, - boolean hasRecoveryCode, - boolean hasPassword, - boolean hasP2PEnabled, - boolean hasExportedKeys, - String currency, - String emergencyKitLastExportedAt, - Integer emergencyKitVersion, - EmergencyKitExport.Method emergencyKitExportMethod, - @NonNull StoredEkVerificationCodes ekVerificationCodes, - @NonNull List ekVersions) { + public StoredUserJson( + long hid, + String email, + String createdAt, + String phoneNumber, + boolean isPhoneNumberVerified, + String firstName, + String lastName, + String profilePictureUrl, + boolean isEmailVerified, + boolean hasRecoveryCode, + boolean hasPassword, + boolean hasP2PEnabled, + boolean hasExportedKeys, + String currency, + String emergencyKitLastExportedAt, + Integer emergencyKitVersion, + EmergencyKitExport.Method emergencyKitExportMethod, + @NonNull StoredEkVerificationCodes ekVerificationCodes, + @NonNull List ekVersions + ) { this.hid = hid; this.email = email; @@ -608,7 +623,7 @@ User toUser() { ? Optional.of(new UserProfile(firstName, lastName, profilePictureUrl)) : Optional.empty(), - SerializationUtils.deserializeCurrencyUnit(currency != null ? currency : "USD"), + loadCurrencyFromStorage(), hasRecoveryCode, hasPassword, @@ -626,6 +641,18 @@ User toUser() { ); } + private CurrencyUnit loadCurrencyFromStorage() { + try { + final String currencyCode = currency != null ? currency : "USD"; + return SerializationUtils.deserializeCurrencyUnit(currencyCode); + + } catch (Exception e) { + // This can happen for example if user primary currency is no longer supported + // after an app or OS update. + return Currency.getUnit(Currency.DEFAULT.getCode()).get(); + } + } + void initEmergencyKitVersion() { if (emergencyKitLastExportedAt != null) { emergencyKitVersion = (int) Libwallet.EKVersionDescriptors; @@ -683,9 +710,11 @@ public StoredUserJson get(@NonNull String key, @NonNull SharedPreferences prefer } @Override - public void set(@NonNull String key, - @NonNull StoredUserJson value, - @NonNull SharedPreferences.Editor editor) { + public void set( + @NonNull String key, + @NonNull StoredUserJson value, + @NonNull SharedPreferences.Editor editor + ) { super.set(key, value, editor); } } diff --git a/android/apollo/src/main/java/io/muun/apollo/data/serialization/SafeCurrencyUnitDeserializer.kt b/android/apollo/src/main/java/io/muun/apollo/data/serialization/SafeCurrencyUnitDeserializer.kt new file mode 100644 index 00000000..058f84a1 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/serialization/SafeCurrencyUnitDeserializer.kt @@ -0,0 +1,33 @@ +package io.muun.apollo.data.serialization + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import io.muun.apollo.domain.errors.MissingCurrencyError +import io.muun.apollo.domain.utils.DeprecatedCurrencyUnit +import timber.log.Timber +import java.io.IOException +import javax.money.CurrencyUnit +import javax.money.Monetary +import javax.money.UnknownCurrencyException + +class SafeCurrencyUnitDeserializer : JsonDeserializer() { + + @Throws(IOException::class) + override fun deserialize(parser: JsonParser, context: DeserializationContext): CurrencyUnit { + val currencyCode = parser.valueAsString + + if (Monetary.isCurrencyAvailable(currencyCode)) { + return Monetary.getCurrency(currencyCode) + + } else { + // In practice, only this type of error should arise. + Timber.e(MissingCurrencyError(UnknownCurrencyException(currencyCode))) + + // This can happen for example if user primary currency is no longer supported + // after an app or OS update, or if user has changed to a device that no longer supports + // their primary currency. + return DeprecatedCurrencyUnit(currencyCode) + } + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/serialization/SerializationUtils.java b/android/apollo/src/main/java/io/muun/apollo/data/serialization/SerializationUtils.java index 11e72515..2c1fc596 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/serialization/SerializationUtils.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/serialization/SerializationUtils.java @@ -8,6 +8,7 @@ import io.muun.apollo.domain.errors.data.MuunDeserializationError; import io.muun.apollo.domain.model.BitcoinAmount; import io.muun.apollo.domain.utils.DateUtils; +import io.muun.apollo.domain.utils.DeprecatedCurrencyUnit; import io.muun.common.dates.MuunZonedDateTime; import io.muun.common.model.PhoneNumber; @@ -63,11 +64,13 @@ public final class SerializationUtils { .addDeserializer(PhoneNumber.class, new PhoneNumberDeserializer()) .addSerializer(BitcoinAmount.class, new BitcoinAmountSerializer()) - .addDeserializer(BitcoinAmount.class, new BitcoinAmountDeserializer()); + .addDeserializer(BitcoinAmount.class, new BitcoinAmountDeserializer()) + + .addDeserializer(CurrencyUnit.class, new SafeCurrencyUnitDeserializer()); JSON_MAPPER = new ObjectMapper() - .registerModule(simpleModule) - .registerModule(new MoneyModule()); + .registerModule(new MoneyModule()) + .registerModule(simpleModule); // Last so our custom deserializers take precedence // Allows unknown Enum values to be ignored and a predefined value specified through // @JsonEnumDefaultValue annotation. If enabled, but no predefined default Enum value is @@ -90,8 +93,10 @@ public static > String serializeEnum(@NotNull T enumValue) { * Deserialize an enum. */ @NotNull - public static > T deserializeEnum(@NotNull Class enumClass, - @NotNull String enumString) { + public static > T deserializeEnum( + @NotNull Class enumClass, + @NotNull String enumString + ) { return Enum.valueOf(enumClass, enumString); } @@ -131,8 +136,10 @@ public static String serializeJson(@NotNull Class jsonType, @NotNull T js * Serialize a class to JSON. */ @NotNull - public static String serializeJson(@NotNull TypeReference jsonType, - @NotNull T jsonValue) { + public static String serializeJson( + @NotNull TypeReference jsonType, + @NotNull T jsonValue + ) { try { return JSON_MAPPER.writerFor(jsonType).writeValueAsString(jsonValue); @@ -158,8 +165,10 @@ public static T deserializeJson(@NotNull Class jsonType, @NotNull String * Deserialize a class from JSON. */ @NotNull - public static T deserializeJson(@NotNull TypeReference jsonType, - @NotNull String jsonString) { + public static T deserializeJson( + @NotNull TypeReference jsonType, + @NotNull String jsonString + ) { try { return JSON_MAPPER.readValue(jsonString, jsonType); @@ -214,7 +223,6 @@ public static String serializeCurrencyUnit(@NotNull CurrencyUnit currencyValue) */ @NotNull public static CurrencyUnit deserializeCurrencyUnit(@NotNull String currencyString) { - try { return Monetary.getCurrency(currencyString); @@ -253,11 +261,19 @@ public static MonetaryAmount deserializeMonetaryAmount(@NotNull String moneyStri } final BigDecimal number = deserializeBigDecimal(parts[0]); - final CurrencyUnit currency = deserializeCurrencyUnit(parts[1]); - + final CurrencyUnit currency = safeDeserializeCurrencyUnit(parts[1]); return Money.of(number, currency); } + private static CurrencyUnit safeDeserializeCurrencyUnit(@NotNull String currencyString) { + if (!Monetary.isCurrencyAvailable(currencyString)) { + return new DeprecatedCurrencyUnit(currencyString); + + } else { + return deserializeCurrencyUnit(currencyString); + } + } + /** * Serialize a BitcoinAmount. */ @@ -282,8 +298,8 @@ public static BitcoinAmount deserializeBitcoinAmount(@NotNull String string) { } final Long inSatoshis = Long.valueOf(parts[0]); - final MonetaryAmount inInputCurrency = deserializeMonetaryAmount(parts[1]); - final MonetaryAmount inPrimaryCurrency = deserializeMonetaryAmount(parts[2]); + final MonetaryAmount inInputCurrency = deserializeMonetaryAmount(parts[1]); + final MonetaryAmount inPrimaryCurrency = deserializeMonetaryAmount(parts[2]); return new BitcoinAmount(inSatoshis, inInputCurrency, inPrimaryCurrency); } @@ -335,9 +351,11 @@ public static String serializeMap(Class keyType, Class valueType, M /** * Deserialize a list of objects from a JSON array. */ - public static Map deserializeMap(Class keyType, - Class valueType, - String json) { + public static Map deserializeMap( + Class keyType, + Class valueType, + String json + ) { try { return JSON_MAPPER .readerFor(TYPE_FACTORY.constructMapType(Map.class, keyType, valueType)) diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/Flags.java b/android/apollo/src/main/java/io/muun/apollo/domain/Flags.java index 760918e3..4e116603 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/Flags.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/Flags.java @@ -5,6 +5,4 @@ */ public class Flags { - public static final boolean USE_MAXIMUM_FEE_ENABLED = false; - } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/LogoutActions.java b/android/apollo/src/main/java/io/muun/apollo/domain/action/LogoutActions.java index 1baa678e..5b8d6517 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/LogoutActions.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/LogoutActions.java @@ -100,11 +100,14 @@ public void destroyRecoverableWallet() { } /** - * Wipe all user associated data from the app (unrecoverable only). + * Wipe all user associated data from the app. + * Note: if user is unrecoverable or recoverable user has performed "delete wallet" this + * action is irreversible (and its intended to be). We're naming this "dangerously" because + * callers should be careful when calling this. */ - public void dangerouslyDestroyUnrecoverableWallet() { + public void dangerouslyDestroyWallet() { final LogoutOptions logoutOptions = logoutOptionsSel.get(); - Preconditions.checkState(!logoutOptions.isBlocked()); // just checking + Preconditions.checkState(logoutOptions.canDeleteWallet()); // just checking destroyWallet(); } diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveBitcoinUriAction.java b/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveBitcoinUriAction.java index fa18bf3b..223bb2b5 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveBitcoinUriAction.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveBitcoinUriAction.java @@ -1,34 +1,27 @@ package io.muun.apollo.domain.action.operation; -import io.muun.apollo.data.preferences.FeeWindowRepository; import io.muun.apollo.domain.action.base.BaseAsyncAction1; import io.muun.apollo.domain.errors.newop.InvalidPaymentRequestError; import io.muun.apollo.domain.libwallet.LibwalletBridge; import io.muun.apollo.domain.model.BitcoinUriContent; -import io.muun.apollo.domain.model.FeeWindow; import io.muun.apollo.domain.model.OperationUri; import io.muun.apollo.domain.model.PaymentRequest; -import io.muun.apollo.domain.utils.StringUtils; -import io.muun.common.utils.BitcoinUtils; import rx.Observable; import javax.inject.Inject; import javax.inject.Singleton; -import javax.money.MonetaryAmount; @Singleton public class ResolveBitcoinUriAction extends BaseAsyncAction1 { - private final FeeWindowRepository feeWindowRepository; /** * Resolves a Bitcoin URI, using BIP-72 or BIP-21 as appropriate. */ @Inject - public ResolveBitcoinUriAction(FeeWindowRepository feeWindowRepository) { + public ResolveBitcoinUriAction() { - this.feeWindowRepository = feeWindowRepository; } @Override @@ -44,22 +37,9 @@ public Observable action(OperationUri operationUri) { } private PaymentRequest resolveBitcoinUri(OperationUri uri) { - final FeeWindow feeWindow = feeWindowRepository.fetchOne(); - final BitcoinUriContent uriContent = LibwalletBridge.getBitcoinUriContent(uri); - final MonetaryAmount amount = (uriContent.amountInStatoshis != null) - ? BitcoinUtils.satoshisToBitcoins(uriContent.amountInStatoshis) - : null; - - final String description = StringUtils.joinText(": ", new String[]{ - uriContent.merchant, - uriContent.memo - }); - - final double feeRate = feeWindow.getFastestFeeInSatoshisPerByte(); - - return PaymentRequest.toAddress(uriContent.address, amount, description, feeRate); + return PaymentRequest.toAddress(uriContent.address); } } diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveLnInvoiceAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveLnInvoiceAction.kt index 07b84ad6..fe0d008c 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveLnInvoiceAction.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveLnInvoiceAction.kt @@ -3,7 +3,6 @@ package io.muun.apollo.domain.action.operation import androidx.annotation.VisibleForTesting import io.muun.apollo.data.net.HoustonClient import io.muun.apollo.data.preferences.BackgroundTimesRepository -import io.muun.apollo.data.preferences.FeeWindowRepository import io.muun.apollo.data.preferences.KeysRepository import io.muun.apollo.domain.action.base.BaseAsyncAction2 import io.muun.apollo.domain.analytics.NewOperationOrigin @@ -12,14 +11,12 @@ import io.muun.apollo.domain.errors.newop.InvoiceExpiredException import io.muun.apollo.domain.libwallet.DecodedInvoice import io.muun.apollo.domain.libwallet.Invoice.decodeInvoice import io.muun.apollo.domain.model.PaymentRequest -import io.muun.apollo.domain.model.PaymentRequest.Companion.toLnInvoice import io.muun.apollo.domain.model.SubmarineSwap import io.muun.apollo.domain.model.SubmarineSwapRequest import io.muun.apollo.domain.utils.DateUtils import io.muun.common.api.SubmarineSwapJson import io.muun.common.crypto.hd.PublicKey import io.muun.common.crypto.hd.PublicKeyPair -import io.muun.common.utils.BitcoinUtils import io.muun.common.utils.Encodings import io.muun.common.utils.Hashes import io.muun.common.utils.LnInvoice @@ -45,14 +42,12 @@ import org.bitcoinj.script.ScriptOpCodes.OP_SWAP import rx.Observable import javax.inject.Inject import javax.inject.Singleton -import javax.money.MonetaryAmount @Singleton class ResolveLnInvoiceAction @Inject internal constructor( private val network: NetworkParameters, private val houstonClient: HoustonClient, private val keysRepository: KeysRepository, - private val feeWindowRepository: FeeWindowRepository, private val backgroundTimesRepository: BackgroundTimesRepository ) : BaseAsyncAction2() { @@ -99,9 +94,6 @@ class ResolveLnInvoiceAction @Inject internal constructor( } private fun buildPaymentRequest(invoice: DecodedInvoice, swap: SubmarineSwap): PaymentRequest { - val feeWindow = feeWindowRepository.fetchOne() - val amount = getInvoiceAmount(invoice) - if (!swap.isLend) { validateNonLendSwap(invoice, swap) } @@ -110,14 +102,9 @@ class ResolveLnInvoiceAction @Inject internal constructor( throw InvalidSwapException(swap.houstonUuid) } - // For AmountLess Invoices, fee rate is initially unknown - val feeRate = if (invoice.amountInSat != null) feeWindow.getFeeRate(swap) else null - return toLnInvoice( + return PaymentRequest.toLnInvoice( invoice, - amount, - invoice.description, swap, - feeRate ) } @@ -143,13 +130,6 @@ class ResolveLnInvoiceAction @Inject internal constructor( } } - private fun getInvoiceAmount(invoice: DecodedInvoice): MonetaryAmount? { - return if (invoice.amountInSat != null) { - BitcoinUtils.satoshisToBitcoins(invoice.amountInSat) - } else - null - } - /** * Create a new Submarine Swap. */ diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveMuunUriAction.java b/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveMuunUriAction.java index 7119ceb1..b823a004 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveMuunUriAction.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/operation/ResolveMuunUriAction.java @@ -1,45 +1,38 @@ package io.muun.apollo.domain.action.operation; import io.muun.apollo.data.db.contact.ContactDao; -import io.muun.apollo.data.preferences.FeeWindowRepository; import io.muun.apollo.data.preferences.UserRepository; import io.muun.apollo.domain.action.base.BaseAsyncAction1; import io.muun.apollo.domain.model.Contact; import io.muun.apollo.domain.model.ExchangeRateWindow; -import io.muun.apollo.domain.model.FeeWindow; import io.muun.apollo.domain.model.OperationUri; import io.muun.apollo.domain.model.PaymentRequest; import io.muun.apollo.domain.model.user.User; import io.muun.apollo.domain.selector.ExchangeRateSelector; -import org.javamoney.moneta.Money; import rx.Observable; -import java.math.BigDecimal; import javax.inject.Inject; import javax.inject.Singleton; -import javax.money.MonetaryAmount; @Singleton public class ResolveMuunUriAction extends BaseAsyncAction1 { private final UserRepository userRepository; private final ContactDao contactDao; - private final FeeWindowRepository feeWindowRepository; private final ExchangeRateSelector rateSelector; /** * Resolves a Muun URI, fetching User and/or Contact as needed. */ @Inject - public ResolveMuunUriAction(UserRepository userRepository, - ContactDao contactDao, - FeeWindowRepository feeWindowRepository, - ExchangeRateSelector rateSelector) { - + public ResolveMuunUriAction( + UserRepository userRepository, + ContactDao contactDao, + ExchangeRateSelector rateSelector + ) { this.userRepository = userRepository; this.contactDao = contactDao; - this.feeWindowRepository = feeWindowRepository; this.rateSelector = rateSelector; } @@ -50,7 +43,6 @@ public Observable action(OperationUri operationUri) { private PaymentRequest resolveMuunUri(OperationUri uri) { final User user = userRepository.fetchOne(); - final FeeWindow feeWindow = feeWindowRepository.fetchOne(); // TODO: this could cause unexpected behaviour since it may not be same rate window as // the one used in paymentContext. We've seen rates for some currencies suddenly being // dropped which may cause trouble if the primary currency is one of them. @@ -66,13 +58,6 @@ private PaymentRequest resolveMuunUri(OperationUri uri) { final String descriptionParam = uri.getParam(OperationUri.MUUN_DESCRIPTION) .orElse(""); - final MonetaryAmount amount = Money.of( - new BigDecimal(amountParam), - currencyParam.toUpperCase() - ); - - final double feeRate = feeWindow.getFastestFeeInSatoshisPerByte(); - switch (uri.getHost()) { case OperationUri.MUUN_HOST_CONTACT: final Contact contact = contactDao @@ -80,11 +65,11 @@ private PaymentRequest resolveMuunUri(OperationUri uri) { .toBlocking() .first(); - return PaymentRequest.toContact(contact, amount, descriptionParam, feeRate); + return PaymentRequest.toContact(contact); case OperationUri.MUUN_HOST_EXTERNAL: final String externalAddress = uri.getExternalAddress(); - return PaymentRequest.toAddress(externalAddress, amount, descriptionParam, feeRate); + return PaymentRequest.toAddress(externalAddress); default: throw new IllegalArgumentException("Invalid host: " + uri.getHost()); diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/BitcoinUri.kt b/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/BitcoinUri.kt index 23d465a3..4fbee3d9 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/BitcoinUri.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/BitcoinUri.kt @@ -1,6 +1,7 @@ package io.muun.apollo.domain.libwallet import androidx.annotation.VisibleForTesting +import io.muun.apollo.data.external.Globals import io.muun.apollo.domain.model.BitcoinAmount import io.muun.common.utils.BitcoinUtils import libwallet.Libwallet @@ -31,4 +32,17 @@ object BitcoinUri { .number .numberValue(BigDecimal::class.java) .toPlainString() // Avoid scientific notation for golang to parse/compare smoothly + + /** + * This is a utility method meant EXCLUSIVELY for testing. It's required since UiTests can't + * directly call Libwallet's go code yet. + * Note: DO NOT use in main code. + */ + @VisibleForTesting + fun parse(rawBip21Uri: String): Triple { + val bitcoinUri = io.muun.common.bitcoinj.BitcoinUri(Globals.INSTANCE.network, rawBip21Uri) + val invoice = bitcoinUri.getParameterByName("lightning") as String + return Triple(bitcoinUri.address!!, bitcoinUri.amount?.value, invoice) + } + } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/model/NextTransactionSize.java b/android/apollo/src/main/java/io/muun/apollo/domain/model/NextTransactionSize.java index d2037020..2ad67a31 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/model/NextTransactionSize.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/model/NextTransactionSize.java @@ -58,7 +58,7 @@ public long getUtxoBalance() { * Get the spendable balance (considering debt). */ public long getUserBalance() { - return Preconditions.checkNonNegative(getUtxoBalance() - getExpectedDebtInSat()); + return Preconditions.checkNotNegative(getUtxoBalance() - getExpectedDebtInSat()); } /** diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/model/PaymentRequest.kt b/android/apollo/src/main/java/io/muun/apollo/domain/model/PaymentRequest.kt index 75461263..b3edf05b 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/model/PaymentRequest.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/model/PaymentRequest.kt @@ -2,18 +2,12 @@ package io.muun.apollo.domain.model import io.muun.apollo.domain.libwallet.DecodedInvoice import io.muun.common.utils.Preconditions -import javax.money.MonetaryAmount data class PaymentRequest( val type: Type, - val amount: MonetaryAmount? = null, - val description: String? = null, val contact: Contact? = null, val address: String? = null, - val invoice: DecodedInvoice? = null, val swap: SubmarineSwap? = null, - val feeInSatoshisPerByte: Double?, //initially null for AmountLess Invoice - val takeFeeFromAmount: Boolean = false, ) { enum class Type { @@ -28,19 +22,13 @@ data class PaymentRequest( @JvmStatic fun toContact( contact: Contact, - amount: MonetaryAmount, - description: String, - feeInSatoshisPerByte: Double, ): PaymentRequest { Preconditions.checkNotNull(contact) return PaymentRequest( Type.TO_CONTACT, - amount = amount, - description = description, contact = contact, - feeInSatoshisPerByte = feeInSatoshisPerByte ) } @@ -48,19 +36,13 @@ data class PaymentRequest( @JvmStatic fun toAddress( address: String, - amount: MonetaryAmount?, - description: String?, - feeInSatoshisPerByte: Double, ): PaymentRequest { Preconditions.checkNotNull(address) return PaymentRequest( Type.TO_ADDRESS, - amount = amount, - description = description, address = address, - feeInSatoshisPerByte = feeInSatoshisPerByte ) } @@ -68,21 +50,14 @@ data class PaymentRequest( @JvmStatic fun toLnInvoice( invoice: DecodedInvoice, - amount: MonetaryAmount?, - description: String, submarineSwap: SubmarineSwap, - feeInSatoshisPerByte: Double?, ): PaymentRequest { Preconditions.checkNotNull(invoice) return PaymentRequest( Type.TO_LN_INVOICE, - amount = amount, - description = description, - invoice = invoice, swap = submarineSwap, - feeInSatoshisPerByte = feeInSatoshisPerByte ) } } diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/model/user/User.java b/android/apollo/src/main/java/io/muun/apollo/domain/model/user/User.java index ff999cea..11ea5d43 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/model/user/User.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/model/user/User.java @@ -169,7 +169,10 @@ public CurrencyUnit getPrimaryCurrency(ExchangeRateProvider rateProvider) { // flawed. It will (strangely) return true when there's no rate for certain currencies. if (rateProvider.getCurrencies().contains(targetCurrency)) { return targetCurrency; + } else { + // TODO this "defaulting" to btc is contrary to our defaulting to usd elsewhere + // We should probably unify behavior. return Currency.getUnit("BTC").get(); } } diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/selector/LogoutOptionsSelector.kt b/android/apollo/src/main/java/io/muun/apollo/domain/selector/LogoutOptionsSelector.kt index 2588463d..d111415c 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/selector/LogoutOptionsSelector.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/selector/LogoutOptionsSelector.kt @@ -1,28 +1,30 @@ package io.muun.apollo.domain.selector import io.muun.common.model.OperationStatus +import io.muun.common.utils.Preconditions import rx.Observable import javax.inject.Inject class LogoutOptionsSelector @Inject constructor( private val userSel: UserSelector, private val paymentContextSel: PaymentContextSelector, - private val operationSel: OperationSelector + private val operationSel: OperationSelector, ) { class LogoutOptions( private val isRecoverable: Boolean, private val hasBalance: Boolean, private val hasUnsettledOps: Boolean, - private val hasPendingIncomingSwaps: Boolean + private val hasPendingIncomingSwaps: Boolean, ) { - fun isBlocked(): Boolean { - return if (isRecoverable) { - hasPendingIncomingSwaps - } else { - hasBalance || hasUnsettledOps - } + fun isLogoutBlocked(): Boolean { + Preconditions.checkArgument(isRecoverable) + return hasPendingIncomingSwaps + } + + fun canDeleteWallet(): Boolean { + return !hasBalance && !hasUnsettledOps } fun isRecoverable(): Boolean { diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/utils/DeprecatedCurrencyUnit.kt b/android/apollo/src/main/java/io/muun/apollo/domain/utils/DeprecatedCurrencyUnit.kt new file mode 100644 index 00000000..562c7f63 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/domain/utils/DeprecatedCurrencyUnit.kt @@ -0,0 +1,44 @@ +package io.muun.apollo.domain.utils + +import javax.money.CurrencyContext +import javax.money.CurrencyContextBuilder +import javax.money.CurrencyUnit + +/** + * This is our "placeholder" ad-hoc CurrencyUnit implementation. It helps us in the edge case where + * a user's primary currency is no longer supported (by the Moneta lib) after an app or OS update, + * or if user has changed to a device that no longer supports their primary currency. Notice this + * situation also affects operation in user's operation history (e.g an operation amount is recorded + * in btc and the user's primary currency at the time of the operation). + * + * Note: main goal for this class is to be able to distinguish a primary currency when its + * deprecated (via instance of) and to still be able to display amounts in + * {@link #wrappedCurrencyCode} in the user's operation history. + */ +class DeprecatedCurrencyUnit(private val wrappedCurrencyCode: String) : CurrencyUnit { + + override fun compareTo(other: CurrencyUnit?): Int { + if (other == null) { + return -1 + } + + // wrappedCurrencyCode is deprecated so we shouldn't find it "in the wild" + return wrappedCurrencyCode.compareTo(other.currencyCode) + } + + override fun getCurrencyCode(): String = + wrappedCurrencyCode + + override fun getNumericCode(): Int = + 0 + + /** + * Using 0 default fraction digits as we expect currencies to be deprecated to be high + * inflationary or otherwise very devalued. + */ + override fun getDefaultFractionDigits(): Int = + 0 + + override fun getContext(): CurrencyContext = + CurrencyContextBuilder.of("FAKE_PROVIDER").build() +} \ No newline at end of file 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 8e02bbce..fff4230f 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 @@ -24,6 +24,7 @@ import java.util.concurrent.TimeoutException import javax.crypto.BadPaddingException import javax.money.Monetary import javax.money.MonetaryException +import javax.money.UnknownCurrencyException fun Observable.toVoid(): Observable = map(RxHelper::toVoid) @@ -54,6 +55,13 @@ fun T.applyArgs(f: Bundle.() -> Unit) = arguments = (arguments ?: Bundle()).apply(f) } + +/** + * Needed as inline reified functions can't be called from Java. + */ +fun Throwable.isInstanceOrIsCausedByUnknownCurrencyException() = + isInstanceOrIsCausedByError() + /** * Needed as inline reified functions can't be called from Java. */ diff --git a/android/apollo/src/test/java/io/muun/apollo/domain/action/OperationActionsTest.java b/android/apollo/src/test/java/io/muun/apollo/domain/action/OperationActionsTest.java index 52f27161..bea2283b 100644 --- a/android/apollo/src/test/java/io/muun/apollo/domain/action/OperationActionsTest.java +++ b/android/apollo/src/test/java/io/muun/apollo/domain/action/OperationActionsTest.java @@ -9,7 +9,6 @@ import io.muun.apollo.domain.action.operation.OperationMetadataMapper; import io.muun.apollo.domain.action.operation.SubmitPaymentAction; import io.muun.apollo.domain.model.Contact; -import io.muun.apollo.domain.model.ExchangeRateWindow; import io.muun.apollo.domain.model.Operation; import io.muun.apollo.domain.model.PaymentRequest; import io.muun.apollo.domain.model.PreparedPayment; @@ -18,9 +17,7 @@ import io.muun.common.crypto.hd.MuunAddress; import io.muun.common.crypto.hd.PrivateKey; import io.muun.common.crypto.hd.PublicKey; -import io.muun.common.model.ExchangeRateProvider; import io.muun.common.model.OperationDirection; -import io.muun.common.utils.BitcoinUtils; import androidx.core.util.Pair; import br.com.six2six.fixturefactory.Fixture; @@ -33,7 +30,6 @@ import rx.Observable; import java.util.List; -import javax.money.MonetaryAmount; import static io.muun.apollo.TestUtils.fetchItemFromObservable; import static org.assertj.core.api.Assertions.assertThat; @@ -106,8 +102,6 @@ public void fetchReplaceOperations() { @Test public void buildPaymentToContact() { - final ExchangeRateWindow rates = Fixture.from(ExchangeRateWindow.class).gimme("valid"); - final Contact contact = Fixture.from(Contact.class).gimme("valid"); final PublicKey publicKey = contact.publicKey; final MuunAddress contactAddress = new MuunAddress( @@ -118,10 +112,7 @@ public void buildPaymentToContact() { final long someFee = 123456; final PaymentRequest payReq = PaymentRequest.toContact( - contact, - TemplateHelpers.money().generateValue(), - "some description", - 10.0 + contact ); doReturn(contact.publicProfile) @@ -156,14 +147,6 @@ public void buildPaymentToContact() { contactAddress.getDerivationPath() ); - // check amount - final ExchangeRateProvider provider = new ExchangeRateProvider(rates.toJson()); - final MonetaryAmount inBtc = payReq.getAmount().with(provider.getCurrencyConversion("BTC")); - final long inSatoshis = BitcoinUtils.bitcoinsToSatoshis(inBtc); - - assertThat(operation.amount.inInputCurrency).isEqualTo(payReq.getAmount()); - assertThat(operation.amount.inSatoshis).isEqualTo(inSatoshis); - // check fee assertThat(operation.fee.inSatoshis).isEqualTo(someFee); } @@ -171,13 +154,8 @@ public void buildPaymentToContact() { @Test public void buildPaymentToAddress() { - final ExchangeRateWindow rates = Fixture.from(ExchangeRateWindow.class).gimme("valid"); - final PaymentRequest payReq = PaymentRequest.toAddress( - TemplateHelpers.address().generateValue(), - TemplateHelpers.money().generateValue(), - "some description", - 10.0 + TemplateHelpers.address().generateValue() ); final long someFee = 123456; @@ -205,14 +183,6 @@ public void buildPaymentToAddress() { assertThat(operation.receiverAddress).isEqualTo(payReq.getAddress()); assertThat(operation.receiverAddressDerivationPath).isEqualTo(null); - // check amount - final ExchangeRateProvider provider = new ExchangeRateProvider(rates.toJson()); - final MonetaryAmount inBtc = payReq.getAmount().with(provider.getCurrencyConversion("BTC")); - final long inSatoshis = BitcoinUtils.bitcoinsToSatoshis(inBtc); - - assertThat(operation.amount.inInputCurrency).isEqualTo(payReq.getAmount()); - assertThat(operation.amount.inSatoshis).isEqualTo(inSatoshis); - // check fee assertThat(operation.fee.inSatoshis).isEqualTo(someFee); } diff --git a/android/apolloui/build.gradle b/android/apolloui/build.gradle index 727f11dc..502e5b68 100644 --- a/android/apolloui/build.gradle +++ b/android/apolloui/build.gradle @@ -84,7 +84,7 @@ static def configExternalLinks(productFlavor, String host) { android { - compileSdkVersion 33 + compileSdk 34 buildFeatures { viewBinding true @@ -92,10 +92,10 @@ android { defaultConfig { applicationId "io.muun.apollo" - minSdkVersion 19 - targetSdkVersion 33 - versionCode 1200 - versionName "52" + minSdk 19 + targetSdk 34 + versionCode 1201 + versionName "52.1" // 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/ @@ -282,12 +282,12 @@ android { jvmTarget = JavaVersion.VERSION_1_8.toString() } - lintOptions { + lint { abortOnError true htmlReport true textReport true - lintConfig file("${project.rootDir}/linters/android-lint/config.xml") - baseline file("lint-baseline.xml") + lintConfig file("$rootDir/linters/android-lint/config.xml") + baseline file('lint-baseline.xml') } packagingOptions { diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/LoginAndSignUpTests.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/LoginAndSignUpTests.kt index a65ec396..aac228e7 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/LoginAndSignUpTests.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/LoginAndSignUpTests.kt @@ -110,7 +110,7 @@ open class LoginAndSignUpTests : BaseInstrumentationTest() { signInScreen.awaitEmailVerification(user.email) signInScreen.back() - signInScreen.abortDialogCancel() + signInScreen.abortDialogCancelWithSafeguard() signInScreen.back() signInScreen.abortDialogAbort() @@ -130,7 +130,7 @@ open class LoginAndSignUpTests : BaseInstrumentationTest() { signInScreen.back() signInScreen.back() - signInScreen.abortDialogCancel() + signInScreen.abortDialogCancelWithSafeguard() signInScreen.back() signInScreen.abortDialogAbort() } @@ -143,31 +143,64 @@ open class LoginAndSignUpTests : BaseInstrumentationTest() { autoFlows.signUp() autoFlows.receiveMoneyFromNetwork(Money.of(0.02, "BTC")) - // Case 1) U.u user with balance > 0 but unconfirmed receiving tx + testDeleteWalletCases(isRecoverableUser = false) + } + + @Test + fun test_06_a_recoverable_user_can_delete_wallet() { + val user = RandomUser() + + autoFlows.createRecoverableUser(user.pin, user.email, user.password) + val recoveryCodeParts = autoFlows.setUpRecoveryCode() + + autoFlows.receiveMoneyFromNetwork(Money.of(0.02, "BTC")) + + testDeleteWalletCases(isRecoverableUser = true) + + // Let's check user credentials don't work anymore + signInScreen.startLogin() + + // Reject invalid email: + signInScreen.checkEmailConfirmEnabled(false) + signInScreen.enterEmail(user.email) + signInScreen.checkEmailConfirmEnabled(true) + signInScreen.confirmEmail() + signInScreen.checkEmailError() + + signInScreen.recoverWithRecoveryCode() + signInScreen.enterRecoveryCode(recoveryCodeParts) + signInScreen.confirmRecoveryCodeOnlyLogin() + + label(R.string.error_incorrect_recovery_code) + } + + private fun testDeleteWalletCases(isRecoverableUser: Boolean) { + + // Case 1) user with balance > 0 but unconfirmed receiving tx autoFlows.checkCannotDeleteWallet() - // Case 2) U.u user with balance > 0 with confirmed receiving tx but not settled + // Case 2) user with balance > 0 with confirmed receiving tx but not settled generateBlocksAndWaitForUpdate(1) autoFlows.checkCannotDeleteWallet() - // Case 3) U.u user with balance > 0 with settled receiving tx + // Case 3) user with balance > 0 with settled receiving tx generateBlocksAndWaitForUpdate(5) // Transaction Settled autoFlows.checkCannotDeleteWallet() - // Case 4) U.u user with balance = 0 but unconfirmed spending tx + // Case 4) user with balance = 0 but unconfirmed spending tx autoFlows.spendAllFunds("some description") autoFlows.checkCannotDeleteWallet() - // Case 5) U.u user with balance = 0 with confirmed spending tx but not settled + // Case 5) user with balance = 0 with confirmed spending tx but not settled generateBlocksAndWaitForUpdate(1) autoFlows.checkCannotDeleteWallet() - // Case 6) U.u user with balance = 0 with settled spending tx + // Case 6) user with balance = 0 with settled spending tx generateBlocksAndWaitForUpdate(5) // Transaction Settled - autoFlows.deleteWallet() + autoFlows.deleteWallet(isRecoverableUser = isRecoverableUser) // TODO make more convoluted scenarios? Failed txs, failed swaps, etc...? } diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/ReceiveTests.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/ReceiveTests.kt new file mode 100644 index 00000000..be63cdde --- /dev/null +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/ReceiveTests.kt @@ -0,0 +1,27 @@ +package io.muun.apollo.presentation + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.muun.apollo.domain.model.AddressType +import org.javamoney.moneta.Money +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +open class ReceiveTests : BaseInstrumentationTest() { + + @Test + fun test_01_a_user_can_receive_money_onchain_with_fixed_amount_aka_bitcoin_uri() { + autoFlows.signUp() + + // Try with SEGWIT, default + val amount = Money.of(0.000102, "BTC") + autoFlows.receiveMoneyFromNetworkViaBitcoinUri(amount) + + // Try with LEGACY + autoFlows.receiveMoneyFromNetworkViaBitcoinUri(amount, AddressType.LEGACY) + + // Try with Taproot + autoFlows.receiveMoneyFromNetworkViaBitcoinUri(amount, AddressType.TAPROOT) + + } +} \ No newline at end of file diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/SettingsTests.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/SettingsTests.kt index 9eda3806..19105df0 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/SettingsTests.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/SettingsTests.kt @@ -1,7 +1,8 @@ package io.muun.apollo.presentation import io.muun.apollo.utils.RandomUser -import org.junit.Ignore +import io.muun.apollo.utils.screens.ReceiveScreen +import io.muun.common.model.ReceiveFormatPreference import org.junit.Test open class SettingsTests : BaseInstrumentationTest() { @@ -41,18 +42,40 @@ open class SettingsTests : BaseInstrumentationTest() { } @Test - @Ignore("feature is not yet turned on") - fun test_03_a_user_can_change_lightning_default() { + fun test_03_a_user_can_change_receive_preference_and_receive_funds() { val user = RandomUser() autoFlows.signUp(user.pin) - autoFlows.checkOnReceiveIfQRIs(false) + // Check Default receive preference is bitcoin (no actual receive, other tests exercise that) + autoFlows.checkReceivePreferenceIs(ReceiveFormatPreference.ONCHAIN) + // Change to Lightning + receive autoFlows.turnOnReceiveLightningByDefault() - autoFlows.checkOnReceiveIfQRIs(true) + autoFlows.checkReceivePreferenceIs(ReceiveFormatPreference.LIGHTNING) - autoFlows.turnOnReceiveBitcoinByDefault() - autoFlows.checkOnReceiveIfQRIs(false) + autoFlows.receiveMoneyFromLNWithAmountLessInvoice(100_000) + + // Change to Unified QR + receive + autoFlows.turnOnUnifiedQr() + autoFlows.checkReceivePreferenceIs(ReceiveFormatPreference.UNIFIED) + + autoFlows.receiveMoneyFromUnifiedQrViaLightning( + 100_000, + ReceiveScreen.UnifiedQrDraft.OffChain(100_000) + ) + + // Test Unified QR changes in config + + homeScreen.goToReceive() + receiveScreen.checkUnifiedQrConfig() + + // Test receive unified QR with amountless invoice + + autoFlows.receiveMoneyFromUnifiedQrViaLightning( + 200_000, + ReceiveScreen.UnifiedQrDraft.OffChain() + ) } + } diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/AutoFlows.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/AutoFlows.kt index ef2a472b..37b1dd0c 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/AutoFlows.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/AutoFlows.kt @@ -6,6 +6,9 @@ import androidx.test.uiautomator.UiDevice import io.muun.apollo.R import io.muun.apollo.data.debug.LappClient import io.muun.apollo.data.external.Gen +import io.muun.apollo.data.external.Globals +import io.muun.apollo.domain.libwallet.BitcoinUri +import io.muun.apollo.domain.model.AddressType import io.muun.apollo.domain.model.user.UserPhoneNumber import io.muun.apollo.presentation.ui.helper.isBtc import io.muun.apollo.presentation.ui.utils.OS @@ -13,8 +16,10 @@ import io.muun.apollo.presentation.ui.utils.UiUtils import io.muun.apollo.utils.WithMuunInstrumentationHelpers.Companion.balanceNotEqualsErrorMessage import io.muun.apollo.utils.screens.ReceiveScreen import io.muun.common.model.DebtType +import io.muun.common.model.ReceiveFormatPreference import io.muun.common.utils.BitcoinUtils import io.muun.common.utils.LnInvoice +import io.muun.common.utils.Preconditions import org.javamoney.moneta.Money import javax.money.MonetaryAmount @@ -149,12 +154,19 @@ class AutoFlows( } /** - * For Unrecoverable Users (some extra logic may apply, e.g u.u with positive balance may not + * Delete Wallet (some extra logic may apply, e.g u.u with positive balance may not * delete their wallets). */ - fun deleteWallet() { + fun deleteWallet(isRecoverableUser: Boolean = false) { goToSettingsAndClickDeleteWallet() + if (isRecoverableUser) { + label(R.string.settings_delete_wallet_alert_body_recoverable_user).await() + + } else { + label(R.string.settings_delete_wallet_alert_body_unrecoverable_user).await() + } + // Confirm on pop-up message. normalizedLabel(R.string.settings_delete_wallet_alert_yes).click() @@ -163,7 +175,7 @@ class AutoFlows( } /** - * For Unrecoverable Users, in some cases, we won't let them delete wallet if they can lose + * In some cases, we won't let them delete wallet if they can lose * money. This AutoFlow is what happens in those scenarios. */ fun checkCannotDeleteWallet() { @@ -175,6 +187,54 @@ class AutoFlows( backToHome() } + /** + * Receive funds using Unified QR feature, using the LN invoice embedded. + * Receive funds via Lightning Network using an AmountLess Invoice. TurboChannels flag hints + * whether we should check for operation status pending or not. + * + * NOTE: BEWARE if amount is low (< debt limit) and turboChannels is disabled, this method will + * fail as our Receive LN feature confirms the payment instantly (full debt mechanism). + */ + fun receiveMoneyFromUnifiedQrViaLightning( + amountInSat: Long, + unifiedQrDraft: ReceiveScreen.UnifiedQrDraft, + ) { + val prevBalance = homeScreen.balanceInBtc + + val unifiedQr = unifiedQrDraft as ReceiveScreen.UnifiedQrDraft.OffChain + Preconditions.checkArgument( + unifiedQr.amountInSat == null || unifiedQr.amountInSat == amountInSat + ) + + // For fixed amount invoices, amount MUST NOT be specified, otherwise an error occurs. + val amountToReceive = unifiedQr.amountInSat + + val invoice = getOwnInvoiceFromUnifiedQr(amountToReceive) + + LappClient().receiveBtcViaLN(invoice, amountInSat, unifiedQr.turboChannel) + + // Wait for balance to be updated: + val amount = BitcoinUtils.satoshisToBitcoins(amountInSat) + homeScreen.waitUntilBalanceEquals(prevBalance.add(amount)) + + checkOperationDetails(amount, statusPending = !unifiedQr.turboChannel) { + homeScreen.goToOperationDetail(0) + } + } + + private fun getOwnInvoiceFromUnifiedQr(amountInSat: Long? = null): String { + homeScreen.goToReceive() + + if (amountInSat != null) { + receiveScreen.addUnifiedQrAmount(amountInSat) + } + + val unifiedQr = receiveScreen.unifiedQr + + device.pressBack() // Back to Home + return BitcoinUri.parse(unifiedQr).third + } + /** * Receive funds via Lightning Network using an AmountLess Invoice. TurboChannels flag hints * whether we should check for operation status pending or not. @@ -232,7 +292,55 @@ class AutoFlows( return invoice } - fun receiveMoneyFromNetwork(amount: Money) = try { + fun receiveMoneyFromNetworkViaBitcoinUri( + amount: MonetaryAmount, + addressType: AddressType = AddressType.SEGWIT, + ) = try { + tryReceiveMoneyFromNetworkViaBitcoinUri(amount, addressType) + } catch (e: AssertionError) { + if (e.message != null && e.message!!.contains(balanceNotEqualsErrorMessage)) { + + LappClient().generateBlocks(30) // we don't want to need this again soon + Thread.sleep(2000) + tryReceiveMoneyFromNetwork(amount) + } else { + throw e + } + } + + private fun tryReceiveMoneyFromNetworkViaBitcoinUri( + amount: MonetaryAmount, + addressType: AddressType = AddressType.SEGWIT, + ) { + val expectedBalance = homeScreen.balanceInBtc + val balanceAfter = expectedBalance.add(amount) + + // Generate a Bitcoin Uri with amount: + val rawClipboard = getOwnBitcoinUri(amount, addressType) + val bitcoinUri = io.muun.common.bitcoinj.BitcoinUri(Globals.INSTANCE.network, rawClipboard) + + val amountInBtc = BitcoinUtils.satoshisToBitcoins(bitcoinUri.amount.value) + assertMoneyEqualsWithRoundingHack(amountInBtc, amount) + + // Hit RegTest to receive money from the network: + LappClient().receiveBtc(amountInBtc.number.toDouble(), bitcoinUri.address!!) + + // Wait for balance to be updated: + homeScreen.waitUntilBalanceEquals(balanceAfter) + } + + private fun getOwnBitcoinUri(amount: MonetaryAmount, addressType: AddressType): String { + homeScreen.goToReceive() + + receiveScreen.addBitcoinUriAmount(amount) + receiveScreen.selectAddressType(addressType) + + val bitcoinUri = receiveScreen.bitcoinUri + device.pressBack() // Back to Home + return bitcoinUri + } + + fun receiveMoneyFromNetwork(amount: MonetaryAmount) = try { tryReceiveMoneyFromNetwork(amount) } catch (e: AssertionError) { if (e.message != null && e.message!!.contains(balanceNotEqualsErrorMessage)) { @@ -245,7 +353,7 @@ class AutoFlows( } } - private fun tryReceiveMoneyFromNetwork(amount: Money) { + private fun tryReceiveMoneyFromNetwork(amount: MonetaryAmount) { val expectedBalance = homeScreen.balanceInBtc val balanceAfter = expectedBalance.add(amount) @@ -582,16 +690,16 @@ class AutoFlows( backToHome() } - fun checkOnReceiveIfQRIs(lightning: Boolean) { - homeScreen.goToReceive() - if (lightning) { - receiveScreen.id(R.id.address_settings).assertDoesntExist() - receiveScreen.id(R.id.invoice_settings).exists() - } else { - receiveScreen.id(R.id.address_settings).exists() - receiveScreen.id(R.id.invoice_settings).assertDoesntExist() - } + fun turnOnUnifiedQr() { + homeScreen.goToSettings() + settingsScreen.turnOnUnifiedQr() + backToHome() + } + + fun checkReceivePreferenceIs(receiveFormatPreference: ReceiveFormatPreference) { + homeScreen.goToReceive() + receiveScreen.checkReceivePreferenceIs(receiveFormatPreference) device.pressBack() } diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/ChangePasswordScreen.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/ChangePasswordScreen.kt index dd450439..bca0ecd9 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/ChangePasswordScreen.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/ChangePasswordScreen.kt @@ -31,6 +31,7 @@ class ChangePasswordScreen( checkEmailVerificationScreenDisplayed(user.email) editNewPassword(newPassword) + reEnterNewPassword(newPassword) acceptConditions() confirmNewPassword() @@ -67,7 +68,11 @@ class ChangePasswordScreen( } private fun editNewPassword(newPassword: String) { - input(R.id.change_password).text = newPassword + input(R.id.change_password_input).text = newPassword + } + + private fun reEnterNewPassword(newPassword: String) { + input(R.id.change_password_confirm_input).text = newPassword } private fun confirmNewPassword() { diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/ReceiveScreen.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/ReceiveScreen.kt index 5e88562c..be92f3e4 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/ReceiveScreen.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/ReceiveScreen.kt @@ -3,10 +3,17 @@ package io.muun.apollo.utils.screens import android.content.Context import androidx.test.uiautomator.UiDevice import io.muun.apollo.R +import io.muun.apollo.data.external.Globals +import io.muun.apollo.domain.libwallet.BitcoinUri +import io.muun.apollo.domain.model.AddressType import io.muun.apollo.presentation.ui.show_qr.ShowQrPage import io.muun.apollo.utils.Clipboard import io.muun.apollo.utils.WithMuunInstrumentationHelpers +import io.muun.common.bitcoinj.ValidationHelpers +import io.muun.common.model.ReceiveFormatPreference +import io.muun.common.utils.Bech32SegwitAddress import io.muun.common.utils.BitcoinUtils +import org.assertj.core.api.Assertions.assertThat import javax.money.MonetaryAmount class ReceiveScreen( @@ -19,6 +26,18 @@ class ReceiveScreen( private set } + sealed class UnifiedQrDraft { + data class OnChain( + val amount: MonetaryAmount? = null, + val addressType: AddressType = AddressType.SEGWIT, + ) : UnifiedQrDraft() + + data class OffChain( + val amountInSat: Long? = null, + val turboChannel: Boolean = true, + ) : UnifiedQrDraft() + } + val address: String get() { id(R.id.show_qr_copy).click() @@ -26,6 +45,9 @@ class ReceiveScreen( return lastCopiedFromClipboard } + val bitcoinUri: String + get() = address // Yes, its obtained the same way, but callers should set up an amount first + val invoice: String get() { normalizedLabel(ShowQrPage.LN.titleRes).click() @@ -34,25 +56,173 @@ class ReceiveScreen( return lastCopiedFromClipboard } + val unifiedQr: String + get() { + id(R.id.show_qr_copy).click() + lastCopiedFromClipboard = Clipboard.read() + return lastCopiedFromClipboard + } + fun goToScanLnUrl() { desc(R.string.scan_lnurl).click() } + fun selectAddressType(addressType: AddressType) { + + if (!id(R.id.edit_address_type).exists()) { + id(R.id.address_settings).click() + } + + id(R.id.edit_address_type).click() + + when (addressType) { + AddressType.SEGWIT -> label(R.string.address_picker_segwit_title).click() + AddressType.LEGACY -> label(R.string.address_picker_legacy_title).click() + AddressType.TAPROOT -> label(R.string.address_picker_taproot_title).click() + } + } + + private fun selectAddressTypeForUnifiedQr(addressType: AddressType) { + + if (!id(R.id.edit_address_type).exists()) { + id(R.id.unified_qr_settings).click() + } + + id(R.id.edit_address_type).click() + + when (addressType) { + AddressType.SEGWIT -> label(R.string.address_picker_segwit_title).click() + AddressType.LEGACY -> label(R.string.address_picker_legacy_title).click() + AddressType.TAPROOT -> label(R.string.address_picker_taproot_title).click() + } + } + + fun addUnifiedQrAmount(amountInSat: Long) { + id(R.id.unified_qr_settings).click() + + editAmount(amountInSat) + } + fun addInvoiceAmount(amountInSat: Long) { normalizedLabel(ShowQrPage.LN.titleRes).click() id(R.id.invoice_settings).click() - id(R.id.add_amount).click() + editAmount(amountInSat) + } - editAmount(BitcoinUtils.satoshisToBitcoins(amountInSat)) + fun addBitcoinUriAmount(amount: MonetaryAmount) { + normalizedLabel(ShowQrPage.BITCOIN.titleRes).click() + id(R.id.address_settings).click() - pressMuunButton(R.id.confirm_amount_button) + editAmount(amount) + } + + private fun editAmount(amountInSat: Long) { + editAmount(BitcoinUtils.satoshisToBitcoins(amountInSat)) } private fun editAmount(amount: MonetaryAmount) { + if (id(R.id.add_amount).exists()) { + id(R.id.add_amount).click() + + } else { + id(R.id.amount_label).click() + } + id(R.id.currency_code).click() labelWith(amount.currency.currencyCode).click() id(R.id.muun_amount).text = amount.number.toString() + + pressMuunButton(R.id.confirm_amount_button) + } + + fun checkReceivePreferenceIs(receiveFormatPreference: ReceiveFormatPreference) { + when (receiveFormatPreference) { + ReceiveFormatPreference.ONCHAIN -> { + receiveScreen.id(R.id.address_settings).exists() + receiveScreen.id(R.id.invoice_settings).assertDoesntExist() + receiveScreen.id(R.id.unified_qr_settings).assertDoesntExist() + } + ReceiveFormatPreference.LIGHTNING -> { + receiveScreen.id(R.id.address_settings).assertDoesntExist() + receiveScreen.id(R.id.invoice_settings).exists() + receiveScreen.id(R.id.unified_qr_settings).assertDoesntExist() + } + ReceiveFormatPreference.UNIFIED -> { + receiveScreen.id(R.id.address_settings).assertDoesntExist() + receiveScreen.id(R.id.invoice_settings).assertDoesntExist() + receiveScreen.id(R.id.unified_qr_settings).exists() + } + } + } + + fun checkUnifiedQrConfig() { + var amountInSat: Long = 100 + var addressType = AddressType.SEGWIT + + addUnifiedQrAmount(amountInSat) + selectAddressTypeForUnifiedQr(addressType) + checkUnifiedQrConfig(amountInSat, addressType) + + amountInSat = 200 + addressType = AddressType.LEGACY + + changeAndCheck(amountInSat, addressType) + + amountInSat = 300 + addressType = AddressType.TAPROOT + + changeAndCheck(amountInSat, addressType) + + device.pressBack() + } + + private fun changeAndCheck(amountInSat: Long, addressType: AddressType) { + editAmount(amountInSat) + selectAddressTypeForUnifiedQr(addressType) + + } + + private fun checkUnifiedQrConfig( + amountInSat: Long, + addressType: AddressType, + ) { + checkAmountIs(amountInSat) + checkAddressTypeIs(addressType) + id(R.id.expiration_time_item).exists() + } + + private fun checkAmountIs(amountInSat: Long) { + checkAmountIs(BitcoinUtils.satoshisToBitcoins(amountInSat)) + } + + private fun checkAmountIs(expectedAmount: MonetaryAmount) { + val selectedAmount = id(R.id.selected_amount).text.toMoney() + assertMoneyEqualsWithRoundingHack(selectedAmount, expectedAmount) + } + + private fun checkAddressTypeIs(expectedAddressType: AddressType) { + val addressType = id(R.id.edit_address_type).text + + val (address, _, _) = BitcoinUri.parse(unifiedQr) + val params = Globals.INSTANCE.network + + when (expectedAddressType) { + AddressType.SEGWIT -> { + assert(Bech32SegwitAddress.decode(params, address).fst.toInt() == 0) + assertThat(addressType).isEqualTo(context.getString(R.string.segwit)) + } + + AddressType.LEGACY -> { + assert(ValidationHelpers.isValidBase58Address(params, address)) + assertThat(addressType).isEqualTo(context.getString(R.string.legacy)) + } + + AddressType.TAPROOT -> { + assert(Bech32SegwitAddress.decode(params, address).fst.toInt() == 1) + assertThat(addressType).isEqualTo(context.getString(R.string.taproot)) + } + } } } \ No newline at end of file diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/SettingsScreen.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/SettingsScreen.kt index 4c53c814..a4d8c25b 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/SettingsScreen.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/SettingsScreen.kt @@ -19,7 +19,7 @@ class SettingsScreen( } fun deleteWallet() { - id(R.id.log_out_text_view).click() + id(R.id.delete_wallet_text_view).click() } fun setBitcoinUnitToSat() { @@ -43,7 +43,7 @@ class SettingsScreen( id(R.id.receive_preference_value).click() - label(R.string.tab_ln_invoice).click() + label(R.string.receive_preference_lightning_title).click() device.pressBack() } @@ -53,7 +53,17 @@ class SettingsScreen( id(R.id.receive_preference_value).click() - label(R.string.tab_bitcoin_address).click() + label(R.string.receive_preference_bitcoin_title).click() + + device.pressBack() + } + + fun turnOnUnifiedQr() { + id(R.id.settings_lightning).click() + + id(R.id.receive_preference_value).click() + + label(R.string.receive_preference_unified).click() device.pressBack() } diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/SignInScreen.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/SignInScreen.kt index 25095b01..18da7e76 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/SignInScreen.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/SignInScreen.kt @@ -47,16 +47,24 @@ class SignInScreen( startLogin() - pressMuunButton(R.id.enter_email_use_rc_only) + recoverWithRecoveryCode() enterRecoveryCode(recoveryCodeParts) - pressMuunButton(R.id.rc_only_login_continue) + confirmRecoveryCodeOnlyLogin() if (email != null) { checkRcLoginEmailAuthScreenDisplayed(email) } } + fun recoverWithRecoveryCode() { + pressMuunButton(R.id.enter_email_use_rc_only) + } + + fun confirmRecoveryCodeOnlyLogin() { + pressMuunButton(R.id.rc_only_login_continue) + } + fun startSignup() { normalizedLabel(R.string.signup_start).click() } @@ -141,6 +149,19 @@ class SignInScreen( checkInputError(R.id.signup_unlock_edit_password, R.string.error_incorrect_password) } + fun abortDialogCancelWithSafeguard() { + try { + signInScreen.abortDialogCancel() + } catch (_: Exception) { + // I'm DONE with this annoying issue regarding Google Password Manager. + // Not gonna keep suffering flakiness from it. ENOUGH! + // TODO: find reliable way to disable Google Password Manager + signInScreen.back() // Add extra back to dismiss Google Password Manager popup + signInScreen.abortDialogCancel() + } + } + + fun abortDialogCancel() { normalizedLabel(R.string.cancel).click() } @@ -149,11 +170,11 @@ class SignInScreen( normalizedLabel(R.string.abort).click() } - private fun enterRecoveryCode(recoveryCodeParts: List) { + fun enterRecoveryCode(recoveryCodeParts: List) { recoveryCodeScreen.enterRecoveryCode(recoveryCodeParts) } - private fun confirmRecoveryCode() { + fun confirmRecoveryCode() { pressMuunButton(R.id.signup_forgot_password_continue) } } \ No newline at end of file diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/Navigator.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/Navigator.java index 1e68b0bd..f2944d04 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/Navigator.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/Navigator.java @@ -495,7 +495,7 @@ public void navigateToLauncher(@NotNull Context context) { public void navigateToDeleteWallet(@NotNull Context context) { final Optional maybeSupportId = userSel.getOptional().flatMap(User::getSupportId); - logoutActions.dangerouslyDestroyUnrecoverableWallet(); + logoutActions.dangerouslyDestroyWallet(); final Intent intent = SuccessDeleteWalletActivity .getStartActivityIntent(context, maybeSupportId); diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BaseActivity.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BaseActivity.java index ad72d37c..d66f57e6 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BaseActivity.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BaseActivity.java @@ -160,7 +160,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { try { super.onCreate(savedInstanceState); - Timber.d("Lifecycle: " + getClass().getSimpleName() + "#onCreate"); + Timber.i("Lifecycle: " + getClass().getSimpleName() + "#onCreate"); if (savedInstanceState != null) { Timber.i("Lifecycle: " + getClass().getSimpleName() + " is being recreated"); diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BaseFragment.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BaseFragment.java index d42d0762..121320c5 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BaseFragment.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BaseFragment.java @@ -160,7 +160,7 @@ public View onCreateView( ViewGroup container, Bundle savedInstanceState ) { - Timber.d("Lifecycle: " + getClass().getSimpleName() + "#onCreateView"); + Timber.i("Lifecycle: " + getClass().getSimpleName() + "#onCreateView"); if (savedInstanceState != null) { Timber.i("Lifecycle: " + getClass().getSimpleName() + " is being recreated"); @@ -200,7 +200,7 @@ private View inflateLayout(@NonNull LayoutInflater inflater, ViewGroup container */ @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - Timber.d("Lifecycle: " + getClass().getSimpleName() + "#onViewCreated"); + Timber.i("Lifecycle: " + getClass().getSimpleName() + "#onViewCreated"); initializeUi(view); presenter.onViewCreated(savedInstanceState); } diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/ek_save/EmergencyKitSaveFragment.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/ek_save/EmergencyKitSaveFragment.kt index d9f599f3..8140fdae 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/ek_save/EmergencyKitSaveFragment.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/ek_save/EmergencyKitSaveFragment.kt @@ -107,7 +107,7 @@ class EmergencyKitSaveFragment : SingleFragment(), override fun setDriveUploading(isUploading: Boolean) { if (isUploading) { val message = if (argumentsBundle.getBoolean(ARG_UPDATE_KIT)) { - R.string.ek_uploading_body + R.string.ek_updating_body } else { R.string.ek_uploading_body } diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/home/HomeFragment.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/home/HomeFragment.kt index ed30d771..0f74d4ca 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/home/HomeFragment.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/home/HomeFragment.kt @@ -166,7 +166,7 @@ class HomeFragment : SingleFragment(), HomeFragmentView { private val minTravelDistance = ViewConfiguration.get(context).scaledTouchSlop override fun onFling( - e1: MotionEvent, + e1: MotionEvent?, e2: MotionEvent, velocityX: Float, velocityY: Float, @@ -174,7 +174,7 @@ class HomeFragment : SingleFragment(), HomeFragmentView { // Future reader: for MotionEvent we want rawX/Y, other coordinates suck (BIG TIME) // See: https://stackoverflow.com/q/1410885/901465 - if (abs(velocityY) > minVelocity && e1.rawY - e2.rawY > minTravelDistance) { + if (abs(velocityY) > minVelocity && e1 != null && e1.rawY - e2.rawY > minTravelDistance) { chevron.performClick() return true } diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/manual_fee/ManualFeeFragment.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/manual_fee/ManualFeeFragment.kt index d68aee59..d1b72ab5 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/manual_fee/ManualFeeFragment.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/manual_fee/ManualFeeFragment.kt @@ -2,13 +2,11 @@ package io.muun.apollo.presentation.ui.fragments.manual_fee import android.text.TextUtils import android.view.View -import android.widget.TextView import butterknife.BindString import butterknife.BindView import butterknife.OnClick import icepick.State import io.muun.apollo.R -import io.muun.apollo.domain.Flags import io.muun.apollo.domain.model.BitcoinAmount import io.muun.apollo.domain.model.BitcoinUnit import io.muun.apollo.presentation.ui.base.SingleFragment @@ -36,9 +34,6 @@ class ManualFeeFragment : SingleFragment(), ManualFeeView { @BindView(R.id.status_message) lateinit var statusMessage: StatusMessage - @BindView(R.id.use_maximum_fee) - lateinit var useMaximumFee: TextView - @BindView(R.id.confirm_fee) lateinit var confirmButton: MuunButton @@ -51,8 +46,6 @@ class ManualFeeFragment : SingleFragment(), ManualFeeView { @State lateinit var mBitcoinUnit: BitcoinUnit - private var shouldShowMaxFeeButton = false - override fun inject() { component.inject(this) } @@ -89,7 +82,6 @@ class ManualFeeFragment : SingleFragment(), ManualFeeView { confirmButton.isEnabled = false statusMessage.visibility = View.GONE feeInput.resetVisibility() - useMaximumFee.visibility = if (shouldShowMaxFeeButton) View.VISIBLE else View.GONE // 1.5 If feeRate is null, we're back at initial state (empty input), nothing else to do if (feeRateInSatsPerVByte == null) { @@ -130,16 +122,6 @@ class ManualFeeFragment : SingleFragment(), ManualFeeView { } } } - - shouldShowMaxFeeButton = showMaximumFeeButton(state) - - if (shouldShowMaxFeeButton) { - useMaximumFee.visibility = View.VISIBLE - useMaximumFee.setOnClickListener { - feeInput.setFeeRate(state.maxFeeRateInSatsPerVByte) - useMaximumFee.visibility = View.GONE - } - } } @OnClick(R.id.confirm_fee) @@ -189,19 +171,6 @@ class ManualFeeFragment : SingleFragment(), ManualFeeView { confirmButton.isEnabled = true // just a warning } - private fun showMaximumFeeButton(state: EditFeeState): Boolean { - - // TODO disabled until properly tested and QA vetted - if (!Flags.USE_MAXIMUM_FEE_ENABLED) { - return false - } - - // Replicating Falcon logic here. - // The use max fee button is only displayed in case the user is not taking fee from amount - // (aka using all funds) and if the user doesn't have a selected fee - return !state.amountInfo.takeFeeFromAmount && state.validated.feeNeedsChange - } - private fun onHowThisWorksClick() { val dialog = TitleAndDescriptionDrawer() dialog.setTitle(R.string.manual_fee_how_this_works_explanation_title) diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsFragment.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsFragment.kt index cc82daa4..7e8f47a6 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsFragment.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsFragment.kt @@ -368,8 +368,8 @@ open class SettingsFragment : SingleFragment(), SettingsView showDialog(muunDialog) } - override fun handleDeleteWallet(isActionBlocked: Boolean, isRecoverableUser: Boolean) { - if (isActionBlocked) { + override fun handleDeleteWallet(canDeleteWallet: Boolean, isRecoverableUser: Boolean) { + if (!canDeleteWallet) { showCantDeleteNonEmptyWalletDialog() } else { diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsPresenter.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsPresenter.kt index c8c0406f..a4d6cdec 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsPresenter.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsPresenter.kt @@ -203,7 +203,7 @@ class SettingsPresenter @Inject constructor( val options = logoutOptionsSel.watch() .toBlocking() .first() - val shouldBlockAndExplain = options.isBlocked() + val shouldBlockAndExplain = options.isLogoutBlocked() Preconditions.checkArgument(options.isRecoverable()) view.handleLogout(shouldBlockAndExplain) @@ -218,7 +218,7 @@ class SettingsPresenter @Inject constructor( .toBlocking() .first() - view.handleDeleteWallet(options.isBlocked(), options.isRecoverable()) + view.handleDeleteWallet(options.canDeleteWallet(), options.isRecoverable()) } override fun getEntryEvent(): AnalyticsEvent { diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsView.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsView.java index b079c19c..dc6b6060 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsView.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/settings/SettingsView.java @@ -42,7 +42,7 @@ public interface SettingsView extends BaseView { /** * Handle the delete wallet action. */ - void handleDeleteWallet(boolean isActionBlocked, boolean isRecoverableUser); + void handleDeleteWallet(boolean canDeleteWallet, boolean isRecoverableUser); /** * Show a simple, standard muun error dialog to communicate that non empty wallet can't be diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/launcher/LauncherPresenter.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/launcher/LauncherPresenter.java index 4679eb37..28de2805 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/launcher/LauncherPresenter.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/launcher/LauncherPresenter.java @@ -12,6 +12,7 @@ import android.net.Uri; import androidx.annotation.Nullable; +import timber.log.Timber; import javax.inject.Inject; @@ -50,6 +51,9 @@ public void handleLaunch(@Nullable Uri uri, boolean isTaskRoot) { if (Globals.INSTANCE.getVersionCode() >= minClientVersion) { + final String maybeUri = uri != null ? uri.toString() : "null"; + Timber.i("HandleLaunch(isTaskRoot: %s, uri: %s)", isTaskRoot, maybeUri); + // If we caught an Intent with an URI (from an external Muun link), handle it: if (uri != null) { useMuunLinkAction.run(uri.toString()); diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordFragment.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordFragment.kt index 255a7812..26268521 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordFragment.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordFragment.kt @@ -1,5 +1,6 @@ package io.muun.apollo.presentation.ui.settings.edit_password +import android.text.TextUtils import android.view.View import android.widget.CheckBox import butterknife.BindView @@ -10,11 +11,15 @@ import io.muun.apollo.presentation.ui.base.BaseActivity import io.muun.apollo.presentation.ui.base.SingleFragment import io.muun.apollo.presentation.ui.view.MuunButton import io.muun.apollo.presentation.ui.view.MuunTextInput +import io.muun.common.Rules class ChangePasswordFragment : SingleFragment(), ChangePasswordView { - @BindView(R.id.change_password) - lateinit var password: MuunTextInput + @BindView(R.id.change_password_input) + lateinit var passwordInput: MuunTextInput + + @BindView(R.id.change_password_confirm_input) + lateinit var passwordConfirmInput: MuunTextInput @BindView(R.id.change_password_condition) lateinit var condition: CheckBox @@ -31,10 +36,28 @@ class ChangePasswordFragment : SingleFragment(), Change } override fun initializeUi(view: View) { - password.setPasswordRevealEnabled(true) continueButton.isEnabled = false - condition.setOnCheckedChangeListener { _, _ -> onConditionCheckedChanged() } + passwordInput.setPasswordRevealEnabled(true) + passwordInput.setOnChangeListener(this) { + // Ugly check needed for some convoluted scenario where we receive input and fragment + // is being re-created or something + if (::passwordInput.isInitialized) { + validateInputs() + } + } + passwordConfirmInput.setPasswordRevealEnabled(true) + passwordConfirmInput.setOnChangeListener(this) { + // Ugly check needed for some convoluted scenario where we receive input and fragment + // is being re-created or something + if (::passwordConfirmInput.isInitialized) { + validateInputs() + } + } + + condition.setOnCheckedChangeListener { _, _ -> + validateInputs() + } continueButton.setOnClickListener { onContinueButtonClick() } } @@ -51,7 +74,7 @@ class ChangePasswordFragment : SingleFragment(), Change } private fun abort() { - safeGetParentActivity().ifPresent(BaseActivity<*>::finish) + safeGetParentActivity().ifPresent(BaseActivity<*>::finishActivity) } override fun setLoading(loading: Boolean) { @@ -60,27 +83,44 @@ class ChangePasswordFragment : SingleFragment(), Change } override fun setPasswordError(error: UserFacingError?) { - password.clearError() + passwordInput.clearError() + + if (error != null) { + passwordInput.setError(error) + passwordInput.requestFocusInput() + } + } + + override fun setConfirmPasswordError(error: UserFacingError?) { + passwordConfirmInput.clearError() + if (error != null) { - password.setError(error) - password.requestFocusInput() + passwordConfirmInput.setError(error) + passwordConfirmInput.requestFocusInput() } } override fun onResume() { super.onResume() - password.requestFocusInput() + passwordInput.requestFocusInput() } override fun blockScreenshots(): Boolean { return true } - private fun onConditionCheckedChanged() { - continueButton.isEnabled = condition.isChecked + private fun validateInputs() { + val validPassword = isValidPassword(passwordInput.text.toString()) + val validPasswordConfirm = isValidPassword(passwordConfirmInput.text.toString()) + + continueButton.isEnabled = validPassword && validPasswordConfirm && condition.isChecked } private fun onContinueButtonClick() { - presenter.submitPassword(password.text.toString()) + presenter.submitPassword(passwordInput.text.toString(), passwordConfirmInput.text.toString()) } + + private fun isValidPassword(password: String) = + !TextUtils.isEmpty(password) && password.length >= Rules.PASSWORD_MIN_LENGTH + } \ No newline at end of file diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordPresenter.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordPresenter.kt index 2ea18c46..89adf99e 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordPresenter.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordPresenter.kt @@ -8,6 +8,7 @@ import io.muun.apollo.domain.analytics.AnalyticsEvent.E_PASSWORD_CHANGED import io.muun.apollo.domain.analytics.AnalyticsEvent.S_PASSWORD_CHANGE_END import io.muun.apollo.domain.errors.EmptyFieldError import io.muun.apollo.domain.errors.passwd.PasswordTooShortError +import io.muun.apollo.domain.errors.passwd.PasswordsDontMatchError import io.muun.apollo.presentation.ui.base.di.PerFragment import io.muun.common.Rules import javax.inject.Inject @@ -46,7 +47,7 @@ class ChangePasswordPresenter @Inject constructor( /** * Submit the new password, checking for errors. */ - fun submitPassword(password: String) { + fun submitPassword(password: String, confirmPassword: String) { view.setPasswordError(null) if (password == "") { @@ -55,8 +56,12 @@ class ChangePasswordPresenter @Inject constructor( } else if (password.length < Rules.PASSWORD_MIN_LENGTH) { view.setPasswordError(PasswordTooShortError()) + } else if (password != confirmPassword) { + view.setConfirmPasswordError(PasswordsDontMatchError()) + } else { view.setPasswordError(null) + view.setConfirmPasswordError(null) view.setLoading(true) finishPasswordChange.run(parentPresenter.currentUuid, password) } diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordView.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordView.java index 2a24c90b..38111c56 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordView.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/settings/edit_password/ChangePasswordView.java @@ -7,4 +7,6 @@ public interface ChangePasswordView extends SingleFragmentView { void setPasswordError(UserFacingError error); + void setConfirmPasswordError(UserFacingError error); + } diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/FeeManualInput.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/FeeManualInput.java index de893d76..bf55aaa3 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/FeeManualInput.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/FeeManualInput.java @@ -24,7 +24,6 @@ import icepick.State; import timber.log.Timber; -import java.math.RoundingMode; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParseException; @@ -78,8 +77,6 @@ public interface OnChangeListener { private OnChangeListener onChangeListener; - private boolean isSkippingListeners; - public FeeManualInput(Context context) { super(context); } @@ -115,9 +112,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { @Override public void afterTextChanged(Editable s) { - if (!isSkippingListeners) { - updateFeeRate(parseNumber(s.toString())); - } + updateFeeRate(parseNumber(s.toString())); } }); } @@ -133,17 +128,6 @@ public void requestFocusInput() { } } - /** - * Set input's fee rate, in satoshis per virtual byte. - */ - public void setFeeRate(double feeRateInSatsPerVbyte) { - isSkippingListeners = true; - feeRateInput.setText(UiUtils.formatFeeRate(feeRateInSatsPerVbyte, RoundingMode.FLOOR)); - isSkippingListeners = false; - - updateFeeRate(feeRateInSatsPerVbyte); - } - private void updateFeeRate(Double feeRateInSatsPerVbyte) { this.feeRateInSatsPerVbyte = feeRateInSatsPerVbyte; notifyChange(); diff --git a/android/apolloui/src/main/res/layout/change_password_fragment.xml b/android/apolloui/src/main/res/layout/change_password_fragment.xml index e8f60153..8a888a6c 100644 --- a/android/apolloui/src/main/res/layout/change_password_fragment.xml +++ b/android/apolloui/src/main/res/layout/change_password_fragment.xml @@ -26,7 +26,7 @@ android:text="@string/change_password_title" /> + + - - Ingresa tu nueva contraseña Nueva contraseña @string/create_password_input_helper + @string/create_password_confirm_input_hint Entiendo que mi contraseña anterior ya no será válida. Confirma tu nueva contraseña @@ -1114,7 +1115,6 @@ No tienes fondos suficientes para pagar la comisión óptima. Elige una comisión para continuar. - Usar máxima comisión disponible Total (Monto + Comisión Mínima) Tu balance diff --git a/android/apolloui/src/main/res/values/strings.xml b/android/apolloui/src/main/res/values/strings.xml index b37e33e4..275a2e59 100644 --- a/android/apolloui/src/main/res/values/strings.xml +++ b/android/apolloui/src/main/res/values/strings.xml @@ -875,6 +875,7 @@ Enter your new password New password @string/create_password_input_helper + @string/create_password_confirm_input_hint I understand that my previous password is no longer valid. @@ -1074,7 +1075,6 @@ Not enough funds to pay for the optimal network fee. Select a network fee to continue. - Use maximum fee Total (Amount + Minimum Fee) Your balance diff --git a/build.gradle b/build.gradle index 6c5f2510..db6dc2a3 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { } dependencies { // Add third party gradle plugins - classpath 'com.android.tools.build:gradle:7.0.4' + classpath 'com.android.tools.build:gradle:7.4.2' // We're using a patched version of SQLDelight, for reproducible builds. Thanks JitPack! classpath 'com.github.muun.sqldelight:gradle-plugin:1.5.3-reproducible-build' } diff --git a/common/src/main/java/io/muun/common/api/SessionJson.java b/common/src/main/java/io/muun/common/api/SessionJson.java index 5ea7803e..b9c39223 100644 --- a/common/src/main/java/io/muun/common/api/SessionJson.java +++ b/common/src/main/java/io/muun/common/api/SessionJson.java @@ -18,7 +18,7 @@ public class SessionJson { public String buildType; @Nonnegative - public int version; + public int version; // this is the clientVersion, not a session version. @NotEmpty public String gcmRegistrationToken; diff --git a/common/src/main/java/io/muun/common/api/error/ErrorCode.java b/common/src/main/java/io/muun/common/api/error/ErrorCode.java index c8ff5944..26435839 100644 --- a/common/src/main/java/io/muun/common/api/error/ErrorCode.java +++ b/common/src/main/java/io/muun/common/api/error/ErrorCode.java @@ -229,7 +229,9 @@ public enum ErrorCode { 2086, StatusCode.CLIENT_FAILURE, "Cannot delete wallet with funds" ), UNSETTLED_OPERATIONS( - 2087, StatusCode.CLIENT_FAILURE, "Cannot delete wallet with unsettled operations" + 2087, + StatusCode.CLIENT_FAILURE, + "Cannot delete wallet with unsettled operations" ), // error responses diff --git a/common/src/main/java/io/muun/common/utils/Preconditions.java b/common/src/main/java/io/muun/common/utils/Preconditions.java index 240248e2..79655c11 100644 --- a/common/src/main/java/io/muun/common/utils/Preconditions.java +++ b/common/src/main/java/io/muun/common/utils/Preconditions.java @@ -187,7 +187,7 @@ public static T checkNotNull(T reference, @Nullable Object errorMessage) { /** * If a condition is true, ensures that an object reference is not null. If it's false, ensure - * that the the reference is null. + * that the reference is null. * * @return the null reference that was validated * @throws IllegalArgumentException if {@code reference} is not null @@ -201,7 +201,6 @@ public static T checkNotNullOnlyIf(@Nullable T reference, boolean condition) } } - /** * Ensures that an object reference passed as a parameter to the calling method is null. * @@ -349,20 +348,6 @@ public static long checkPositive(long number, @Nullable Object errorMessage) { return number; } - /** - * Ensures that {@code number} is positive. - * - * @param number a number - * @return the value of {@code number} - * @throws IllegalArgumentException if {@code number} is negative - */ - public static long checkNonNegative(long number) { - if (number < 0) { - throw new IllegalArgumentException("Number " + number + " expected to be non-negative"); - } - return number; - } - /** * Ensures that {@code index} specifies a valid element in an array, list or string of * size {@code size}. An element index may range from zero, inclusive, to {@code size}, diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf0..249e5832 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 669386b8..8fad3f5a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 83f2acfd..a69d9cb6 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,78 +17,113 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -105,84 +140,101 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 24467a14..53a6b238 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,10 +25,13 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,38 +64,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/libwallet/go.mod b/libwallet/go.mod index 5b88265f..0bd19427 100644 --- a/libwallet/go.mod +++ b/libwallet/go.mod @@ -3,11 +3,9 @@ module github.com/muun/libwallet go 1.14 require ( - github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/btcsuite/btcd v0.20.1-beta.0.20200515232429-9f0179fd2c46 github.com/btcsuite/btcutil v1.0.2 github.com/fiatjaf/go-lnurl v1.3.1 - github.com/google/uuid v1.1.1 github.com/jinzhu/gorm v1.9.16 github.com/lightningnetwork/lightning-onion v1.0.1 github.com/lightningnetwork/lnd v0.10.4-beta @@ -15,14 +13,13 @@ 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-20210921155107-089bfa567519 + golang.org/x/crypto v0.25.0 + golang.org/x/image v0.18.0 // indirect 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 + golang.org/x/net v0.27.0 // 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/muun/mobile v0.0.0-20220913162405-8cc629edd37b +replace golang.org/x/mobile => github.com/muun/mobile v0.0.0-20240709203120-049ae58602a0 diff --git a/libwallet/go.sum b/libwallet/go.sum index 92e1994a..f7420546 100644 --- a/libwallet/go.sum +++ b/libwallet/go.sum @@ -1,36 +1,22 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.33.1 h1:fmJQWZ1w9PGkHR1YL/P7HloDvqlmKQ4Vpb7PC2e+aCk= cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -git.schwanenlied.me/yawning/bsaes.git v0.0.0-20180720073208-c0276d75487e h1:F2x1bq7RaNCIuqYpswggh1+c1JmwdnkHNC9wy1KDip0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20221208032759-85de2813cf6b/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= git.schwanenlied.me/yawning/bsaes.git v0.0.0-20180720073208-c0276d75487e/go.mod h1:BWqTsj8PgcPriQJGl7el20J/7TuT1d/hSyFDXMEpoEo= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e h1:n+DcnTNkQnHlwpsrHoQtkrJIO7CBx029fw6oR4vIob4= github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ= -github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 h1:MG93+PZYs9PyEsj/n5/haQu2gK0h4tUtSy9ejtMwWa0= github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82/go.mod h1:GbuBk21JqF+driLX3XtJYNZjGa45YDoa9IqCTzNSfEc= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2 h1:2be4ykKKov3M1yISM2E8gnGXZ/N2SsPawfnGiXxaYEU= github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= @@ -64,7 +50,6 @@ github.com/btcsuite/btcwallet/wtxmgr v1.2.0 h1:ZUYPsSv8GjF9KK7lboB2OVHF0uYEcHxgr github.com/btcsuite/btcwallet/wtxmgr v1.2.0/go.mod h1:h8hkcKUE3X7lMPzTUoGnNiw5g7VhGrKEW3KpR2r0VnY= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8 h1:nOsAWScwueMVk/VLm/dvQQD7DuanyvAUb6B3P3eT274= github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4= @@ -74,23 +59,10 @@ github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -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/champo/mobile v0.0.0-20220913162405-8cc629edd37b h1:lBk5LxUbwRPdx8jikAiwae6Z7Z9qupNlgJC2QZXJpVg= -github.com/champo/mobile v0.0.0-20220913162405-8cc629edd37b/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= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -99,49 +71,35 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fiatjaf/go-lnurl v1.3.1 h1:9Qn4n1ZyzTMW/YuVX2Wr9cE+LEAzpE1hrCbxVK/yBKE= github.com/fiatjaf/go-lnurl v1.3.1/go.mod h1:BqA8WXAOzntF7Z3EkVO7DfP4y5rhWUmJ/Bu9KBke+rs= -github.com/frankban/quicktest v1.2.2 h1:xfmOhhoH5fGPgbEAlhLpJH9p0z/0Qizio9osmvn9IUY= github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/strfmt v0.19.5 h1:0utjKrw+BAh8s57XE9Xz8DUBsVvPmRUB6styvl9wWIM= github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= -github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -151,23 +109,19 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.8.6 h1:XvND7+MPP7Jp+JpqSZ7naSl5nVZf6k0LbL1V3EKh0zc= github.com/grpc-ecosystem/grpc-gateway v1.8.6/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hhrutter/lzw v0.0.0-20190827003112-58b82c5a41cc/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk= github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650 h1:1yY/RQWNSBjJe2GDCIYoLmpWVidrooriUr4QS/zaATQ= @@ -176,15 +130,12 @@ github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 h1:o1wMw7uTNyA58IlEd github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7/go.mod h1:WkUxfS2JUu3qPo6tRld7ISb8HiC0gVSU91kooBMDVok= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc= github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= -github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad h1:heFfj7z0pGsNCekUlsFhO2jstxO4b5iQ665LjwM5mDc= github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jezek/xgb v1.0.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= @@ -198,34 +149,22 @@ github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c h1:3UvYABOQRhJAApj9MdCN+Ydv841ETSoy6xLzdmmr/9A= github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= -github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d h1:hJXjZMxj0SWlMoQkzeZDLi2cmeiWKa7y1B8Rg+qaoEc= github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= -github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI= github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/juju/retry v0.0.0-20180821225755-9058e192b216 h1:/eQL7EJQKFHByJe3DeE8Z36yqManj9UY5zppDoQi4FU= github.com/juju/retry v0.0.0-20180821225755-9058e192b216/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4= -github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 h1:Pp8RxiF4rSoXP9SED26WCfNB28/dwTDpPXS8XMJR8rc= github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= -github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d h1:irPlN9z5VCe6BTsqVsxheCZH99OFSmqSVyTigW4mEoY= github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk= -github.com/juju/version v0.0.0-20180108022336-b64dbd566305 h1:lQxPJ1URr2fjsKnJRt/BxiIxjLt9IKGvS+0injMHbag= github.com/juju/version v0.0.0-20180108022336-b64dbd566305/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U= -github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec h1:n1NeQ3SgUHyISrjFFoO5dR748Is8dBL9qpaTNfphQrs= github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -237,13 +176,11 @@ github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQ github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg= github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200 h1:j4iZ1XlUAPQmW6oSzMcJGILYsRHNs+4O3Gk+2Ms5Dww= github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h1:MlZmoKa7CJP3eR1s5yB7Rm5aSyadpKkxqAwLQmog7N0= -github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d h1:QWD/5MPnaZfUVP7P8wLa4M8Td2DI7XXHXt2vhVtUgGI= github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI= github.com/lightningnetwork/lightning-onion v1.0.1 h1:qChGgS5+aPxFeR6JiUsGvanei1bn6WJpYbvosw/1604= github.com/lightningnetwork/lightning-onion v1.0.1/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4= github.com/lightningnetwork/lnd v0.10.4-beta h1:Af2zOCPePeaU8Tkl8IqtTjr4BP3zYfi+hAtQYcCMM58= github.com/lightningnetwork/lnd v0.10.4-beta/go.mod h1:4d02pduRVtZwgTJ+EimKJTsEAY0jDwi0SPE9h5aRneM= -github.com/lightningnetwork/lnd/cert v1.0.2 h1:g2rEu+sM2Uyz0bpfuvwri/ks6R/26H5iY1NcGbpDJ+c= github.com/lightningnetwork/lnd/cert v1.0.2/go.mod h1:fmtemlSMf5t4hsQmcprSoOykypAPp+9c+0d0iqTScMo= github.com/lightningnetwork/lnd/clock v1.0.1 h1:QQod8+m3KgqHdvVMV+2DRNNZS1GRFir8mHZYA+Z2hFo= github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg= @@ -254,25 +191,19 @@ github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qp github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= -github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6 h1:b/Op1jKdoE6tzGyjzFx8gc7ZyW3hVFs1jUCQfM/Z2Jo= github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg= github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/muun/mobile v0.0.0-20220913162405-8cc629edd37b h1:vAbL/T5aP3yNt1TAhukQvsnxLR4pAf1+EmVWecDMr+Q= -github.com/muun/mobile v0.0.0-20220913162405-8cc629edd37b/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= +github.com/muun/mobile v0.0.0-20240709203120-049ae58602a0 h1:1Wc7cbXYLR73MAFL8ztWyYiVAKj6JspMNEjrbFE2Y1w= +github.com/muun/mobile v0.0.0-20240709203120-049ae58602a0/go.mod h1:TCsc78+c4cqb8IKEosz2LwJ6YRNkIjMuAYeHYjchGDE= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= @@ -288,32 +219,23 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -325,17 +247,12 @@ github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 h1:tcJ6OjwOMvExLlzrAVZute09ocAGa7KqOON60++Gz4E= github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY= -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= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 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= -go.mongodb.org/mongo-driver v1.0.3 h1:GKoji1ld3tw2aC+GX1wbr/J2fX13yNacEYoJ8Nhr0yU= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -343,36 +260,35 @@ golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= 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= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190823064033-3a9bac650e44/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk= golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= +golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd h1:ePuNC7PZ6O5BzgPn9bZayERXBdfZjUYoXEf5BTfDfh8= -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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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= @@ -388,27 +304,28 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= 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/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= 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= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -420,68 +337,71 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -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/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 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/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 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= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -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/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -489,28 +409,21 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v1 v1.0.1 h1:oQFRXzZ7CkBGdm1XZm/EbQYaYNNEElNBOd09M6cqNso= gopkg.in/errgo.v1 v1.0.1/go.mod h1:3NjfXwocQRYAPTq4/fzX+CwUhPRcR/azYRhj8G+LqMo= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gormigrate.v1 v1.6.0 h1:XpYM6RHQPmzwY7Uyu+t+xxMXc86JYFJn4nEc9HzQjsI= gopkg.in/gormigrate.v1 v1.6.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw= -gopkg.in/macaroon-bakery.v2 v2.0.1 h1:0N1TlEdfLP4HXNCg7MQUMp5XwvOoxk+oe9Owr2cpvsc= gopkg.in/macaroon-bakery.v2 v2.0.1/go.mod h1:B4/T17l+ZWGwxFSZQmlBwp25x+og7OkhETfr3S9MbIA= -gopkg.in/macaroon.v2 v2.0.0 h1:LVWycAfeJBUjCIqfR9gqlo7I8vmiXRr51YEOZ1suop8= gopkg.in/macaroon.v2 v2.0.0/go.mod h1:+I6LnTMkm/uV5ew/0nsulNjL16SK4+C8yDmRUzHR17I= -gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -521,5 +434,4 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/libwallet/newop/state.go b/libwallet/newop/state.go index 5e072d0f..6241c803 100644 --- a/libwallet/newop/state.go +++ b/libwallet/newop/state.go @@ -918,18 +918,11 @@ type ConfirmState struct { func (s *ConfirmState) OpenFeeEditor() error { - maxFeeRate := newPaymentAnalyzer(s.PaymentContext).MaxFeeRateToAddress(&operation.PaymentToAddress{ - TakeFeeFromAmount: s.TakeFeeFromAmount, - AmountInSat: s.Amount.InSat, - FeeRateInSatsPerVByte: s.FeeRateInSatsPerVByte, - }) - next := &EditFeeState{ - Resolved: s.Resolved, - AmountInfo: s.AmountInfo, - Validated: s.Validated, - Note: s.Note, - MaxFeeRateInSatsPerVByte: maxFeeRate, + Resolved: s.Resolved, + AmountInfo: s.AmountInfo, + Validated: s.Validated, + Note: s.Note, } next.emit() diff --git a/libwallet/operation/payment_analyzer.go b/libwallet/operation/payment_analyzer.go index 142dcef5..7d18cd7b 100644 --- a/libwallet/operation/payment_analyzer.go +++ b/libwallet/operation/payment_analyzer.go @@ -580,30 +580,3 @@ func (a *PaymentAnalyzer) computeFeeForTFFASwap(payment *PaymentToInvoice, feeRa return a.feeCalculator.Fee(onChainAmount, feeRate, true) } - -// MaxFeeRateToAddress computes the maximum fee rate that can be used when -// paying a given amount. This does not imply that the payment _can be made_. -// When given invalid parameters, it's likely to still obtain a value here and -// the resulting analysis would be Unpayable. It's up to the caller to first -// verify the amount is payable, and only then call this method. -func (a *PaymentAnalyzer) MaxFeeRateToAddress(payment *PaymentToAddress) float64 { - - if payment.AmountInSat > a.totalBalance() { - return 0 - } - - var restInSat int64 - if payment.TakeFeeFromAmount { - restInSat = payment.AmountInSat - } else { - restInSat = a.totalBalance() - payment.AmountInSat - } - - for _, sizeForAmount := range a.nextTransactionSize.SizeProgression { - if sizeForAmount.AmountInSat >= payment.AmountInSat { - return float64(restInSat) / float64(sizeForAmount.SizeInVByte) - } - } - - return 0 -} diff --git a/libwallet/operation/payment_analyzer_test.go b/libwallet/operation/payment_analyzer_test.go index 419d4296..34d5d163 100644 --- a/libwallet/operation/payment_analyzer_test.go +++ b/libwallet/operation/payment_analyzer_test.go @@ -2239,170 +2239,3 @@ func TestAnalyzeOffChain(t *testing.T) { }) } } - -func TestMaxFeeRate(t *testing.T) { - testCases := []struct { - desc string - nts *NextTransactionSize - payment *PaymentToAddress - expected float64 - }{ - { - desc: "small amount with one coin", - payment: &PaymentToAddress{ - TakeFeeFromAmount: false, - AmountInSat: 10_000, - }, - expected: 9_900, - }, - { - desc: "take fee from amount one coin", - payment: &PaymentToAddress{ - AmountInSat: 1_000_000, - TakeFeeFromAmount: true, - }, - expected: 10_000, - }, - { - desc: "zero amount", - payment: &PaymentToAddress{ - TakeFeeFromAmount: false, - AmountInSat: 0, - }, - expected: 10_000, - }, - { - desc: "zero amount using TFFA", - payment: &PaymentToAddress{ - TakeFeeFromAmount: true, - AmountInSat: 0, - }, - expected: 0, - }, - { - desc: "amount greater than balance", - payment: &PaymentToAddress{ - TakeFeeFromAmount: false, - AmountInSat: 1_000_000_000, - }, - expected: 0, - }, - { - desc: "small amount with one coin and debt > 0", - nts: &NextTransactionSize{ - SizeProgression: defaultNTS.SizeProgression, - ExpectedDebtInSat: 10_000, - }, - payment: &PaymentToAddress{ - TakeFeeFromAmount: false, - AmountInSat: 10_000, - }, - expected: 9_800, - }, - { - desc: "take fee from amount success with debt > 0", - nts: &NextTransactionSize{ - SizeProgression: defaultNTS.SizeProgression, - ExpectedDebtInSat: 10_000, - }, - payment: &PaymentToAddress{ - AmountInSat: 990_000, - TakeFeeFromAmount: true, - }, - expected: 9_900, - }, - { - desc: "amount greater than balance because debt > 0", - nts: &NextTransactionSize{ - SizeProgression: defaultNTS.SizeProgression, - ExpectedDebtInSat: 10_000, - }, - payment: &PaymentToAddress{ - TakeFeeFromAmount: false, - AmountInSat: 999_900, - }, - expected: 0, - }, - { - desc: "needs 2 coins to spend", - nts: &NextTransactionSize{ - SizeProgression: []SizeForAmount{ - { - AmountInSat: 10_000, - SizeInVByte: 240, - }, - { - AmountInSat: 20_000, - SizeInVByte: 450, - }, - }, - ExpectedDebtInSat: 0, - }, - payment: &PaymentToAddress{ - TakeFeeFromAmount: false, - AmountInSat: 11_000, - }, - expected: 20, - }, - { - desc: "needs 2 coins to spend with debt", - nts: &NextTransactionSize{ - SizeProgression: []SizeForAmount{ - { - AmountInSat: 10_000, - SizeInVByte: 240, - }, - { - AmountInSat: 20_000, - SizeInVByte: 400, - }, - }, - ExpectedDebtInSat: 8_000, - }, - payment: &PaymentToAddress{ - TakeFeeFromAmount: false, - AmountInSat: 11_000, - FeeRateInSatsPerVByte: 0, - }, - expected: 2.5, - }, - { - desc: "TFFA needs 2 coins to spend with debt", - nts: &NextTransactionSize{ - SizeProgression: []SizeForAmount{ - { - AmountInSat: 10_000, - SizeInVByte: 240, - }, - { - AmountInSat: 20_000, - SizeInVByte: 400, - }, - }, - ExpectedDebtInSat: 8_000, - }, - payment: &PaymentToAddress{ - TakeFeeFromAmount: true, - AmountInSat: 12_000, - }, - expected: 30, - }, - } - - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { - - var analyzer *PaymentAnalyzer - if tC.nts != nil { - analyzer = NewPaymentAnalyzer(defaultFeeWindow, tC.nts) - } else { - analyzer = NewPaymentAnalyzer(defaultFeeWindow, defaultNTS) - } - - maxFeeRate := analyzer.MaxFeeRateToAddress(tC.payment) - if maxFeeRate != tC.expected { - t.Fatalf("Max fee rate %v != %v", maxFeeRate, tC.expected) - } - }) - } -} diff --git a/tools/bootstrap-gomobile.sh b/tools/bootstrap-gomobile.sh index c5f8dd16..afef6846 100755 --- a/tools/bootstrap-gomobile.sh +++ b/tools/bootstrap-gomobile.sh @@ -11,4 +11,4 @@ mkdir -p "$build_dir/pkg" GOMODCACHE="$build_dir/pkg" \ go install golang.org/x/mobile/cmd/gomobile && \ - go install golang.org/x/mobile/cmd/gobind \ No newline at end of file + go install golang.org/x/mobile/cmd/gobind diff --git a/tools/libwallet-android.sh b/tools/libwallet-android.sh index f348a99c..e835ae72 100755 --- a/tools/libwallet-android.sh +++ b/tools/libwallet-android.sh @@ -5,16 +5,18 @@ set -e repo_root=$(git rev-parse --show-toplevel) build_dir="$repo_root/libwallet/.build" +# Install and setup gomobile on demand (no-op if already installed and up-to-date) +. "$repo_root/tools/bootstrap-gomobile.sh" + # OSS project has a different folder libwallet aar, so we receive it as param libwallet="$1" if [[ ! -s "$1" ]]; then libwallet="$repo_root/android/libwallet/libs/libwallet.aar" fi - cd "$repo_root/libwallet" -mkdir -p "$(dirname $libwallet)" +mkdir -p "$(dirname "$libwallet")" # Create the cache folders mkdir -p "$build_dir/android" @@ -35,10 +37,17 @@ if [[ -z $GOCACHE ]]; then GOCACHE="$build_dir/android" fi +# gomobile bind generates the src-android-* directories several times, leading to fail with: +# /tmp/go-build3034672677/b001/exe/gomobile: mkdir $GOCACHE/src-android-arm64: file exists +# exit status 1 +# There is no significant change in build times without these folders. +rm -rf "$GOCACHE"/src-android-* 2>/dev/null \ + || echo "No src-android-* directories found in GOCACHE." + GOMODCACHE="$GOMODCACHE" \ go run golang.org/x/mobile/cmd/gomobile bind \ -target="android" -o "$libwallet" \ - -cache "$GOCACHE"\ + -androidapi 19 \ -trimpath -ldflags="-buildid=. -v" \ . ./newop